File: SVSlicesImpl.mesa
Last edited by: Eric Bier on August 19, 1984 6:22:58 pm PDT
Copyright © 1984 by Xerox Corporation. All rights reserved.
Contents: A scanline data structure representing a ray-traced slice thru a 3D scene. Can be shown in 3D by drawing the endpoints in perspective or in 2D by rotating the slicing plane into the viewing plane.
DIRECTORY
CastRays,
CoordSys,
CSG,
CSGGraphics,
Graphics,
Imager,
Matrix3d,
PadGraphics,
Preprocess3d,
SV2d,
SV3d,
SVError,
SVModelTypes,
SVRayTypes,
SVSceneTypes,
SVSlices;
SVSlicesImpl:
CEDAR
PROGRAM
IMPORTS CastRays, CoordSys, CSG, CSGGraphics, Matrix3d, PadGraphics, Preprocess3d, SVError
EXPORTS SVSlices =
BEGIN
Camera: TYPE = SVModelTypes.Camera;
Classification: TYPE = SVRayTypes.Classification;
CSGTree: TYPE = SVRayTypes.CSGTree;
CoordSystem: TYPE = SVModelTypes.CoordSystem;
FrameBlock: TYPE = SVSceneTypes.FrameBlock;
Matrix4by4: TYPE = SV3d.Matrix4by4;
Plane: TYPE = SV3d.Plane;
Point2d: TYPE = SV2d.Point2d;
Point3d: TYPE = SV3d.Point3d;
Ray: TYPE = SVRayTypes.Ray;
Vector: TYPE = SV3d.Vector;
SliceScanLine: TYPE = REF SliceScanLineObj;
SliceScanLineObj:
TYPE = SVSlices.SliceScanLineObj;
Slice: TYPE = REF SliceObj;
SliceObj: TYPE = SVSlices.SliceObj;
CreateSlice:
PUBLIC
PROC [maxScans:
NAT, coordSys: CoordSystem, frame: FrameBlock, plane: NAT]
RETURNS [slice: Slice] = {
slice ← NEW[SliceObj[maxScans]];
slice.coordSys ← coordSys;
slice.frame ← frame;
slice.plane ← plane;
FOR i:
NAT
IN [0..maxScans)
DO
slice[i] ← NEW[SliceScanLineObj];
ENDLOOP;
};
UpdateSlice:
PUBLIC PROC [slice: Slice, coordSys: CoordSystem, frame: FrameBlock, plane: NAT] = {
slice.coordSys ← coordSys;
slice.frame ← frame;
slice.plane ← plane;
};
ChangeSlicePlane:
PUBLIC PROC [slice: Slice, plane:
NAT] = {
slice.plane ← plane;
};
AddScanLineToSlice:
PRIVATE
PROC [scanLine:
NAT, class: Classification, localBasePt: Point3d, localDirection: Vector, slice: Slice]
RETURNS [modified: Slice] = {
FOR i:
NAT
IN [1..class.count]
DO
t: REAL;
t ← class.params[i];
slice[scanLine].hits[i][1] ← localBasePt[1] + t*localDirection[1];
slice[scanLine].hits[i][2] ← localBasePt[2] + t*localDirection[2];
slice[scanLine].hits[i][3] ← localBasePt[3] + t*localDirection[3];
slice[scanLine].classifs[i] ← class.classifs[i];
ENDLOOP;
slice[scanLine].count ← class.count;
slice[scanLine].classifs[class.count+1] ← class.classifs[class.count+1];
RETURN[slice];
};
RayTraceTheSlice:
PUBLIC
PROC [slice: Slice, tree: CSGTree, camera: Camera] =
TRUSTED {
y, z: REAL;
inc: REAL;
block: FrameBlock ← slice.frame;
basePt, worldBasePt: Point3d;
direction, worldDirection: Vector;
ray: Ray;
class: Classification;
localWRTCamera, localWRTWorld: Matrix4by4;
[] ← Preprocess3d.PreprocessForSlice[tree, camera];
localWRTCamera ← CoordSys.FindInTermsOfCamera[slice.coordSys, camera.coordSys];
localWRTWorld ← CoordSys.FindInTermsOfWorld[slice.coordSys];
ray ← CSG.GetRayFromPool[];
SELECT slice.plane FROM
1 => { -- x = 0 plane. Start rays from hi Z.
y ← block.loY;
inc ← (block.hiY - block.loY) / (slice.maxScans - 1.0);
direction ← [0.0, 0.0, block.loZ - block.hiZ];
worldDirection ← Matrix3d.UpdateVectorEvenScaling[localWRTWorld, direction];
FOR i:
NAT
IN [0..slice.maxScans)
DO
basePt ← [0.0, y, block.hiZ];
worldBasePt ← Matrix3d.Update[localWRTWorld, basePt];
CSG.StuffWorldRay[ray, worldBasePt, worldDirection, camera];
class ← CastRays.RayCastBoundingSpheres[ray, tree.son];
slice ← AddScanLineToSlice[i, class, basePt, direction, slice];
CastRays.ReturnClassToPool[class];
slice[i].lineVal ← y;
y ← y + inc;
ENDLOOP;
};
2 => { -- y = 0 plane. Start rays from lo X.
z ← block.loZ;
inc ← (block.hiZ - block.loZ) / (slice.maxScans - 1.0);
direction ← [block.hiX - block.loX, 0.0, 0.0];
worldDirection ← Matrix3d.UpdateVectorEvenScaling[localWRTWorld, direction];
FOR i:
NAT
IN [0..slice.maxScans)
DO
basePt ← [block.loX, 0.0, z];
worldBasePt ← Matrix3d.Update[localWRTWorld, basePt];
CSG.StuffWorldRay[ray, worldBasePt, worldDirection, camera];
class ← CastRays.RayCastBoundingSpheres[ray, tree.son];
slice ← AddScanLineToSlice[i, class, basePt, direction, slice];
CastRays.ReturnClassToPool[class];
slice[i].lineVal ← z;
z ← z + inc;
ENDLOOP;
};
3 => { -- z = 0 plane. Start rays from lo X.
y ← block.loY;
inc ← (block.hiY - block.loY) / (slice.maxScans - 1.0);
direction ← [block.hiX - block.loX, 0.0, 0.0];
worldDirection ← Matrix3d.UpdateVectorEvenScaling[localWRTWorld, direction];
FOR i:
NAT
IN [0..slice.maxScans)
DO
basePt ← [block.loX, y, 0.0];
worldBasePt ← Matrix3d.Update[localWRTWorld, basePt];
CSG.StuffWorldRay[ray, worldBasePt, worldDirection, camera];
class ← CastRays.RayCastBoundingSpheres[ray, tree.son];
slice ← AddScanLineToSlice[i, class, basePt, direction, slice];
CastRays.ReturnClassToPool[class];
slice[i].lineVal ← y;
y ← y + inc;
ENDLOOP;
};
ENDCASE => ERROR SVError.NotYetImplemented;
CSG.ReturnRayToPool[ray];
ClipSliceAgainstFrame[slice];
WidthAndHeight:
PUBLIC
PROC [slice: Slice]
RETURNS [width, height:
REAL] = {
f: FrameBlock ← slice.frame;
SELECT slice.plane FROM
1 => {
-- x = 0 plane. y top to bottom. z left to right.
width ← f.hiZ - f.loZ;
height ← f.hiY - f.loY;
};
2 => {
-- y = 0 plane. z top to bottom. x left to right.
width ← f.hiX - f.loX;
height ← f.hiZ - f.loZ;
};
3, 4 => {
-- z = 0 plane. y top to bottom. x left to right.
width ← f.hiX - f.loX;
height ← f.hiY - f.loY;
};
ENDCASE => ERROR;
ShiftScanLineUp:
PRIVATE
PROC [slice: Slice, i:
NAT] = {
FOR j:
NAT ← slice[i].count, j-1
UNTIL j < 1
DO
slice[i].hits[j+1] ← slice[i].hits[j];
slice[i].classifs[j+1] ← slice[i].classifs[j];
ENDLOOP;
ClipSliceAgainstFrame:
PRIVATE
PROC [slice: Slice] = {
Clip the scanlines represented by a slice against its own frame so they can be drawn in a straightforward manner.
startPt, endPt: Point3d;
block: FrameBlock ← slice.frame;
beenIn: BOOL;
SELECT slice.plane FROM
1 => {
FOR i:
NAT
IN [0..slice.maxScans)
DO
For each scan line...
beenIn ← FALSE;
startPt ← [0, slice[i].lineVal, block.hiZ];
An obscure way of saying "The point at the current y value, on the left border of the slice".
IF slice[i].classifs[1] =
TRUE
THEN { -- ray starts inside
beenIn ← TRUE;
ShiftScanLineUp[slice, i];
slice[i].hits[1] ← startPt;
slice[i].classifs[1] ← FALSE;
slice[i].count ← slice[i].count + 1;
};
FOR j:
NAT
IN [1..slice[i].count]
DO
endPt ← slice[i].hits[j];
IF endPt[3] > block.loZ AND endPt[3] < block.hiZ THEN beenIn ← TRUE;
IF endPt[3] < block.loZ THEN GOTO EndOfScanline;
REPEAT
EndOfScanline => { -- use this point instead of the out of bounds one
IF beenIn THEN {
slice[i].hits[j] ← [0, slice[i].lineVal, block.loZ];
slice[i].count ← j;
}
ELSE slice[i].count ← 0;
};
FINISHED => {
IF beenIn THEN {
IF slice[i].classifs[slice[i].count + 1] =
TRUE
THEN {
slice[i].count ← slice[i].count + 1;
slice[i].hits[slice[i].count] ← [0, slice[i].lineVal, block.loZ];
};
}
ELSE slice[i].count ← 0;
};
ENDLOOP;
ENDLOOP;
};
2 => {
FOR i:
NAT
IN [0..slice.maxScans)
DO
For each scan line...
beenIn ← FALSE;
startPt ← [block.loX, 0, slice[i].lineVal];
An obscure way of saying "The point at the current z value, on the left border of the slice".
IF slice[i].classifs[1] =
TRUE
THEN {
beenIn ← TRUE;
ShiftScanLineUp[slice, i];
slice[i].hits[1] ← startPt;
slice[i].classifs[1] ← FALSE;
slice[i].count ← slice[i].count + 1;
};
FOR j:
NAT
IN [1..slice[i].count]
DO
endPt ← slice[i].hits[j];
IF endPt[1] < block.hiX AND endPt[1] > block.loX THEN beenIn ← TRUE;
IF endPt[1] > block.hiX THEN GOTO EndOfScanline;
REPEAT
EndOfScanline => {
IF beenIn THEN {
slice[i].hits[j] ← [block.hiX, 0, slice[i].lineVal];
slice[i].count ← j;
}
ELSE slice[i].count ← 0;
};
FINISHED => {
IF beenIn THEN {
IF slice[i].classifs[slice[i].count + 1] =
TRUE
THEN {
slice[i].count ← slice[i].count + 1;
slice[i].hits[slice[i].count] ← [block.hiX, 0, slice[i].lineVal];
};
}
ELSE slice[i].count ← 0;
};
ENDLOOP;
ENDLOOP;
};
3 => {
FOR i:
NAT
IN [0..slice.maxScans)
DO
For each scan line...
beenIn ← FALSE;
startPt ← [block.loX, slice[i].lineVal, 0];
An obscure way of saying "The point at the current y value, on the left border of the slice".
IF slice[i].classifs[1] =
TRUE
THEN {
beenIn ← TRUE;
ShiftScanLineUp[slice, i];
slice[i].hits[1] ← startPt;
slice[i].classifs[1] ← FALSE;
slice[i].count ← slice[i].count + 1;
};
FOR j:
NAT
IN [1..slice[i].count]
DO
endPt ← slice[i].hits[j];
IF endPt[1] < block.hiX AND endPt[1] > block.loX THEN beenIn ← TRUE;
IF endPt[1] > block.hiX THEN GOTO EndOfScanline;
REPEAT
EndOfScanline => {
IF beenIn THEN {
slice[i].hits[j] ← [block.hiX, slice[i].lineVal, 0];
slice[i].count ← j;
}
ELSE slice[i].count ← 0;
};
FINISHED => {
IF beenIn THEN {
IF slice[i].classifs[slice[i].count + 1] =
TRUE
THEN {
slice[i].count ← slice[i].count + 1;
slice[i].hits[slice[i].count] ← [block.hiX, slice[i].lineVal, 0];
};
}
ELSE slice[i].count ← 0;
};
ENDLOOP;
ENDLOOP;
};
ENDCASE => ERROR;
};
DrawSlice:
PUBLIC
PROC [dc: Imager.Context, slice: Slice, origin: Point2d] =
TRUSTED {
Draw the slice in a 2d window such as the scratchpad.
Think of the slice as a sequence of black stripes of width 1 screen scanline.
startPtLocal, endPtLocal: Point3d;
startPad, endPad: Point2d;
SELECT slice.plane FROM
1 => {
-- x=0 plane
FOR i:
NAT
IN [0..slice.maxScans)
DO
For each scan line...
FOR j:
NAT
IN [2..slice[i].count]
DO
For each potential endPtLocal...
IF slice[i].classifs[j]
THEN {
startPtLocal ← slice[i].hits[j-1];
endPtLocal ← slice[i].hits[j];
startPad ← [-startPtLocal[3], startPtLocal[2]];
endPad ← [-endPtLocal[3], endPtLocal[2]];
PadGraphics.MoveTo[dc, startPad, origin];
PadGraphics.DrawTo[dc, endPad, origin];
};
ENDLOOP;
ENDLOOP;
};
2 => {
-- y = 0 plane
FOR i:
NAT
IN [0..slice.maxScans)
DO
For each scan line...
FOR j:
NAT
IN [2..slice[i].count]
DO
For each potential endPtLocal...
IF slice[i].classifs[j]
THEN {
startPtLocal ← slice[i].hits[j-1];
endPtLocal ← slice[i].hits[j];
startPad ← [startPtLocal[1], -startPtLocal[3]];
endPad ← [endPtLocal[1], -endPtLocal[3]];
PadGraphics.MoveTo[dc, startPad, origin];
PadGraphics.DrawTo[dc, endPad, origin];
};
ENDLOOP;
ENDLOOP;
};
3, 4 => {
FOR i:
NAT
IN [0..slice.maxScans)
DO
For each scan line...
FOR j:
NAT
IN [2..slice[i].count]
DO
For each potential endPtLocal...
IF slice[i].classifs[j]
THEN {
startPtLocal ← slice[i].hits[j-1];
endPtLocal ← slice[i].hits[j];
startPad ← [startPtLocal[1], startPtLocal[2]];
endPad ← [endPtLocal[1], endPtLocal[2]];
PadGraphics.MoveTo[dc, startPad, origin];
PadGraphics.DrawTo[dc, endPad, origin];
};
ENDLOOP;
ENDLOOP;
};
ENDCASE => ERROR;
};
DrawSliceAlternate: PRIVATE PROC [dc: Imager.Context, slice: Slice, origin: Point2d] = TRUSTED {
Draw the slice in a 2d window such as the scratchpad.
Think of the slice as a sequence of black stripes of width 1 screen scanline.
startPtLocal, endPtLocal: Point3d;
startPad, endPad: Point2d;
FOR i: NAT IN [0..slice.maxScans) DO
FOR j: NAT ← 1, j+2 UNTIL j +1> slice[i].count DO
startPtLocal ← slice[i].hits[j];
endPtLocal ← slice[i].hits[j+1];
startPad ← [startPtLocal[1], startPtLocal[2]];
endPad ← [endPtLocal[1], endPtLocal[2]];
PadGraphics.MoveTo[dc, startPad, origin];
PadGraphics.DrawTo[dc, endPad, origin];
ENDLOOP;
ENDLOOP;
DrawSlice3d:
PUBLIC
PROC [dc: Graphics.Context, slice: Slice, camera: Camera] =
TRUSTED {
Draw the outline of the slice in with a set of solid objects. Like DrawSlice except that points we use CSGGraphics type procdures to project the lines onto a given viewer's screen.
startPtLocal, endPtLocal: Point3d;
FOR i:
NAT
IN [0..slice.maxScans)
DO
FOR j:
NAT
IN [2..slice[i].count]
DO
IF slice[i].classifs[j]
THEN {
startPtLocal ← slice[i].hits[j-1];
endPtLocal ← slice[i].hits[j];
CSGGraphics.DrawLine[dc, startPtLocal, endPtLocal, camera, slice.coordSys];
};
ENDLOOP;
ENDLOOP;
END.