<> <> <> 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 <> 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: BOOLEAN _ FALSE; <> 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: BOOLEAN _ IF 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; }; <> LoadShape: PUBLIC PROC[ shape: REF ShapeInstance, fileName: Rope.ROPE, type: ATOM _ $ConvexPolygon, insideVisible: BOOLEAN _ FALSE ] ~ { numVtces: NAT; min, max: Triple; radius: REAL; nullVertex: ThreeDScenes.Vertex; -- gets initialized vertex record nullShade: ThreeDScenes.ShadingValue; -- gets initialized ShadingValue record input: IO.STREAM; <> [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]; <> 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: BOOLEAN _ FALSE] ~ { 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: NAT _ IO.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; }; <> 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 { <> 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; }; <> 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: ATOM _ NIL, limit: REAL _ 0.0] RETURNS [REF ShapeInstance] ~ { <> RETURN[ ShapeDo[ context, shape, TRUE, limitType, limit ] ]; }; ShapeSubdivide: PUBLIC PROC[ context: REF Context, shape: REF ShapeInstance, limitType: ATOM _ NIL, limit: REAL _ 0.0] RETURNS [REF ShapeInstance] ~ { <> RETURN[ ShapeDo[ context, shape, FALSE, limitType, limit ] ]; }; ExpandToLines: PROC[ context: REF Context, patch: REF Patch, action: PROC[context: REF Context, patch: REF Patch] ] ~ { <> 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] ] ~ { <> 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]; }; <> BackFacing: PUBLIC PROC[ poly: REF Patch, useEyeSpace: BOOLEAN _ FALSE] RETURNS [BOOLEAN] ~ { <> SELECT poly.type FROM $ConvexPolygon => IF useEyeSpace THEN { <> this: ThreeDScenes.VertexInfo _ poly.vtx[0]; next: ThreeDScenes.VertexInfo _ poly.vtx[1]; last: ThreeDScenes.VertexInfo _ poly.vtx[2]; <> 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 => { <