<> <> <> DIRECTORY Atom USING [GetPropFromList], Real USING [RoundC, 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], Linear3d USING [DotProd, Triple, Quad, Normalize, CrossProd, SumTriple, EvalPlane], ThreeDMisc USING [GetMappedColor, GetImagerContext, SetNamedColor], ThreeDScenes USING [SixSides, OutCode, NoneOut, AllOut, ShapeInstance, ShapeSequence, Context, ShadingSequence, ClipState, Vertex, VertexInfo, VertexSequence, SetUpStandardFile, ReadColors, PutShading, GetShading, ShadePt, FillInBackGround, XfmToEyeSpace, XfmToDisplay, ReadVertexCoords, FillViewPort, GetVtxShades, XfmPtToDisplay], Tilers USING [PolygonTiler], ThreeDPolygons USING [Polygon, PtrPolySequence, PtrPoly, SortSequence, ShapePolygon]; ThreeDPolygonsImpl: CEDAR PROGRAM IMPORTS Atom, Real, RealFns, IO, Basics, ThreeDScenes, Tilers, Linear3d, ScanConvert, ThreeDMisc, Pixels, Imager EXPORTS ThreeDPolygons = BEGIN <> ThreeDPolygonsError: PUBLIC SIGNAL [reason: ATOM] = CODE; RGB: TYPE ~ ImagerColor.RGB; -- [ r, g, b: REAL]; SixSides: TYPE ~ ThreeDScenes.SixSides; Triple: TYPE ~ Linear3d.Triple; SampleSet: TYPE ~ Pixels.SampleSet; ShapeInstance: TYPE ~ ThreeDScenes.ShapeInstance; Polygon: TYPE ~ ThreeDPolygons.Polygon; Vertex: TYPE ~ ThreeDScenes.Vertex; tempPoly, tempPoly2: REF Polygon _ 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: 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 ThreeDScenes.Context, s: REF ShapeInstance] RETURNS[SampleSet] ~ { ref: REF ANY _ ThreeDScenes.GetShading[ s, $Color]; r, g, b: REAL; ir, ig, ib: INTEGER; 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 => {}; $Dithered, $PseudoClr => { IF pixelBytes.length # 1 THEN pixelBytes _ Pixels.GetSampleSet[1]; pixelBytes[0] _ ThreeDMisc.GetMappedColor[context, [ir, ig, ib] ]; }; $Grey => { IF pixelBytes.length # 1 THEN pixelBytes _ Pixels.GetSampleSet[1]; pixelBytes[0] _ (ir + ig + ib)/3; }; $FullClr, $Dorado24 => { IF pixelBytes.length # 3 THEN pixelBytes _ Pixels.GetSampleSet[3]; pixelBytes[0] _ ir; pixelBytes[1] _ ig; pixelBytes[2] _ ib; }; ENDCASE => SIGNAL ThreeDPolygonsError[$BadRenderMode]; RETURN[pixelBytes]; }; CombineBoxes: PROC[context: REF ThreeDScenes.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; }; <> LoadShape: PUBLIC PROC[ shape: REF ShapeInstance, fileName: Rope.ROPE, type: ATOM _ $Polyhedron ] ~ { numVtces: NAT; min, max: Triple; radius: REAL; <> input: IO.STREAM; [input, numVtces] _ ThreeDScenes.SetUpStandardFile[fileName]; shape.numSurfaces _ IO.GetInt[input]; shape.type _ type; ThreeDScenes.ReadVertexCoords[shape, input, numVtces]; -- read vertices ReadPolygons[shape, input, shape.numSurfaces]; -- read polygons <> min _ max _ [shape.vertex[0].x, shape.vertex[0].y, shape.vertex[0].z]; FOR i: NAT IN (0..numVtces) DO -- get min and max in x, y, and z 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; }; ReadPolygons: PUBLIC PROC[shape: REF ShapeInstance, in: IO.STREAM, nPolys: NAT] ~ { surface: REF ThreeDPolygons.PtrPolySequence; shape.surface _ NEW[ ThreeDPolygons.PtrPolySequence[shape.numSurfaces] ]; surface _ NARROW[shape.surface, REF ThreeDPolygons.PtrPolySequence]; FOR i: NAT IN [0..nPolys) DO -- Read Polygons nVtces: NAT _ IO.GetInt[in]; surface[i] _ NEW[ThreeDPolygons.PtrPoly[nVtces]]; surface[i].nVtces _ nVtces; FOR j: NAT IN [0..nVtces) DO surface[i].vtxPtr[j] _ IO.GetInt[in] - 1; -- switch to counting from zero ENDLOOP; ENDLOOP; }; GetPolygonColors: PUBLIC PROC[shape: REF ShapeInstance, fileName: Rope.ROPE] ~ { in: IO.STREAM; nPolys: NAT; ThreeDScenes.PutShading[ shape, $Type, $Faceted ]; [in, nPolys] _ ThreeDScenes.SetUpStandardFile[fileName]; ThreeDScenes.ReadColors[shape, in, nPolys]; GetPolyNormals[shape]; ThreeDScenes.PutShading[ shape, $PolyColors, fileName ]; shape.shadingInValid _ TRUE; }; <> ClipPoly: PUBLIC PROC[ context: REF ThreeDScenes.Context, shape: REF ShapeInstance, poly, poly2: REF Polygon] RETURNS [REF Polygon, REF Polygon] ~ { Clip: PROC[side: SixSides, pIn, pOut: REF Polygon] RETURNS [REF Polygon, REF Polygon] = { Dist: PROC[side: SixSides, vtx: Vertex] RETURNS [REAL] = { -- + inside, - outside RETURN[ Linear3d.EvalPlane[ context.clippingPlanes[side], [vtx.ex, vtx.ey, vtx.ez] ] ]; }; shading: ATOM _ NARROW[ ThreeDScenes.GetShading[shape, $Type] ]; lastDist, dist: REAL; outCnt, last: NAT; IF pIn.nVtces < 3 THEN RETURN[ pIn, pOut ]; -- return if degenerate last _ pIn.nVtces - 1; outCnt _ 0; lastDist _ Dist[side, pIn.vtx[last].coord]; IF pOut.length < 2 * pIn.nVtces THEN pOut _ NEW[Polygon[2 * pIn.nVtces]]; FOR i: NAT IN [0..pIn.nVtces) DO a, b: REAL; dist _ Dist[side, pIn.vtx[i].coord]; IF lastDist * dist < 0. THEN { -- put out point if clip plane crossed b _ dist / (dist - lastDist); a _ 1.0 - 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].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; IF shading # $Fancy THEN { -- Integer shading, using precomputed colors 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 ]; } ELSE { -- fancy shading, using original colors and texture 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.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; }; IF (shading = $Shiny OR shading = $Fancy) THEN { -- normals 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.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].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; IF shading # $Fancy THEN { -- Integer shading, using precomputed colors 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; } ELSE { -- fancy shading, using original colors and texture 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.txtrX _ pIn.vtx[i].shade.txtrX; pOut.vtx[outCnt].shade.txtrY _ pIn.vtx[i].shade.txtrY; }; IF (shading = $Shiny OR shading = $Fancy) THEN { -- normals 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.nVtces _ outCnt; RETURN [ pOut, pIn ]; }; -- end Clip Proc orOfCodes: ThreeDScenes.OutCode _ ThreeDScenes.NoneOut; FOR i: NAT IN [0..poly.nVtces) DO orOfCodes _ LOOPHOLE[ Basics.BITOR[LOOPHOLE[orOfCodes], LOOPHOLE[poly.vtx[i].coord.clip]], ThreeDScenes.OutCode]; ENDLOOP; 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, poly2 ]; }; <> BackFacing: PUBLIC PROC[ poly: REF Polygon] RETURNS [BOOLEAN] ~ { <> this: ThreeDScenes.VertexInfo _ poly.vtx[0]; next: ThreeDScenes.VertexInfo _ poly.vtx[1]; last: ThreeDScenes.VertexInfo _ poly.vtx[poly.nVtces - 1]; direction: Triple _ Linear3d.Normalize[Linear3d.CrossProd[ [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[ Linear3d.DotProd[[this.coord.ex, this.coord.ey, this.coord.ez] , direction] > 0. ]; }; <<>> ShadePoly: PUBLIC PROC[ context: REF ThreeDScenes.Context, poly: REF Polygon] ~ { ref: REF _ Atom.GetPropFromList[poly.props, $Shininess]; shininess: REAL _ IF ref # NIL THEN NARROW[ref, REF REAL]^ ELSE 0.0; clr: RGB; transmittance: REAL; shading: ATOM _ NARROW[ Atom.GetPropFromList[ poly.props, $Type ] ]; IF shading = $Shiny OR shading = $Fancy THEN shininess _ 0.0; -- highlights later FOR i: NAT IN [0..poly.nVtces) DO -- Check limits and store with vertices [clr, transmittance] _ ThreeDScenes.ShadePt[context, poly.vtx[i], shininess]; 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 ThreeDScenes.Context, shape: REF ShapeInstance] ~ { <> AverageVertices: PROC[poly: REF ThreeDPolygons.PtrPoly] 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 ThreeDPolygons.PtrPolySequence; ref: REF _ Atom.GetPropFromList[shape.shadingProps, $Shininess]; shininess: REAL _ IF ref # NIL THEN NARROW[ref, REF REAL]^ ELSE 0.0; clr: RGB; IF shape.surface = NIL OR ThreeDScenes.GetShading[shape, $Type] # $Faceted THEN { SIGNAL ThreeDPolygonsError[$InsufficientInfo]; RETURN; }; poly _ NARROW[ shape.surface, REF ThreeDPolygons.PtrPolySequence ]; FOR i: NAT IN [0..shape.shade.length) DO IF poly[i].clipState # out THEN { trns: REAL _ 0.0; -- transmittance vtx: Vertex _ AverageVertices[poly[i]]; pt: ThreeDScenes.VertexInfo _ [ vtx, shape.shade[i], NIL ]; [clr, trns] _ ThreeDScenes.ShadePt[context, pt, shininess]; -- calculate shade shape.shade[i].ir _ Real.FixC[clr.R * 255.0]; shape.shade[i].ig _ Real.FixC[clr.G * 255.0]; shape.shade[i].ib _ Real.FixC[clr.B * 255.0]; shape.shade[i].it _ Real.FixC[ trns * 255.0]; }; ENDLOOP; shape.shadingInValid _ FALSE; }; GetNormal: PROC[vertex: REF ThreeDScenes.VertexSequence, poly: REF ThreeDPolygons.PtrPoly, cVtx: NAT] RETURNS[normal: Triple] ~ { lVtx: NAT _ (cVtx + poly.nVtces - 1) MOD poly.nVtces; nVtx: NAT _ (cVtx + 1) MOD poly.nVtces; normal _ Linear3d.CrossProd[ DiffPosns[ vertex[poly.vtxPtr[lVtx]], vertex[poly.vtxPtr[cVtx]] ], DiffPosns[ vertex[poly.vtxPtr[nVtx]], vertex[poly.vtxPtr[cVtx]] ] ]; }; GetPolyNormals: PUBLIC PROC[shape: REF ShapeInstance] ~ { <> surface: REF ThreeDPolygons.PtrPolySequence; IF shape.surface = NIL THEN RETURN; -- not a shape (probably a light source) surface _ NARROW[shape.surface, REF ThreeDPolygons.PtrPolySequence]; IF shape.shade = NIL OR shape.shade.length # shape.numSurfaces THEN { shape.shade _ NEW[ ThreeDScenes.ShadingSequence[shape.numSurfaces] ]; ThreeDScenes.PutShading[shape, $Type, $Faceted]; }; FOR i: NAT IN [0..shape.numSurfaces) DO sumNmls: Triple _ [0., 0., 0.]; FOR cVtx: NAT IN [0..surface[i].nVtces) DO sumNmls _ Linear3d.SumTriple[ sumNmls, GetNormal[shape.vertex, surface[i], cVtx] ]; ENDLOOP; sumNmls _ Linear3d.Normalize[sumNmls]; shape.shade[i].xn _ sumNmls.x; shape.shade[i].yn _ sumNmls.y; shape.shade[i].zn _ sumNmls.z; ENDLOOP; shape.vtcesInValid _ TRUE; -- make sure eye-space normals correct }; <<>> GetVtxNormals: PUBLIC PROC[shape: REF ShapeInstance] ~ { <> surface: REF ThreeDPolygons.PtrPolySequence _ NARROW[shape.surface]; 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 shape.shade[i].xn _ shape.shade[i].yn _ shape.shade[i].zn _ 0.; ENDLOOP; FOR i: NAT IN [0..shape.numSurfaces) DO -- get normals at vertices, add to earlier ones 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]] _ Linear3d.SumTriple[ cornerNormal, [xn, yn, zn]]; ENDLOOP; ENDLOOP; FOR i: NAT IN [0..shape.shade.length) DO OPEN shape.shade[i]; [[xn, yn, zn]] _ Linear3d.Normalize[ [xn, yn, zn] ]; ENDLOOP; shape.vtcesInValid _ TRUE; -- make sure eye-space normals correct }; <> LoadSortSequence: PUBLIC PROC[ context: REF ThreeDScenes.Context, buckets: REF ThreeDPolygons.SortSequence _ NIL ] RETURNS[REF ThreeDPolygons.SortSequence] ~ { shape: REF ThreeDScenes.ShapeSequence _ context.shapes; polygonCount: NAT _ 0; bucketListPtr: NAT _ context.depthResolution; minDepth, maxDepth: REAL _ shape[0].centroid.ez; zScale: REAL _ 1.; FOR i: NAT IN [0.. shape.length) DO -- get minimum and maximum depth and polygon count 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; polygonCount _ polygonCount + 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 < polygonCount + context.depthResolution THEN buckets _ NEW[ThreeDPolygons.SortSequence[polygonCount + context.depthResolution]]; FOR i: NAT IN [0..context.depthResolution) DO buckets[i].shape _ NIL; buckets[i].next _ 0; ENDLOOP; FOR s: NAT IN [0.. shape.length) DO -- Enter polygons (by z-centroid) in bucket lists IF shape[s].clipState # out AND shape[s].surface # NIL THEN { poly: REF ThreeDPolygons.PtrPolySequence _ NARROW[shape[s].surface]; FOR i: NAT IN [0..shape[s].numSurfaces) DO zSum: REAL _ 0.; iz: INTEGER; IF shape[s].clipState = in THEN { -- unclipped, inside FOR j: NAT IN [0..poly[i].nVtces) DO zSum _ zSum + shape[s].vertex[poly[i].vtxPtr[j]].ez; ENDLOOP; iz _ Real.FixI[zScale * ((zSum / poly[i].nVtces) - minDepth)]; poly[i].clipState _ in; } ELSE IF shape[s].clipState = clipped THEN { -- clipped, evaluate clipping tags orOfCodes: ThreeDScenes.OutCode _ ThreeDScenes.NoneOut; andOfCodes: ThreeDScenes.OutCode _ ThreeDScenes.AllOut; FOR j: NAT IN [0..poly[i].nVtces) DO k: NAT _ poly[i].vtxPtr[j]; zSum _ zSum + shape[s].vertex[k].ez; orOfCodes _ LOOPHOLE[ Basics.BITOR[ LOOPHOLE[orOfCodes], LOOPHOLE[shape[s].vertex[k].clip] ], ThreeDScenes.OutCode]; andOfCodes _ LOOPHOLE[ Basics.BITAND[LOOPHOLE[andOfCodes], LOOPHOLE[shape[s].vertex[k].clip]], ThreeDScenes.OutCode]; ENDLOOP; IF andOfCodes # ThreeDScenes.NoneOut THEN poly[i].clipState _ out ELSE IF orOfCodes = ThreeDScenes.NoneOut THEN poly[i].clipState _ in ELSE poly[i].clipState _ clipped; IF poly[i].clipState # out THEN { iz _ Real.FixI[zScale * ((zSum / poly[i].nVtces) - minDepth)]; IF iz < 0 THEN iz _ 0; }; }; IF poly[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].polygon _ i; }; ENDLOOP; }; ENDLOOP; RETURN[ buckets ]; }; DoBackToFront: PUBLIC PROC[ context: REF ThreeDScenes.Context, sortSeq: REF ThreeDPolygons.SortSequence, action: PROC[ThreeDPolygons.ShapePolygon] ] ~ { ThreeDScenes.FillInBackGround[context]; FOR i: NAT DECREASING IN [0..context.depthResolution) DO j: NAT _ i; WHILE sortSeq[j].shape # NIL DO 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 ThreeDScenes.Context, sortSeq: REF ThreeDPolygons.SortSequence, action: PROC[ThreeDPolygons.ShapePolygon] ] ~ { ThreeDScenes.FillViewPort[context, [0.0,0.0,0.0] ]; -- clear screen and alpha buffer FOR i: NAT IN [0..context.depthResolution) DO j: NAT _ i; WHILE sortSeq[j].shape # NIL DO 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 ThreeDScenes.FillInBackGround[context]; }; <<>> DoForPolygons: PUBLIC PROC[ set: REF ThreeDScenes.ShapeSequence, action1: PROC[ThreeDPolygons.ShapePolygon], action2: PROC[REF ThreeDScenes.ShapeInstance] _ NIL ] ~ { FOR s: NAT IN [0..set.length) DO IF set[s].clipState = in AND action2 # NIL THEN action2[set[s]] ELSE IF set[s].clipState # out AND set[s].surface # NIL THEN { poly: REF ThreeDPolygons.PtrPolySequence _ NARROW[set[s].surface]; FOR i: NAT IN [0..set[s].numSurfaces) DO IF set[s].clipState = in THEN poly[i].clipState _ in -- unclipped, inside ELSE IF set[s].clipState = clipped THEN { -- clipped, evaluate clipping tags orOfCodes: ThreeDScenes.OutCode _ ThreeDScenes.NoneOut; andOfCodes: ThreeDScenes.OutCode _ ThreeDScenes.AllOut; FOR j: NAT IN [0..poly[i].nVtces) DO k: NAT _ poly[i].vtxPtr[j]; orOfCodes _ LOOPHOLE[ Basics.BITOR[ LOOPHOLE[orOfCodes], LOOPHOLE[set[s].vertex[k].clip] ], ThreeDScenes.OutCode]; andOfCodes _ LOOPHOLE[ Basics.BITAND[LOOPHOLE[andOfCodes], LOOPHOLE[set[s].vertex[k].clip]], ThreeDScenes.OutCode]; ENDLOOP; IF andOfCodes # ThreeDScenes.NoneOut THEN poly[i].clipState _ out ELSE IF orOfCodes = ThreeDScenes.NoneOut THEN poly[i].clipState _ in ELSE poly[i].clipState _ clipped; }; IF poly[i].clipState # out THEN action1[ [set[s], i, 0] ]; ENDLOOP; }; ENDLOOP; }; ShowObjects: PUBLIC PROC[ context: REF ThreeDScenes.Context, frontToBack: BOOLEAN _ FALSE ] ~ { ShowPolygon: PROC[p: ThreeDPolygons.ShapePolygon] ~{ OutputPoly[ context, p ]; }; shape: REF ThreeDScenes.ShapeSequence _ context.shapes; sortSeq: REF ThreeDPolygons.SortSequence; <> FOR i: NAT IN [0.. shape.length) DO 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 everything shaded IF shape[i].shadingInValid AND (shape[i].clipState # out) THEN { IF shape[i].shade = NIL THEN GetPolyNormals[shape[i]]; -- makes default faceted IF ThreeDScenes.GetShading[ shape[i], $Type ] = $Faceted THEN GetShades[ context, shape[i] ] ELSE ThreeDScenes.GetVtxShades[ context, shape[i] ]; }; ENDLOOP; sortSeq _ LoadSortSequence[context]; IF frontToBack THEN DoFrontToBack[context, sortSeq, ShowPolygon] ELSE DoBackToFront[context, sortSeq, ShowPolygon]; }; ShowWireFrameObjects: PUBLIC PROC[context: REF ThreeDScenes.Context ] ~ { ShowPolygon: PROC[p: ThreeDPolygons.ShapePolygon] ~{ OutputPlygnLines[ context, p ]; }; ShowShape: PROC[s: REF ThreeDScenes.ShapeInstance] ~{ OutputShapeLines[ context, s ]; }; shape: REF ThreeDScenes.ShapeSequence _ context.shapes; FOR i: NAT IN [0.. shape.length) DO 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; ThreeDScenes.FillInBackGround[context]; IF context.renderMode = $LF OR context.renderMode = $Dithered THEN ThreeDMisc.SetNamedColor[context, "White" ]; DoForPolygons[context.shapes, ShowPolygon, ShowShape]; CombineBoxes[context]; -- get combined bounding box on scene }; OutputShapeLines: PUBLIC PROC[context: REF ThreeDScenes.Context, s: REF ThreeDScenes.ShapeInstance] ~ { <> ax, ay, bx, by: NAT; color: SampleSet _ ShapetoColorBytes[context, s]; imagerCtx: Imager.Context; usingImager: BOOLEAN _ FALSE; polygons: REF ThreeDPolygons.PtrPolySequence; IF s.surface = NIL THEN RETURN; IF context.renderMode = $LF OR context.renderMode = $Dithered THEN { usingImager _ TRUE; imagerCtx _ ThreeDMisc.GetImagerContext[context]; }; polygons _ NARROW[s.surface, REF ThreeDPolygons.PtrPolySequence]; FOR i: NAT IN [0..s.numSurfaces) DO FOR j: NAT IN [0..polygons[i].nVtces] DO k: NAT _ IF j = polygons[i].nVtces THEN 0 ELSE j; bx _ Real.RoundC[s.vertex[polygons[i].vtxPtr[k]].sx]; by _ Real.RoundC[s.vertex[polygons[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; }; OutputPlygnLines: PUBLIC PROC[context: REF ThreeDScenes.Context, p: ThreeDPolygons.ShapePolygon] ~ { poly: REF ThreeDPolygons.PtrPoly _ NARROW[p.shape.surface, REF ThreeDPolygons.PtrPolySequence][p.polygon]; vertex: REF ThreeDScenes.VertexSequence _ p.shape.vertex; color: SampleSet _ ShapetoColorBytes[context, p.shape]; imagerCtx: Imager.Context; usingImager: BOOLEAN _ FALSE; IF poly.clipState = out THEN RETURN[]; -- reject if outside frame IF context.renderMode = $LF OR context.renderMode = $Dithered THEN { usingImager _ TRUE; imagerCtx _ ThreeDMisc.GetImagerContext[context]; }; IF poly.clipState = in THEN { ax, ay, bx, by: NAT; FOR j: NAT IN [0..poly.nVtces] DO i: NAT _ IF j = poly.nVtces THEN 0 ELSE j; bx _ Real.RoundC[vertex[poly.vtxPtr[i]].sx]; by _ Real.RoundC[vertex[poly.vtxPtr[i]].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; } ELSE IF poly.clipState = clipped THEN { IF (tempPoly = NIL) OR (tempPoly.length < 2 * poly.nVtces) THEN { tempPoly _ NEW[Polygon[2 * poly.nVtces] ]; tempPoly2 _ NEW[Polygon[2 * poly.nVtces] ]; }; tempPoly.nVtces _ poly.nVtces; FOR i: NAT IN [0..poly.nVtces) DO tempPoly.vtx[i].coord _ p.shape.vertex[poly.vtxPtr[i]]; ENDLOOP; [tempPoly, tempPoly2] _ ClipPoly[ context, p.shape, tempPoly, tempPoly2 ]; IF tempPoly.nVtces > 2 THEN { ax, ay, bx, by: NAT; FOR j: NAT IN [0..tempPoly.nVtces] DO i: NAT _ IF j = tempPoly.nVtces THEN 0 ELSE j; x, y, z: REAL; [[x, y, z]] _ ThreeDScenes.XfmPtToDisplay[ context, [tempPoly.vtx[i].coord.ex, tempPoly.vtx[i].coord.ey, tempPoly.vtx[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], color ] ELSE Imager.MaskVectorI[ imagerCtx, ax, ay, bx, by ]; ax _ bx; ay _ by; ENDLOOP; }; }; IF stopMe THEN HoldEverything[]; -- shut down if stop signal received }; OutputPoly: PUBLIC PROC[context: REF ThreeDScenes.Context, p: ThreeDPolygons.ShapePolygon] ~ { poly: REF ThreeDPolygons.PtrPoly _ NARROW[p.shape.surface, REF ThreeDPolygons.PtrPolySequence][p.polygon]; vertex: REF ThreeDScenes.VertexSequence _ p.shape.vertex; shade: REF ThreeDScenes.ShadingSequence _ p.shape.shade; shading: ATOM _ NARROW[ ThreeDScenes.GetShading[ p.shape, $Type ] ]; faceted: BOOLEAN _ shading = $Faceted; IF poly.clipState = out THEN RETURN[]; -- reject if outside frame IF (tempPoly = NIL) OR (tempPoly.length < 2 * poly.nVtces) THEN { tempPoly _ NEW[Polygon[2 * poly.nVtces] ]; tempPoly2 _ NEW[Polygon[2 * poly.nVtces] ]; }; tempPoly.nVtces _ poly.nVtces; FOR i: NAT IN [0..poly.nVtces) DO j: NAT _ poly.vtxPtr[i]; k: NAT _ IF faceted THEN p.polygon ELSE j; -- shades go with polygons when faceted tempPoly.vtx[i].coord _ vertex[j]; tempPoly.vtx[i].shade _ shade[k]; ENDLOOP; IF BackFacing[tempPoly] THEN IF p.shape.type = $Polyhedron -- backfacing poly!! THEN RETURN[] -- Reject if closed surface ELSE { FOR i: NAT IN [0..tempPoly.nVtces) DO -- recalculate shading for back side r, g, b, t: REAL; tempPoly.vtx[i].shade.exn _ -tempPoly.vtx[i].shade.exn; -- reverse normals tempPoly.vtx[i].shade.eyn _ -tempPoly.vtx[i].shade.eyn; tempPoly.vtx[i].shade.ezn _ -tempPoly.vtx[i].shade.ezn; [[r,g,b], t] _ ThreeDScenes.ShadePt[ context, tempPoly.vtx[i], 0.0 ]; tempPoly.vtx[i].shade.ir _ Real.FixC[r * 255.0]; tempPoly.vtx[i].shade.ig _ Real.FixC[g * 255.0]; tempPoly.vtx[i].shade.ib _ Real.FixC[b * 255.0]; tempPoly.vtx[i].shade.it _ Real.FixC[ t * 255.0]; ENDLOOP; FOR i: NAT IN [0..tempPoly.nVtces/2) DO -- reorder to keep clockwise on display tempVtx: ThreeDScenes.VertexInfo _ tempPoly.vtx[i]; tempPoly.vtx[i] _ tempPoly.vtx[tempPoly.nVtces-1 - i]; tempPoly.vtx[tempPoly.nVtces-1 - i] _ tempVtx; ENDLOOP; }; IF poly.clipState = clipped THEN { [tempPoly, tempPoly2] _ ClipPoly[context, p.shape, tempPoly, tempPoly2]; FOR i: NAT IN [0..tempPoly.nVtces) DO OPEN tempPoly.vtx[i].coord; [[sx, sy, sz]] _ ThreeDScenes.XfmPtToDisplay[context, [ex, ey, ez]]; ENDLOOP; }; IF tempPoly.nVtces > 2 THEN { tempPoly.props _ p.shape.shadingProps; -- transmit shading info Tilers.PolygonTiler[context, shading, tempPoly]; }; IF stopMe THEN HoldEverything[]; -- shut down if stop signal received }; END.