ThreeDSurfacesImpl.mesa
Copyright © 1984, 1986 by Xerox Corporation. All rights reserved.
Last Edited by: Crow, May 29, 1986 5:42:31 pm PDT
DIRECTORY
Atom     USING [GetPropFromList, PutPropOnList],
Real     USING [RoundC, Fix, FixI, FixC],
RealFns    USING [SqRt],
Basics     USING [BITOR, BITAND],
Rope     USING [ROPE],
IO      USING [STREAM, GetInt],
Imager    USING [Context, MaskVectorI],
ImagerColor   USING [RGB],
Pixels     USING [GetSampleSet, SampleSet],
ScanConvert   USING [PutLine, justNoticeable],
Vector3d    USING [Dot, Triple, Quad, Normalize, Cross, Add],
Plane3d    USING [DistanceToPt],
ThreeDMisc   USING [GetMappedColor, GetImagerContext, GetBackground,
        SetNamedColor],
ThreeDScenes  USING [AllOut, ClipState, Context, Error,
        GetShading, GetVtxShades, NoneOut, OutCode, PutShading,
        ReadColors, ReadVertexCoords, SetUpStandardFile, ShadePt,
        ShadingSequence, ShadingValue, ShapeInstance, ShapeSequence,
        SixSides,
        Vertex, VertexInfo, VertexInfoSequence, VertexSequence,
        XfmPtToDisplay, XfmToDisplay, XfmToEyeSpace],
Tilers     USING [PolygonTiler],
ThreeDSurfaces  USING [Patch, PatchProcs, PtrPatchSequence, PtrPatch, PatchSequence,
        SortSequence, ShapePatch];
ThreeDSurfacesImpl: CEDAR PROGRAM
IMPORTS Atom, Real, RealFns, IO, Basics, ThreeDScenes, Tilers, Vector3d, Plane3d, ScanConvert, ThreeDMisc, Pixels, Imager
EXPORTS ThreeDSurfaces
= BEGIN
Internal Declarations
Context: TYPE ~ ThreeDScenes.Context;
RGB: TYPE ~ ImagerColor.RGB;          --  [ r, g, b: REAL];
SixSides: TYPE ~ ThreeDScenes.SixSides;
Triple: TYPE ~ Vector3d.Triple;
SampleSet: TYPE ~ Pixels.SampleSet;
ShapeInstance: TYPE ~ ThreeDScenes.ShapeInstance;
Patch: TYPE ~ ThreeDSurfaces.Patch;
PatchSequence: TYPE ~ ThreeDSurfaces.PatchSequence;
PtrPatch: TYPE ~ ThreeDSurfaces.PtrPatch;
PtrPatchSequence: TYPE ~ ThreeDSurfaces.PtrPatchSequence;
ShapePatch: TYPE ~ ThreeDSurfaces.ShapePatch;
Vertex: TYPE ~ ThreeDScenes.Vertex;
VertexSequence: TYPE ~ ThreeDScenes.VertexSequence;
ShadingValue: TYPE ~ ThreeDScenes.ShadingValue;
ShadingSequence: TYPE ~ ThreeDScenes.ShadingSequence;
tempPatch, tempPatch2: REF Patch ← NIL;  -- temps for clipping, etc.
pixelBytes: SampleSet ← Pixels.GetSampleSet[1];     -- cache for pixel size
stopMe: BOOLEANFALSE;
Utility Procedures
Sqr: PROCEDURE [number: REAL] RETURNS [REAL] ~ INLINE { RETURN[number * number]; };
Sgn: PROCEDURE [number: REAL] RETURNS [REAL] ~ INLINE {
IF number < 0. THEN RETURN[-1.] ELSE RETURN[1.];
};
DiffPosns: PROC[vtx1, vtx2: REF Vertex] RETURNS[Triple] ~ {
RETURN[[vtx1.x - vtx2.x, vtx1.y - vtx2.y, vtx1.z - vtx2.z]] };
HoldEverything: PROCEDURE [] ~ {
stopMe ← FALSE;
ERROR ABORTED;
};
EnableDisplay: PUBLIC PROC [] ~ { stopMe ← FALSE; };
StopDisplay: PUBLIC PROC [] ~ { stopMe ← TRUE; };
ShapetoColorBytes: PROC [context: REF Context, s: REF ShapeInstance]
       RETURNS[SampleSet] ~ {
ref: REF ANY ← ThreeDScenes.GetShading[ s, $Color];
r, g, b: REAL;
ir, ig, ib: INTEGER;
addOn: NAT ← 0;
IF context.alphaBuffer THEN addOn ← addOn + 1;
IF context.depthBuffer THEN addOn ← addOn + 1;
IF ref # NIL
THEN {
[r, g, b] ← NARROW[ ref, REF RGB]^;        -- shape color
ir ← Real.RoundC[r*255.0]; ig ← Real.RoundC[g*255.0]; ib ← Real.RoundC[b*255.0];
}
ELSE ir ← ig ← ib ← 255;
SELECT context.renderMode FROM        -- convert color to byte sequence
$LF => {
clr: RGB ← ThreeDMisc.GetBackground[context];
IF (255.0 * (clr.R + clr.G + clr.B) / 3) > 128
THEN pixelBytes[0] ← 255 ELSE pixelBytes[0] ← 0; -- ensure contrast with backgrnd
};
$Dithered, $PseudoClr => {
IF pixelBytes.length < 1+addOn THEN pixelBytes ← Pixels.GetSampleSet[1+addOn];
pixelBytes[0] ← ThreeDMisc.GetMappedColor[context, [ir, ig, ib] ];
IF addOn > 0 THEN pixelBytes[1] ← 0; IF addOn > 1 THEN pixelBytes[2] ← 0;
};
$Grey   => {
IF pixelBytes.length < 1+addOn THEN pixelBytes ← Pixels.GetSampleSet[1+addOn];
pixelBytes[0] ← (ir + ig + ib)/3;
IF addOn > 0 THEN pixelBytes[1] ← 0; IF addOn > 1 THEN pixelBytes[2] ← 0;
};
$FullClr, $Dorado24  => {
IF pixelBytes.length < 3+addOn THEN pixelBytes ← Pixels.GetSampleSet[3+addOn];
pixelBytes[0] ← ir;
pixelBytes[1] ← ig;
pixelBytes[2] ← ib;
IF addOn > 0 THEN pixelBytes[3] ← 0; IF addOn > 1 THEN pixelBytes[4] ← 0;
};
ENDCASE  => SIGNAL ThreeDScenes.Error[[$Unimplemented, "Unknown renderMode"]];
RETURN[pixelBytes];
};
CombineBoxes: PROC[context: REF Context] ~ { -- get combined bounding box
context.extentCovered ← [LAST[NAT], 0, LAST[NAT], 0];
FOR i: NAT IN [0..context.shapes.length) DO
IF context.shapes[i].clipState # out THEN {
IF context.extentCovered.left > context.shapes[i].screenExtent.left
THEN context.extentCovered.left ← context.shapes[i].screenExtent.left;
IF context.extentCovered.right < context.shapes[i].screenExtent.right
THEN context.extentCovered.right ← context.shapes[i].screenExtent.right;
IF context.extentCovered.bottom > context.shapes[i].screenExtent.bottom
THEN context.extentCovered.bottom ← context.shapes[i].screenExtent.bottom;
IF context.extentCovered.top < context.shapes[i].screenExtent.top
THEN context.extentCovered.top ← context.shapes[i].screenExtent.top;
};
ENDLOOP;
};
ShapePatchToPatch: PUBLIC PROC[context: REF Context,
           sPatch: REF ThreeDSurfaces.ShapePatch]
       RETURNS
[patch: REF Patch] ~ {
ptrPatch: REF PtrPatch ←
  NARROW
[sPatch.shape.surface, REF PtrPatchSequence][sPatch.patch];
vertex: REF ThreeDScenes.VertexSequence ← sPatch.shape.vertex;
shade: REF ThreeDScenes.ShadingSequence ← sPatch.shape.shade;
faceted: BOOLEAN
   NARROW[ ThreeDScenes.GetShading[ sPatch.shape, $Type ], ATOM ] = $Faceted;
lines: BOOLEAN
   NARROW[ ThreeDScenes.GetShading[ sPatch.shape, $Type ], ATOM ] = $Lines;
patchInfo: REF ThreeDScenes.ShadingSequence ← NARROW[
ThreeDScenes.GetShading[ sPatch.shape, $PatchColors ]
];
coloredPatches: BOOLEANIF patchInfo # NIL AND NOT faceted
THEN ThreeDScenes.GetShading[ sPatch.shape, $PatchColorFile ] # NIL
ELSE FALSE;
IF ptrPatch.clipState = out THEN RETURN[NIL];      -- reject if outside frame
patch ← tempPatch;           -- get global temp storage for patch
IF (patch = NIL) OR (patch.length < 2 * ptrPatch.nVtces)
THEN patch ← tempPatch ← NEW[Patch[2 * ptrPatch.nVtces] ];
patch.nVtces ← ptrPatch.nVtces;
patch.clipState ← ptrPatch.clipState;
patch.type ← ptrPatch.type;
patch.oneSided ← ptrPatch.oneSided;
patch.props ← sPatch.shape.shadingProps;     -- transmit shading info
FOR i: NAT IN [0..ptrPatch.nVtces) DO
j: NAT ← ptrPatch.vtxPtr[i];
patch[i].coord ← vertex[j]^;
patch[i].shade ← shade[j]^;
IF lines THEN {             -- lines use unshaded values
patch[i].shade.ir ← Real.FixC[patch[i].shade.r * 255.0];
patch[i].shade.ig ← Real.FixC[patch[i].shade.g * 255.0];
patch[i].shade.ib ← Real.FixC[patch[i].shade.b * 255.0];
}
ELSE IF patchInfo # NIL THEN        -- shades for individual patches
IF faceted
THEN patch[i].shade ← patchInfo[sPatch.patch]^
ELSE IF coloredPatches THEN {
patch[i].shade.ir ← Real.FixC[ patch[i].shade.ir * patchInfo[sPatch.patch].r ];
patch[i].shade.ig ← Real.FixC[ patch[i].shade.ig * patchInfo[sPatch.patch].g ];
patch[i].shade.ib ← Real.FixC[ patch[i].shade.ib * patchInfo[sPatch.patch].b ];
patch[i].shade.it ← Real.FixC[ patch[i].shade.it * patchInfo[sPatch.patch].t ];
IF context.alphaBuffer THEN {   -- possible pixel by pixel shading
patch[i].shade.r ← shade[j].r * patchInfo[sPatch.patch].r;
patch[i].shade.g ← shade[j].g * patchInfo[sPatch.patch].g;
patch[i].shade.b ← shade[j].b * patchInfo[sPatch.patch].b;
patch[i].shade.t ← shade[j].t * patchInfo[sPatch.patch].t;
};
};
ENDLOOP;
};
Procedures for Reading in Shape Descriptions
LoadShape: PUBLIC PROC[ shape: REF ShapeInstance, fileName: Rope.ROPE,
       type: ATOM ← $ConvexPolygon, insideVisible: BOOLEANFALSE ] ~ {
numVtces: NAT;
min, max: Triple;
radius: REAL;
nullVertex: ThreeDScenes.Vertex;     -- gets initialized vertex record
nullShade: ThreeDScenes.ShadingValue;   -- gets initialized ShadingValue record
input: IO.STREAM;
Get File, Read number of points and number of polygons
[input, numVtces] ← ThreeDScenes.SetUpStandardFile[fileName];
shape.numSurfaces ← IO.GetInt[input];
shape.type ← type;
shape.insideVisible ← insideVisible;
shape.vertex ← NEW[ ThreeDScenes.VertexSequence[numVtces] ];
shape.shade ← NEW[ ThreeDScenes.ShadingSequence[numVtces] ];
FOR i: NAT IN [0..numVtces) DO-- initialize sequences (compiler should do this (grump))
shape.vertex[i] ← NEW[ ThreeDScenes.Vertex ← nullVertex ];
shape.shade[i] ← NEW[ ThreeDScenes.ShadingValue ← nullShade];
ENDLOOP;
ThreeDScenes.PutShading[shape, $Color, NEW[ RGB ← [0.7, 0.7, 0.7] ] ]; -- default grey
ThreeDScenes.ReadVertexCoords[shape.vertex, input, numVtces]; -- read vertices
ReadPatches[shape, input, shape.numSurfaces, shape.type, insideVisible];
Find approximation to bounding sphere
min ← max ← [shape.vertex[0].x, shape.vertex[0].y, shape.vertex[0].z];
FOR i: NAT IN (0..shape.vertex.length) DO -- get min and max in x, y, and z
IF shape.vertex[i] # NIL THEN {
IF shape.vertex[i].x < min.x
THEN min.x ← shape.vertex[i].x
ELSE IF shape.vertex[i].x > max.x THEN max.x ← shape.vertex[i].x;
IF shape.vertex[i].y < min.y
THEN min.y ← shape.vertex[i].y
ELSE IF shape.vertex[i].y > max.y THEN max.y ← shape.vertex[i].y;
IF shape.vertex[i].z < min.z
THEN min.z ← shape.vertex[i].z
ELSE IF shape.vertex[i].x > max.z THEN max.z ← shape.vertex[i].z;
};
ENDLOOP;
shape.centroid.x ← (min.x + max.x) / 2;      -- get middle point in each coordinate
shape.centroid.y ← (min.y + max.y) / 2;
shape.centroid.z ← (min.z + max.z) / 2;
shape.boundingRadius ← 0.;
FOR i: NAT IN [0..numVtces) DO -- find radius
radius ← RealFns.SqRt[
  Sqr[shape.vertex[i].x - shape.centroid.x]
+ Sqr[shape.vertex[i].y - shape.centroid.y]
+ Sqr[shape.vertex[i].z - shape.centroid.z] ];
IF radius > shape.boundingRadius
THEN shape.boundingRadius ← radius;
ENDLOOP;
};
ReadPatches: PUBLIC PROC[shape: REF ShapeInstance, in: IO.STREAM, nPatches: NAT,
        type: ATOM, insideVisible: BOOLEANFALSE] ~ {
surface: REF PtrPatchSequence;
shape.surface ← NEW[ PtrPatchSequence[shape.numSurfaces] ];
surface ← NARROW[shape.surface, REF PtrPatchSequence];
FOR i: NAT IN [0..nPatches) DO -- Read Patchs
nVtces: NATIO.GetInt[in];
surface[i] ← NEW[PtrPatch[nVtces]];
surface[i].nVtces ← nVtces;
surface[i].type ← type;
surface[i].oneSided ← NOT insideVisible;
FOR j: NAT IN [0..nVtces) DO
surface[i].vtxPtr[j] ← IO.GetInt[in] - 1; -- switch to counting from zero
ENDLOOP;
ENDLOOP;
};
GetPatchColors: PUBLIC PROC[shape: REF ShapeInstance, fileName: Rope.ROPE] ~ {
in: IO.STREAM;
nPolys: NAT;
patchInfo: REF ThreeDScenes.ShadingSequence ← NARROW[
ThreeDScenes.GetShading[ shape, $PatchColors ]
];
[in, nPolys] ← ThreeDScenes.SetUpStandardFile[fileName];
IF patchInfo = NIL THEN patchInfo ← NEW[ThreeDScenes.ShadingSequence[nPolys] ];
ThreeDScenes.ReadColors[patchInfo, in, nPolys];
ThreeDScenes.PutShading[ shape, $PatchColors, patchInfo ];
ThreeDScenes.PutShading[ shape, $PatchColorFile, fileName ];
shape.shadingInValid ← TRUE;
};
Procedures for Transformations and Clipping
ClipPoly: PUBLIC PROC[ context: REF Context, poly: REF Patch]
   RETURNS [REF Patch] ~ {
Clip: PROC[side: SixSides, pIn, pOut: REF Patch] RETURNS [REF Patch, REF Patch] = {
Dist: PROC[side: SixSides, vtx: Vertex] RETURNS [REAL] = {  -- + inside, - outside
RETURN[ Plane3d.DistanceToPt[
[vtx.ex, vtx.ey, vtx.ez],
context.clippingPlanes[side]
] ];
};
lastDist, dist: REAL; outCnt, last: NAT;
IF pIn.nVtces < 3 THEN RETURN[ pIn, pOut ]; -- return if degenerate
outCnt ← 0;
IF pIn.type # $Path THEN {
lastDist ← Dist[side, pIn.vtx[pIn.nVtces - 1].coord];
last ← pIn.nVtces - 1;
};
IF pOut.length < 2 * pIn.nVtces THEN pOut ← NEW[Patch[2 * pIn.nVtces]];
FOR i: NAT IN [0..pIn.nVtces) DO
a, b: REAL;
dist ← Dist[side, pIn.vtx[i].coord];
IF (i # 0 OR pIn.type # $Path) AND lastDist * dist < 0. THEN { 
Put out point if clip plane crossed
b ← dist / (dist - lastDist); a ← 1.0 - b;
pOut.vtx[outCnt].coord.x ← pIn.vtx[i].coord.x * a + pIn.vtx[last].coord.x * b;
pOut.vtx[outCnt].coord.y ← pIn.vtx[i].coord.y * a + pIn.vtx[last].coord.y * b;
pOut.vtx[outCnt].coord.z ← pIn.vtx[i].coord.z * a + pIn.vtx[last].coord.z * b;
pOut.vtx[outCnt].coord.ex ← pIn.vtx[i].coord.ex * a + pIn.vtx[last].coord.ex * b;
pOut.vtx[outCnt].coord.ey ← pIn.vtx[i].coord.ey * a + pIn.vtx[last].coord.ey * b;
pOut.vtx[outCnt].coord.ez ← pIn.vtx[i].coord.ez * a + pIn.vtx[last].coord.ez * b;
pOut.vtx[outCnt].shade.r ← pIn.vtx[i].shade.r * a + pIn.vtx[last].shade.r * b;
pOut.vtx[outCnt].shade.g ← pIn.vtx[i].shade.g * a + pIn.vtx[last].shade.g * b;
pOut.vtx[outCnt].shade.b ← pIn.vtx[i].shade.b * a + pIn.vtx[last].shade.b * b;
pOut.vtx[outCnt].shade.t ← pIn.vtx[i].shade.t * a + pIn.vtx[last].shade.t * b;
pOut.vtx[outCnt].shade.ir ← Real.RoundC[
         pIn.vtx[i].shade.ir * a + pIn.vtx[last].shade.ir * b ];
pOut.vtx[outCnt].shade.ig ← Real.RoundC[
         pIn.vtx[i].shade.ig * a + pIn.vtx[last].shade.ig * b ];
pOut.vtx[outCnt].shade.ib ← Real.RoundC[
         pIn.vtx[i].shade.ib * a + pIn.vtx[last].shade.ib * b ];
pOut.vtx[outCnt].shade.it ← Real.RoundC[
         pIn.vtx[i].shade.it * a + pIn.vtx[last].shade.it * b ];
pOut.vtx[outCnt].shade.txtrX ←
       pIn.vtx[i].shade.txtrX * a + pIn.vtx[last].shade.txtrX * b;
pOut.vtx[outCnt].shade.txtrY ←
       pIn.vtx[i].shade.txtrY * a + pIn.vtx[last].shade.txtrY * b;
pOut.vtx[outCnt].shade.exn ← pIn.vtx[i].shade.exn*a + pIn.vtx[last].shade.exn*b;
pOut.vtx[outCnt].shade.eyn ← pIn.vtx[i].shade.eyn*a + pIn.vtx[last].shade.eyn*b;
pOut.vtx[outCnt].shade.ezn ← pIn.vtx[i].shade.ezn*a + pIn.vtx[last].shade.ezn*b;
outCnt ← outCnt + 1;
};
IF dist >= 0. THEN {      -- put out point if inside
pOut.vtx[outCnt].coord.x ← pIn.vtx[i].coord.x;
pOut.vtx[outCnt].coord.y ← pIn.vtx[i].coord.y;
pOut.vtx[outCnt].coord.z ← pIn.vtx[i].coord.z;
pOut.vtx[outCnt].coord.ex ← pIn.vtx[i].coord.ex;
pOut.vtx[outCnt].coord.ey ← pIn.vtx[i].coord.ey;
pOut.vtx[outCnt].coord.ez ← pIn.vtx[i].coord.ez;
pOut.vtx[outCnt].shade.r ← pIn.vtx[i].shade.r;
pOut.vtx[outCnt].shade.g ← pIn.vtx[i].shade.g;
pOut.vtx[outCnt].shade.b ← pIn.vtx[i].shade.b;
pOut.vtx[outCnt].shade.t ← pIn.vtx[i].shade.t;
pOut.vtx[outCnt].shade.ir ← pIn.vtx[i].shade.ir;
pOut.vtx[outCnt].shade.ig ← pIn.vtx[i].shade.ig;
pOut.vtx[outCnt].shade.ib ← pIn.vtx[i].shade.ib;
pOut.vtx[outCnt].shade.it ← pIn.vtx[i].shade.it;
pOut.vtx[outCnt].shade.txtrX ← pIn.vtx[i].shade.txtrX;
pOut.vtx[outCnt].shade.txtrY ← pIn.vtx[i].shade.txtrY;
pOut.vtx[outCnt].shade.exn ← pIn.vtx[i].shade.exn;
pOut.vtx[outCnt].shade.eyn ← pIn.vtx[i].shade.eyn;
pOut.vtx[outCnt].shade.ezn ← pIn.vtx[i].shade.ezn;
outCnt ← outCnt + 1;
};
lastDist ← dist; last ← i;
ENDLOOP;
pOut.type ← pIn.type;
pOut.oneSided ← pIn.oneSided;
pOut.nVtces ← outCnt;
pOut.props ← pIn.props;
RETURN [ pOut, pIn ];
}; -- end Clip Proc
orOfCodes: ThreeDScenes.OutCode ← ThreeDScenes.NoneOut;
poly2: REF Patch ← tempPatch2;       -- get global temp storage for patch
IF poly.type # $ConvexPolygon AND poly.type # $Path
THEN SIGNAL ThreeDScenes.Error[[$Unimplemented, "Not clippable"]];
FOR i: NAT IN [0..poly.nVtces) DO
orOfCodes ← LOOPHOLE[
Basics.BITOR[LOOPHOLE[orOfCodes], LOOPHOLE[poly.vtx[i].coord.clip]],
ThreeDScenes.OutCode];
ENDLOOP;
IF (poly2 = NIL) OR (poly2.length < 2 * poly.nVtces)
THEN poly2 ← tempPatch2 ← NEW[Patch[2 * poly.nVtces] ];
IF orOfCodes.near THEN [poly, poly2] ← Clip[ Near, poly, poly2];
IF orOfCodes.far THEN [poly, poly2] ← Clip[ Far, poly, poly2];
IF orOfCodes.left THEN [poly, poly2] ← Clip[ Left, poly, poly2];
IF orOfCodes.right THEN [poly, poly2] ← Clip[ Right, poly, poly2];
IF orOfCodes.bottom THEN [poly, poly2] ← Clip[Bottom, poly, poly2];
IF orOfCodes.top THEN [poly, poly2] ← Clip[ Top, poly, poly2];
RETURN[ poly ];
};
GetPatchClipState: PUBLIC PROC[ patch: REF Patch] ~ {
orOfCodes: ThreeDScenes.OutCode ← ThreeDScenes.NoneOut;
andOfCodes: ThreeDScenes.OutCode ← ThreeDScenes.AllOut;
FOR i: NAT IN [0..patch.nVtces) DO
orOfCodes ← LOOPHOLE[
Basics.BITOR[ LOOPHOLE[orOfCodes], LOOPHOLE[patch[i].coord.clip]],
ThreeDScenes.OutCode];
andOfCodes ← LOOPHOLE[
Basics.BITAND[LOOPHOLE[andOfCodes], LOOPHOLE[patch[i].coord.clip]],
ThreeDScenes.OutCode];
ENDLOOP;
IF andOfCodes # ThreeDScenes.NoneOut
THEN patch.clipState ← out
ELSE IF orOfCodes = ThreeDScenes.NoneOut
THEN patch.clipState ← in
ELSE patch.clipState ← clipped;
};
GetPtrPatchClipState: PROC[ shape: REF ShapeInstance, patch: REF PtrPatch] ~ {
orOfCodes: ThreeDScenes.OutCode ← ThreeDScenes.NoneOut;
andOfCodes: ThreeDScenes.OutCode ← ThreeDScenes.AllOut;
FOR j: NAT IN [0..patch.nVtces) DO
k: NAT ← patch.vtxPtr[j];
orOfCodes ← LOOPHOLE[
Basics.BITOR[ LOOPHOLE[orOfCodes], LOOPHOLE[shape.vertex[k].clip]],
ThreeDScenes.OutCode];
andOfCodes ← LOOPHOLE[
Basics.BITAND[LOOPHOLE[andOfCodes], LOOPHOLE[shape.vertex[k].clip]],
ThreeDScenes.OutCode];
ENDLOOP;
IF andOfCodes # ThreeDScenes.NoneOut
THEN patch.clipState ← out
ELSE IF orOfCodes = ThreeDScenes.NoneOut
THEN patch.clipState ← in
ELSE patch.clipState ← clipped;
};
Procedures for expansion to polygons
ShapeDo: PROC[ context: REF Context, shape: REF ShapeInstance, fullExpand: BOOLEAN,
     limitType: ATOM, limit: REAL]
   RETURNS [REF ShapeInstance] ~ {
MakeRoom: PROC [] ~ { -- ensure enough space for storing expanded shape
IF vertex = NIL     -- first we attempt to guess at how much space will be needed
THEN vertex ← NEW[ VertexSequence[newPts.length * shape.numSurfaces] ]
ELSE IF vertex.length < newPts.length + ptIndex  -- then we expand as needed
THEN {
vertex2: REF VertexSequence;
IF vertex.length * 2 > LAST[NAT]
THEN SIGNAL ThreeDScenes.Error[[$Unimplemented,
          "Sequence too long (Dragon needed)"]];
vertex2 ← NEW[ VertexSequence[vertex.length*2] ];
FOR i: NAT IN [0..vertex.length) DO     -- copy old sequence
IF vertex[i] # NIL
THEN vertex2[i] ← NEW[ Vertex ← vertex[i]^ ]
ELSE vertex2[i] ← NEW[ Vertex ];
ENDLOOP;
vertex ← vertex2;
};
IF shade = NIL     -- first we attempt to guess at how much space will be needed
THEN shade ← NEW[ ShadingSequence[newPts.length * shape.numSurfaces] ]
ELSE IF shade.length < newPts.length + ptIndex  -- then we expand as needed
THEN {
shade2: REF ShadingSequence ← NEW[ ShadingSequence[vertex.length*2] ];
FOR i: NAT IN [0..shade.length) DO     -- copy old sequence
IF shade[i] # NIL
THEN shade2[i] ← NEW[ ShadingValue ← shade[i]^ ]
ELSE shade2[i] ← NEW[ ShadingValue ];
ENDLOOP;
shade ← shade2;
};
IF surface = NIL    -- first we attempt to guess at how much space will be needed
THEN surface ← NEW[ PtrPatchSequence[newPatches.length * shape.numSurfaces] ]
ELSE IF surface.length < newPatches.length + patchIndex -- then we expand as needed
THEN {
surface2: REF PtrPatchSequence;
IF surface.length * 2 > LAST[NAT]
THEN SIGNAL ThreeDScenes.Error[[$Unimplemented,
          "Sequence too long (Dragon needed)"]];
surface2 ← NEW[ PtrPatchSequence[surface.length*2] ];
FOR i: NAT IN [0..surface.length) DO     -- copy old sequence
IF surface[i] # NIL
THEN surface2[i] ← NEW[ PtrPatch ← surface[i]^ ]
ELSE surface2[i] ← NEW[ PtrPatch ];
ENDLOOP;
surface ← surface2;
};
};
patch: REF PtrPatchSequence ← NARROW[ shape.surface ];
vertex: REF ThreeDScenes.VertexSequence;
shade: REF ThreeDScenes.ShadingSequence;
surface: REF PtrPatchSequence;
patchInfo: REF ThreeDScenes.ShadingSequence ← NARROW[
ThreeDScenes.GetShading[ shape, $PatchColors ]
];
newPts: REF ThreeDScenes.VertexInfoSequence;
newPatches: REF PtrPatchSequence;
ptIndex, patchIndex: INT ← 0;
class: REF ThreeDSurfaces.PatchProcs ← NARROW[
            Atom.GetPropFromList[context.props, patch[0].type] ];
IF class = NIL THEN ERROR ThreeDScenes.Error[[$MisMatch, "No class registered"]];
IF fullExpand AND class.expand = NIL
THEN ERROR ThreeDScenes.Error[[$MisMatch, "No expand Proc"]];
IF NOT fullExpand AND class.subdivide = NIL
THEN ERROR ThreeDScenes.Error[[$MisMatch, "No subdivide Proc"]];
FOR pNum: NAT IN [0..shape.numSurfaces) DO
IF fullExpand                 -- expand patch
THEN [newPts, newPatches] ← class.expand[shape, patch[pNum], limitType, limit]
ELSE [newPts, newPatches] ← class.subdivide[shape, patch[pNum], limitType, limit];
MakeRoom[];           -- get storage space for expanded shape
FOR i: NAT IN [0..newPts.length) DO      -- copy the vertices into the whole
vertex[i+ptIndex] ← NEW[ ThreeDScenes.Vertex ];
vertex[i+ptIndex]^ ← newPts[i].coord;
shade[i+ptIndex] ← NEW[ ThreeDScenes.ShadingValue ];
shade[i+ptIndex]^ ← newPts[i].shade;
IF patchInfo # NIL THEN {
shade[i+ptIndex].r ← shade[i+ptIndex].r * patchInfo[pNum].r;
shade[i+ptIndex].g ← shade[i+ptIndex].g * patchInfo[pNum].g;
shade[i+ptIndex].b ← shade[i+ptIndex].b * patchInfo[pNum].b;
};
ENDLOOP;
FOR i: NAT IN [0..newPatches.length) DO   -- copy the polygons into the whole
surface[i+patchIndex] ← NEW[ PtrPatch[newPatches[i].length] ];
FOR j: NAT IN [0..newPatches[i].length) DO
surface[i+patchIndex][j] ← newPatches[i][j] + ptIndex;
ENDLOOP;
surface[i+patchIndex].type ← newPatches[i].type;
surface[i+patchIndex].oneSided ← newPatches[i].oneSided;
surface[i+patchIndex].nVtces ← newPatches[i].length;
surface[i+patchIndex].clipState ← newPatches[i].clipState;
surface[i+patchIndex].props ← newPatches[i].props;
ENDLOOP;
ptIndex ← ptIndex + newPts.length;       -- update the indices
patchIndex ← patchIndex + newPatches.length;
ENDLOOP;
IF fullExpand THEN shape.type ← $ConvexPolygon;  -- set type to polygons if fully expanded
shape.vertex ← vertex;          -- point the shape at the new data
shape.shade ← shade;
shape.surface ← surface;
shape.numSurfaces ← surface.length;
ThreeDScenes.PutShading[ shape, $PatchColors, NIL ];
shape.shadingInValid ← TRUE;
shape.vtcesInValid ← TRUE;
RETURN[shape];
};
ShapeExpand: PUBLIC PROC[ context: REF Context, shape: REF ShapeInstance,
         limitType: ATOMNIL, limit: REAL ← 0.0]
     RETURNS [REF ShapeInstance] ~ {
Expands a whole shape, calling procedures supplied by the surface type
RETURN[ ShapeDo[ context, shape, TRUE, limitType, limit ] ];
};
ShapeSubdivide: PUBLIC PROC[ context: REF Context, shape: REF ShapeInstance,
          limitType: ATOMNIL, limit: REAL ← 0.0]
      RETURNS [REF ShapeInstance] ~ {
Subdivide a whole shape once, calling procedures supplied by the surface type
RETURN[ ShapeDo[ context, shape, FALSE, limitType, limit ] ];
};
ExpandToLines: PROC[ context: REF Context, patch: REF Patch,
        action: PROC[context: REF Context, patch: REF Patch] ] ~ {
expand a patch into curved lines, pass them as "$Paths"
class: REF ThreeDSurfaces.PatchProcs ← NARROW[
            Atom.GetPropFromList[context.props, patch.type] ];
IF class = NIL THEN ERROR ThreeDScenes.Error[[$MisMatch, "No class registered"]];
class.displayLines[context, patch, NIL, 0.0, action];  -- expand to default limit and display
PROC[ context: REF Context, patch: REF Patch, limitType: ATOM, limit: REAL, action: PROC];
};
limitInPixels: REAL ← 2.0;
ExpandToConvexPolys: PROC[ context: REF Context, patch: REF Patch,
          action: PROC[context: REF Context, patch: REF Patch] ] ~ {
expand a patch into displayable polygons
class: REF ThreeDSurfaces.PatchProcs ← NARROW[
            Atom.GetPropFromList[context.props, patch.type] ];
IF class = NIL THEN ERROR ThreeDScenes.Error[[$MisMatch, "No class registered"]];
IF context.alphaBuffer
THEN class.display[context, patch, NIL, 0.0, action]  -- expand to maximum and display
ELSE class.display[context, patch, NIL, limitInPixels, action]; -- expand for quick display
PROC[ context: REF Context, patch: REF Patch, limitType: ATOM, limit: REAL, action: PROC];
};
RegisterSurfaceType: PUBLIC PROC[ context: REF Context,
           type: ATOM, procs: REF ThreeDSurfaces.PatchProcs ] ~ {
context.props ← Atom.PutPropOnList[context.props, type, procs];
};
Procedures for Shading
BackFacing: PUBLIC PROC[ poly: REF Patch, useEyeSpace: BOOLEANFALSE]
    RETURNS [BOOLEAN] ~ {
Assumes left-handed space (eye space or screen space)
SELECT poly.type FROM
$ConvexPolygon  => IF useEyeSpace
THEN {
Backfacing test based on first vertex and adjacent vertices
this: ThreeDScenes.VertexInfo ← poly.vtx[0];
next: ThreeDScenes.VertexInfo ← poly.vtx[1];
last: ThreeDScenes.VertexInfo ← poly.vtx[2];
last: ThreeDScenes.VertexInfo ← poly.vtx[poly.nVtces - 1];
direction: Triple ← Vector3d.Normalize[Vector3d.Cross[
[next.coord.ex - this.coord.ex, next.coord.ey - this.coord.ey, next.coord.ez - this.coord.ez],
[last.coord.ex - this.coord.ex, last.coord.ey - this.coord.ey, last.coord.ez - this.coord.ez] ] ];
RETURN[ Vector3d.Dot[[this.coord.ex, this.coord.ey, this.coord.ez] , direction] > 0. ];
}
ELSE {      -- do check in image space, rejecting eensy polygon edges
s: REAL ← 1.0 / ScanConvert.justNoticeable;  -- scales visible feature size to 1
FOR i: NAT IN [0..poly.nVtces) DO
this: ThreeDScenes.VertexInfo ← poly.vtx[i];
next: ThreeDScenes.VertexInfo ← poly.vtx[(i+1) MOD poly.nVtces];
last: ThreeDScenes.VertexInfo ← poly.vtx[(i+poly.nVtces-1) MOD poly.nVtces];
zNorm: INT ←   -- integer computation of z-coord of normal
  ( Real.Fix[s*next.coord.sx - s*this.coord.sx] )
* ( Real.Fix[s*last.coord.sy - s*this.coord.sy] )
- ( Real.Fix[s*next.coord.sy - s*this.coord.sy] )
* ( Real.Fix[s*last.coord.sx - s*this.coord.sx] );
IF zNorm # 0 THEN RETURN[ zNorm > 0 ]
ENDLOOP;
SIGNAL ThreeDScenes.Error[[$Condition, "Edges too small for stable arithmetic"]];
};
$Bezier    => { 
Backfacing test based on convex hull being hidden behind its base
SIGNAL ThreeDScenes.Error[[$Unimplemented, "Backfacing for patches not done"]];
};
ENDCASE => SIGNAL ThreeDScenes.Error[[$Unimplemented, "Unknown type"]];
RETURN[ FALSE ];
};
ShadePoly: PUBLIC PROC[ context: REF Context, poly: REF Patch] ~ {
ref: REF ← Atom.GetPropFromList[poly.props, $Shininess];
shininess: REALIF ref # NIL THEN NARROW[ref, REF REAL]^ ELSE 0.0;
clr: RGB;
transmittance: REAL;
shading: ATOMNARROW[ Atom.GetPropFromList[ poly.props, $Type ] ];
FOR i: NAT IN [0..poly.nVtces) DO
IF shading = $Faceted THEN { -- calculate normals from vertex coords
lVtx: NAT ← (i + poly.nVtces - 1) MOD poly.nVtces;
nVtx: NAT ← (i + 1) MOD poly.nVtces;
[[ poly[i].shade.exn, poly[i].shade.eyn, poly[i].shade.ezn ]] ← Vector3d.Cross[
[ poly[nVtx].coord.ex - poly[i].coord.ex,    -- in eyespace, so do left-handed
poly[nVtx].coord.ey - poly[i].coord.ey,
poly[nVtx].coord.ez - poly[i].coord.ez ],
[ poly[lVtx].coord.ex - poly[i].coord.ex,
poly[lVtx].coord.ey - poly[i].coord.ey,
poly[lVtx].coord.ez - poly[i].coord.ez ]
];
};
[clr, transmittance] ← ThreeDScenes.ShadePt[context, poly[i], 0.0];
poly.vtx[i].shade.ir ← Real.FixC[clr.R * 255.0];
poly.vtx[i].shade.ig ← Real.FixC[clr.G * 255.0];
poly.vtx[i].shade.ib ← Real.FixC[clr.B * 255.0];
poly.vtx[i].shade.it ← Real.FixC[ transmittance * 255.0];
ENDLOOP;
};
GetShades: PUBLIC PROC[context: REF Context, shape: REF ShapeInstance] ~ {
Calculate shades for Faceted shading (Used for quick display)
AverageVertices: PROC[poly: REF PtrPatch] RETURNS[vtx: Vertex] ~ {
FOR i: NAT IN [0..poly.nVtces) DO
addVtx: Vertex ← shape.vertex[poly.vtxPtr[i]]^;
vtx.x ← vtx.x + addVtx.x; vtx.y ← vtx.y + addVtx.y; vtx.z ← vtx.z + addVtx.z;
vtx.ex ← vtx.ex + addVtx.ex; vtx.ey ← vtx.ey + addVtx.ey; vtx.ez ← vtx.ez + addVtx.ez;
vtx.sx ← vtx.sx + addVtx.sx; vtx.sy ← vtx.sy + addVtx.sy; vtx.sz ← vtx.sz + addVtx.sz;
vtx.clip ← LOOPHOLE[ Basics.BITOR[ LOOPHOLE[vtx.clip], LOOPHOLE[addVtx.clip] ],
       ThreeDScenes.OutCode];
ENDLOOP;
vtx.x ← vtx.x/poly.nVtces; vtx.y ← vtx.y/poly.nVtces; vtx.z ← vtx.z/poly.nVtces;
vtx.ex ← vtx.ex/poly.nVtces; vtx.ey ← vtx.ey/poly.nVtces; vtx.ez ← vtx.ez/poly.nVtces;
vtx.sx ← vtx.sx/poly.nVtces; vtx.sy ← vtx.sy/poly.nVtces; vtx.sz ← vtx.sz/poly.nVtces;
};
poly: REF PtrPatchSequence;
polyShades: REF ThreeDScenes.ShadingSequence ← NARROW[
ThreeDScenes.GetShading[ shape, $PatchColors ]
];
ref: REF ← Atom.GetPropFromList[shape.shadingProps, $Shininess];
shininess: REALIF ref # NIL THEN NARROW[ref, REF REAL]^ ELSE 0.0;
clr: RGB;
IF shape.surface = NIL OR polyShades = NIL
OR
ThreeDScenes.GetShading[shape, $Type] # $Faceted THEN {
SIGNAL ThreeDScenes.Error[[$Condition, "Data missing for faceted shading"]];
RETURN;
};
poly ← NARROW[ shape.surface, REF PtrPatchSequence ];
FOR i: NAT IN [0..poly.length) DO
IF poly[i] # NIL THEN {
trns: REAL ← 0.0;   -- transmittance
vtx: Vertex ← AverageVertices[poly[i]];
pt: ThreeDScenes.VertexInfo ← [ vtx, polyShades[i]^, NIL ];
[clr, trns] ← ThreeDScenes.ShadePt[context, pt, shininess]; -- calculate shade
IF polyShades[i] = NIL THEN polyShades[i] ← NEW[ ThreeDScenes.ShadingValue ];
polyShades[i].ir ← Real.FixC[clr.R * 255.0];
polyShades[i].ig ← Real.FixC[clr.G * 255.0];
polyShades[i].ib ← Real.FixC[clr.B * 255.0];
polyShades[i].it ← Real.FixC[ trns * 255.0];
};
ENDLOOP;
ThreeDScenes.PutShading[ shape, $PatchColors, polyShades ]
};
GetNormal: PROC[vertex: REF ThreeDScenes.VertexSequence,
      poly: REF PtrPatch, cVtx: NAT]
    RETURNS[normal: Triple] ~ {
lVtx: NAT ← (cVtx + poly.nVtces - 1) MOD poly.nVtces;
nVtx: NAT ← (cVtx + 1) MOD poly.nVtces;
normal ← Vector3d.Cross[      -- in object space so do right-handed
DiffPosns[ vertex[poly.vtxPtr[lVtx]], vertex[poly.vtxPtr[cVtx]] ],
DiffPosns[ vertex[poly.vtxPtr[nVtx]], vertex[poly.vtxPtr[cVtx]] ]
];
};
GetPolyNormals: PUBLIC PROC[shape: REF ShapeInstance] ~ {
Compute Normals, Sum normals at polygon corners to get polygon normal
surface: REF PtrPatchSequence;
patchInfo: REF ThreeDScenes.ShadingSequence ← NARROW[
ThreeDScenes.GetShading[ shape, $PatchColors ]
];
IF shape.type # $ConvexPolygon
THEN SIGNAL ThreeDScenes.Error[[$MisMatch, "Operation only for polygons"]];
IF shape.surface = NIL THEN RETURN;    -- not a shape (probably a light source)
surface ← NARROW[shape.surface, REF PtrPatchSequence];
IF patchInfo = NIL THEN
patchInfo ← NEW[ ThreeDScenes.ShadingSequence[shape.numSurfaces] ];
FOR i: NAT IN [0..patchInfo.length) DO
IF surface[i] # NIL THEN {
sumNmls: Triple ← [0., 0., 0.];
FOR cVtx: NAT IN [0..surface[i].nVtces) DO
sumNmls ← Vector3d.Add[ sumNmls,
          GetNormal[shape.vertex, surface[i], cVtx] ];
ENDLOOP;
sumNmls ← Vector3d.Normalize[sumNmls];
IF patchInfo[i] = NIL THEN patchInfo[i] ← NEW[ ThreeDScenes.ShadingValue ];
patchInfo[i].xn ← sumNmls.x;
patchInfo[i].yn ← sumNmls.y;
patchInfo[i].zn ← sumNmls.z;
};
ENDLOOP;
ThreeDScenes.PutShading[ shape, $PatchColors, patchInfo ];
shape.vtcesInValid ← TRUE;        -- make sure eye-space normals correct
};
GetVtxNormals: PUBLIC PROC[shape: REF ShapeInstance] ~ {
Sum normals for vertices given by adjacent polygon corners, only for polygons!
surface: REF PtrPatchSequence ← NARROW[shape.surface];
IF shape.type # $ConvexPolygon
THEN SIGNAL ThreeDScenes.Error[[$MisMatch, "Operation only for polygons"]];
IF shape.shade = NIL OR shape.shade.length # shape.vertex.length THEN {
shape.shade ← NEW[ ThreeDScenes.ShadingSequence[shape.vertex.length] ];
IF ThreeDScenes.GetShading[ shape, $Type ] = NIL
OR
ThreeDScenes.GetShading[ shape, $Type ] = $Faceted
THEN ThreeDScenes.PutShading[ shape, $Type, $Smooth];
};
FOR i: NAT IN [0..shape.vertex.length) DO
IF shape.shade[i] = NIL THEN shape.shade[i] ← NEW[ ThreeDScenes.ShadingValue ];
shape.shade[i].xn ← shape.shade[i].yn ← shape.shade[i].zn ← 0.0;
ENDLOOP;
FOR i: NAT IN [0..shape.numSurfaces) DO -- get normals at vertices, add to earlier ones
IF surface[i] # NIL THEN FOR cVtx: NAT IN [0..surface[i].nVtces) DO
OPEN shape.shade[surface[i].vtxPtr[cVtx]]; 
cornerNormal: Triple ← GetNormal[ shape.vertex, surface[i], cVtx ];
[[xn, yn, zn]] ← Vector3d.Add[ cornerNormal, [xn, yn, zn]];
ENDLOOP;
ENDLOOP;
FOR i: NAT IN [0..shape.shade.length) DO
IF shape.shade[i] # NIL THEN {
OPEN shape.shade[i];
[[xn, yn, zn]] ← Vector3d.Normalize[ [xn, yn, zn] ];
};
ENDLOOP;
shape.vtcesInValid ← TRUE;        -- make sure eye-space normals correct
};
Procedures for Sorting and Display
LoadSortSequence: PUBLIC PROC[ context: REF Context,
          buckets: REF ThreeDSurfaces.SortSequence ← NIL ]
      RETURNS[REF ThreeDSurfaces.SortSequence] ~ {
shape: REF ThreeDScenes.ShapeSequence ← context.shapes;
patchCount: CARDINAL ← 0;
bucketListPtr: NAT ← context.depthResolution;
minDepth: REAL ← context.yonLimit;
maxDepth: REAL ← context.hitherLimit;
zScale: REAL ← 1.;
FOR i: NAT IN [0.. shape.length) DO -- get minimum and maximum depth and patch count
IF shape[i] # NIL AND Atom.GetPropFromList[shape[i].props, $Hidden] = NIL THEN
IF shape[i].clipState # out AND shape[i].surface # NIL THEN {
radius: REAL ← shape[i].boundingRadius;
IF shape[i].centroid.ez - radius < minDepth
THEN minDepth ← shape[i].centroid.ez - radius;
IF shape[i].centroid.ez + radius > maxDepth
THEN maxDepth ← shape[i].centroid.ez + radius;
patchCount ← patchCount + shape[i].numSurfaces;
};
ENDLOOP;
minDepth ← MAX[minDepth, 0];     -- nothing allowed behind the eyepoint
IF (maxDepth - minDepth) > 0.
THEN zScale ← (context.depthResolution - 1) / (maxDepth - minDepth);
IF buckets = NIL OR buckets.length < patchCount + context.depthResolution THEN {
buckets ← NEW[ThreeDSurfaces.SortSequence[patchCount + context.depthResolution]];
FOR i: NAT IN [0..buckets.length) DO     -- initialize elements of sort structure
buckets[i] ← NEW[ ShapePatch ← [NIL, 0, 0] ];
ENDLOOP;
};
FOR i: NAT IN [0..context.depthResolution) DO     -- clear structure for new sort
buckets[i].shape ← NIL; buckets[i].next ← 0;
ENDLOOP;
FOR s: NAT IN [0.. shape.length) DO -- Enter patches (by z-centroid) in bucket lists
IF shape[s] # NIL AND Atom.GetPropFromList[shape[s].props, $Hidden] = NIL THEN
IF shape[s].clipState # out AND shape[s].surface # NIL THEN {
patch: REF PtrPatchSequence ← NARROW[shape[s].surface];
FOR i: NAT IN [0..shape[s].numSurfaces) DO
IF patch[i] # NIL THEN {
zSum: REAL ← 0.;
iz: INTEGER;
IF shape[s].clipState = in THEN {        -- unclipped, inside
FOR j: NAT IN [0..patch[i].nVtces) DO
zSum ← zSum + shape[s].vertex[patch[i].vtxPtr[j]].ez;
ENDLOOP;
iz ← Real.FixI[zScale * ((zSum / patch[i].nVtces) - minDepth)];
patch[i].clipState ← in;
}
ELSE IF shape[s].clipState = clipped THEN { -- clipped, evaluate clipping tags
GetPtrPatchClipState[ shape[s], patch[i]];
IF patch[i].clipState # out THEN {
FOR j: NAT IN [0..patch[i].nVtces) DO
k: NAT ← patch[i].vtxPtr[j];
zSum ← zSum + shape[s].vertex[k].ez;
ENDLOOP;
iz ← Real.FixI[zScale * ((zSum / patch[i].nVtces) - minDepth)];
IF iz < 0 THEN iz ← 0;
};
};
IF patch[i].clipState # out THEN {
IF buckets[iz].shape # NIL THEN {   -- previous entry in this bucket
buckets[bucketListPtr].next ← buckets[iz].next;
buckets[iz].next ← bucketListPtr;
iz ← bucketListPtr; bucketListPtr ← bucketListPtr + 1;
};
buckets[iz].shape ← shape[s];
buckets[iz].patch ← i;
};
};
ENDLOOP;
};
ENDLOOP;
RETURN[ buckets ];
};
GetDepths: PROC[ context: REF Context] ~ {
shape: REF ThreeDScenes.ShapeSequence ← context.shapes;
minDepth: REAL ← context.yonLimit;
maxDepth: REAL ← context.hitherLimit;
zScale: REAL ← 1.;
FOR i: NAT IN [0.. shape.length) DO -- get minimum and maximum depth
IF shape[i] # NIL AND Atom.GetPropFromList[shape[i].props, $Hidden] = NIL THEN
IF shape[i].clipState # out AND shape[i].surface # NIL THEN {
radius: REAL ← shape[i].boundingRadius;
IF shape[i].centroid.ez - radius < minDepth
THEN minDepth ← shape[i].centroid.ez - radius;
IF shape[i].centroid.ez + radius > maxDepth
THEN maxDepth ← shape[i].centroid.ez + radius;
};
ENDLOOP;
minDepth ← MAX[minDepth, context.hitherLimit]; -- nothing allowed behind the eyepoint
IF (maxDepth - minDepth) > 0.
THEN zScale ← (context.depthResolution - 1) / (maxDepth - minDepth);
FOR s: NAT IN [0.. shape.length) DO
IF shape[s] # NIL AND Atom.GetPropFromList[shape[s].props, $Hidden] = NIL THEN
IF shape[s].clipState # out AND shape[s].surface # NIL THEN {
patch: REF PtrPatchSequence ← NARROW[shape[s].surface];
FOR i: NAT IN [0..shape[s].numSurfaces) DO
IF patch[i] # NIL THEN
IF shape[s].clipState = in
THEN {        -- unclipped, inside
FOR j: NAT IN [0..patch[i].nVtces) DO
shape[s].vertex[patch[i].vtxPtr[j]].sz ←
 zScale * (shape[s].vertex[patch[i].vtxPtr[j]].ez - minDepth);
ENDLOOP;
patch[i].clipState ← in;
}
ELSE IF shape[s].clipState = clipped THEN-- clipped, ensure positive result
FOR j: NAT IN [0..patch[i].nVtces) DO
shape[s].vertex[patch[i].vtxPtr[j]].sz ← MAX[
0.0,
zScale * (shape[s].vertex[patch[i].vtxPtr[j]].ez - minDepth)
];
ENDLOOP;
ENDLOOP;
};
ENDLOOP;
};
currentColor: SampleSet;       -- global color for line drawings
currentShape: REF ShapeInstance;
UpdateShapeCache: PROC[context: REF Context, shape: REF ShapeInstance] ~ {
currentShape ← shape;
IF NARROW[ Atom.GetPropFromList[shape.shadingProps, $Type ], ATOM ] = $Lines
THEN currentColor ← ShapetoColorBytes[context, shape];
};
DoBackToFront: PUBLIC PROC[ context: REF Context,
          sortSeq: REF ThreeDSurfaces.SortSequence,
          action: PROC[REF ThreeDSurfaces.ShapePatch] ] ~ {
FOR i: NAT DECREASING IN [0..context.depthResolution) DO
j: NAT ← i;
WHILE sortSeq[j].shape # NIL DO
IF sortSeq[j].shape # currentShape THEN UpdateShapeCache[context, sortSeq[j].shape];
action[sortSeq[j]];        -- call back with next polygon in order
j ← sortSeq[j].next;
IF j = 0 THEN EXIT;
ENDLOOP;
ENDLOOP;
CombineBoxes[context];   -- get combined bounding box on scene
};
DoFrontToBack: PUBLIC PROC[ context: REF Context,
          sortSeq: REF ThreeDSurfaces.SortSequence,
          action: PROC[REF ThreeDSurfaces.ShapePatch] ] ~ {
FOR i: NAT IN [0..context.depthResolution) DO
j: NAT ← i;
WHILE sortSeq[j].shape # NIL DO
IF sortSeq[j].shape # currentShape THEN UpdateShapeCache[context, sortSeq[j].shape];
action[sortSeq[j]];        -- call back with next polygon in order
j ← sortSeq[j].next;
IF j = 0 THEN EXIT;
ENDLOOP;
ENDLOOP;
CombineBoxes[context];   -- get combined bounding box on scene
};
DoForPatches: PUBLIC PROC[ context: REF Context, set: REF ThreeDScenes.ShapeSequence,
          patchAction: PROC[REF ThreeDSurfaces.ShapePatch],
          shapeAction: PROC[REF ThreeDScenes.ShapeInstance] ← NIL ] ~ {
FOR s: NAT IN [0..set.length) DO IF Atom.GetPropFromList[set[s].props, $Hidden] = NIL
THEN {  
IF set[s].clipState = in AND set[s].type = $ConvexPolygon AND shapeAction # NIL
THEN shapeAction[set[s]]
ELSE IF set[s].clipState # out AND set[s].surface # NIL THEN {
patch: REF PtrPatchSequence ← NARROW[set[s].surface];
currentColor ← ShapetoColorBytes[context, set[s]];    -- set color for shape
FOR i: NAT IN [0..set[s].numSurfaces) DO IF patch[i] # NIL THEN {
IF set[s].clipState = in
THEN patch[i].clipState ← in       -- unclipped, inside
ELSE IF set[s].clipState = clipped
THEN GetPtrPatchClipState[ set[s], patch[i] ]
ELSE patch[i].clipState ← out;
IF patch[i].clipState # out THEN patchAction[NEW[ ShapePatch ← [set[s], i, 0] ]];
};
ENDLOOP;
};
};
ENDLOOP;
};
ShowObjects: PUBLIC PROC[ context: REF Context, frontToBack: BOOLEANFALSE ] ~ {
ShowPatch: PROC[p: REF ThreeDSurfaces.ShapePatch] ~{
patch: REF Patch ← ShapePatchToPatch[ context, p ];
OutputPatch[ context, patch ];
};
shape: REF ThreeDScenes.ShapeSequence ← context.shapes;
Get Everything (including light centroids) into eyespace
FOR i: NAT IN [0.. shape.length) DO
IF Atom.GetPropFromList[shape[i].props, $Hidden] = NIL OR shape[i].type = $Light
THEN IF shape[i].vtcesInValid THEN { 
shape[i].clipState ← ThreeDScenes.XfmToEyeSpace[ context, shape[i] ];
IF shape[i].clipState # out THEN ThreeDScenes.XfmToDisplay[context, shape[i] ];
};
ENDLOOP;
FOR i: NAT IN [0.. shape.length) DO         -- now, get it shaded
IF shape[i].shadingInValid AND (shape[i].clipState # out) THEN {
IF ThreeDScenes.GetShading[ shape[i], $Type ] = $Faceted
THEN GetShades[ context, shape[i] ];
ThreeDScenes.GetVtxShades[ context, shape[i] ];
};
ENDLOOP;
IF context.depthBuffer
THEN { GetDepths[context]; DoForPatches[context, context.shapes, ShowPatch]; }
ELSE {
context.sortSequence ← LoadSortSequence[context, NARROW[context.sortSequence] ];
IF frontToBack
THEN DoFrontToBack[context, NARROW[context.sortSequence], ShowPatch]
ELSE DoBackToFront[context, NARROW[context.sortSequence], ShowPatch];
};
};
ShowWireFrameObjects: PUBLIC PROC[context: REF Context ] ~ {
ShowPatch: PROC[p: REF ThreeDSurfaces.ShapePatch] ~{
patch: REF Patch ← ShapePatchToPatch[ context, p ];
OutputPatchEdges[ context, patch ];
};
ShowShape: PROC[s: REF ThreeDScenes.ShapeInstance] ~{
OutputShapeLines[ context, s ];
};
shape: REF ThreeDScenes.ShapeSequence ← context.shapes;
FOR i: NAT IN [0.. shape.length) DO
IF Atom.GetPropFromList[shape[i].props, $Hidden] = NIL AND shape[i].vtcesInValid THEN {
shape[i].clipState ← ThreeDScenes.XfmToEyeSpace[ context, shape[i] ];
IF shape[i].clipState # out THEN ThreeDScenes.XfmToDisplay[context, shape[i] ];
};
ENDLOOP;
IF context.renderMode = $LF OR context.renderMode = $Dithered
THEN ThreeDMisc.SetNamedColor[context, "White" ];
IF context.alphaBuffer
THEN DoForPatches[context, context.shapes, ShowPatch]
ELSE DoForPatches[context, context.shapes, ShowPatch, ShowShape];
CombineBoxes[context];   -- get combined bounding box on scene
};
EdgesToPolygons: PROC[context: REF Context, patch: REF Patch] ~ {
Make a polygon for each edge and tile it
baseRadius: REAL ← .003;
limit: NATIF patch.type = $Path THEN patch.nVtces - 1 ELSE patch.nVtces;
edge: REF Patch ← NEW[ Patch[6] ];
MakeEdge: PROC[pt1, pt2: ThreeDScenes.VertexInfo] ~ {
dirX, dirY, mag, offSetX1, offSetY1, offSetX2, offSetY2: REAL;
dirX ← pt2.coord.ex - pt1.coord.ex;     -- get unit vector in edge direction
dirY ← pt2.coord.ey - pt1.coord.ey;
mag ← RealFns.SqRt[ Sqr[dirX] + Sqr[dirY] ];
IF mag < ScanConvert.justNoticeable THEN RETURN[];   -- quit if too short to be seen
dirX ← dirX / mag; dirY ← dirY / mag;
offSetX1 ← dirX * baseRadius; offSetY1 ← dirY * baseRadius;
offSetX2 ← dirX * baseRadius; offSetY2 ← dirY * baseRadius;
FOR i: NAT IN [0..3) DO edge[i] ← pt1; ENDLOOP;
FOR i: NAT IN [3..6) DO edge[i] ← pt2; ENDLOOP;
edge[0].coord.ex ← edge[0].coord.ex+offSetY1; edge[0].coord.ey ← edge[0].coord.ey-offSetX1;
edge[1].coord.ex ← edge[1].coord.ex-offSetX1; edge[1].coord.ey ← edge[1].coord.ey-offSetY1;
edge[2].coord.ex ← edge[2].coord.ex-offSetY1; edge[2].coord.ey ← edge[2].coord.ey+offSetX1;
edge[3].coord.ex ← edge[3].coord.ex-offSetY2; edge[3].coord.ey ← edge[3].coord.ey+offSetX2;
edge[4].coord.ex ← edge[4].coord.ex+offSetX2; edge[4].coord.ey ← edge[4].coord.ey+offSetY2;
edge[5].coord.ex ← edge[5].coord.ex+offSetY2; edge[5].coord.ey ← edge[5].coord.ey-offSetX2;
IF edge.clipState = in THEN FOR i: NAT IN [0..6) DO
OPEN edge[i].coord;
[[sx, sy, sz]] ← ThreeDScenes.XfmPtToDisplay[ context, [ex, ey, ez] ];
ENDLOOP;
OutputPatch[context, edge];
};
edge.type ← $ConvexPolygon;
edge.nVtces ← 6;
edge.oneSided ← patch.oneSided;
edge.clipState ← patch.clipState;
edge.props ← Atom.PutPropOnList[ NIL, $Type, $Smooth ];
FOR i: NAT IN [0..limit) DO
MakeEdge[ patch[i], patch[(i+1) MOD patch.nVtces] ];
ENDLOOP;
};
OutputShapeLines: PUBLIC PROC[context: REF Context,
          s: REF ThreeDScenes.ShapeInstance] ~ { 
Unclipped shape output
ax, ay, bx, by: NAT;
color: SampleSet ← ShapetoColorBytes[context, s];
imagerCtx: Imager.Context;
usingImager: BOOLEANFALSE;
patches: REF PtrPatchSequence;
IF s.surface = NIL THEN RETURN;
IF context.renderMode = $LF OR context.renderMode = $Dithered THEN {
IF context.renderMode = $Dithered THEN {
usingImager ← TRUE;
imagerCtx ← ThreeDMisc.GetImagerContext[context];
};
patches NARROW[s.surface, REF PtrPatchSequence];
FOR i: NAT IN [0..s.numSurfaces) DO
IF patches[i].type # $ConvexPolygon
THEN SIGNAL ThreeDScenes.Error[[$MisMatch, "Operation only for convex polygons"]]
ELSE FOR j: NAT IN [0..patches[i].nVtces] DO
k: NATIF j = patches[i].nVtces THEN 0 ELSE j;
bx ← Real.RoundC[s.vertex[patches[i].vtxPtr[k]].sx];
by ← Real.RoundC[s.vertex[patches[i].vtxPtr[k]].sy];
IF j > 0 THEN
IF NOT usingImager
THEN ScanConvert.PutLine[ context.display, [ax,ay], [bx,by], color ]
ELSE Imager.MaskVectorI[ imagerCtx, ax, ay, bx, by ];
ax ← bx; ay ← by;
ENDLOOP;
IF stopMe THEN HoldEverything[];  -- shut down if stop signal received
ENDLOOP;
};
OutputPatchEdges: PUBLIC PROC[context: REF Context, patch: REF Patch] ~ {
imagerCtx: Imager.Context;
usingImager: BOOLEANFALSE;
IF patch = NIL OR patch.clipState = out THEN RETURN[];    -- reject if outside frame
IF patch.type # $ConvexPolygon AND patch.type # $Path   -- expand to curves
THEN { ExpandToLines[context, patch, OutputPatchEdges]; RETURN[]; };
IF context.alphaBuffer              -- antialiased
THEN { EdgesToPolygons[context, patch]; RETURN[]; };
IF context.renderMode = $LF OR context.renderMode = $Dithered THEN {
IF context.renderMode = $Dithered THEN {
usingImager ← TRUE;
imagerCtx ← ThreeDMisc.GetImagerContext[context];
};
IF patch.clipState = in THEN {
ax, ay, bx, by: NAT;
limit: NATIF patch.type = $Path THEN patch.nVtces - 1 ELSE patch.nVtces;
FOR j: NAT IN [0..limit] DO
i: NATIF j = patch.nVtces THEN 0 ELSE j;
bx ← Real.RoundC[patch[i].coord.sx];
by ← Real.RoundC[patch[i].coord.sy];
IF j > 0 THEN
IF NOT usingImager
THEN ScanConvert.PutLine[ context.display, [ax,ay], [bx,by], currentColor ]
ELSE Imager.MaskVectorI[ imagerCtx, ax, ay, bx, by ];
ax ← bx; ay ← by;
ENDLOOP;
}
ELSE IF patch.clipState = clipped THEN {
patch ← ClipPoly[ context, patch ];
IF patch.nVtces > 2 OR ( patch.type = $Path AND patch.nVtces > 1 ) THEN {
ax, ay, bx, by: NAT;
limit: NATIF patch.type = $Path THEN patch.nVtces - 1 ELSE patch.nVtces;
FOR j: NAT IN [0..limit] DO
i: NATIF j = patch.nVtces THEN 0 ELSE j;
x, y, z: REAL;
[[x, y, z]] ← ThreeDScenes.XfmPtToDisplay[
context,
[patch[i].coord.ex, patch[i].coord.ey, patch[i].coord.ez]
];
bx ← Real.RoundC[x]; by ← Real.RoundC[y];
IF j > 0 THEN
IF NOT usingImager
THEN ScanConvert.PutLine[ context.display, [ax,ay], [bx,by], currentColor ]
ELSE Imager.MaskVectorI[ imagerCtx, ax, ay, bx, by ];
ax ← bx; ay ← by;
ENDLOOP;
};
};
IF stopMe THEN HoldEverything[];  -- shut down if stop signal received
};
OutputPatch: PUBLIC PROC[context: REF Context, patch: REF Patch] ~ {
IF patch = NIL OR patch.clipState = out THEN RETURN[];   -- reject if outside frame
IF NARROW[ Atom.GetPropFromList[patch.props, $Type ], ATOM ] = $Lines THEN {
OutputPatchEdges[context, patch]; -- to show sorted, anti-aliased line drawings
RETURN[];
};
IF patch.type # $ConvexPolygon     -- catch unexpanded patches, etc.
THEN { ExpandToConvexPolys[context, patch, OutputPatch]; RETURN; };
IF NARROW[ Atom.GetPropFromList[patch.props, $Type ], ATOM ] = $Lines THEN {
OutputPatchEdges[context, patch]; -- to show expanded curved patches, etc
RETURN[];
};
IF patch.clipState = clipped THEN {
patch ← ClipPoly[context, patch];
FOR i: NAT IN [0..patch.nVtces) DO OPEN patch[i].coord;
[[sx, sy, sz]] ← ThreeDScenes.XfmPtToDisplay[context, [ex, ey, ez]];
ENDLOOP;
};
IF patch.nVtces > 2 THEN {
IF NOT BackFacing[ patch
      ! ThreeDScenes.Error => IF reason.code = $Condition
               THEN
GO TO GiveUp
     
]
THEN Tilers.PolygonTiler[context, patch]
ELSE IF patch.oneSided          -- backfacing!!
THEN RETURN[]          -- Reject if closed surface
ELSE {
FOR i: NAT IN [0..patch.nVtces) DO-- recalculate shading for back side
r, g, b, t: REAL;
patch[i].shade.exn ← -patch[i].shade.exn;  -- reverse normals
patch[i].shade.eyn ← -patch[i].shade.eyn;
patch[i].shade.ezn ← -patch[i].shade.ezn;
[[r,g,b], t] ← ThreeDScenes.ShadePt[ context, patch[i], 0.0 ];
patch[i].shade.ir ← Real.FixC[r * 255.0];
patch[i].shade.ig ← Real.FixC[g * 255.0];
patch[i].shade.ib ← Real.FixC[b * 255.0];
patch[i].shade.it ← Real.FixC[ t * 255.0];
ENDLOOP;
FOR i: NAT IN [0..patch.nVtces/2) DO  -- reorder to keep clockwise on display
tempVtx: ThreeDScenes.VertexInfo ← patch[i];
patch[i] ← patch[patch.nVtces-1 - i];
patch[patch.nVtces-1 - i] ← tempVtx;
ENDLOOP;
Tilers.PolygonTiler[context, patch]
};
EXITS GiveUp => NULL
};
IF stopMe THEN HoldEverything[];  -- shut down if stop signal received
};
END.