<> <> <> <> DIRECTORY Atom, Basics, G3dBasic, G3dMatrix, G3dRender, G3dScanConvert, G3dClipXfmShade, G3dShape, G3dVector; PolySilhouetteProcs: CEDAR MONITOR IMPORTS Atom, Basics, G3dMatrix, G3dRender, G3dClipXfmShade, G3dVector = BEGIN <> Ray: TYPE ~ G3dBasic.Ray; NatSequence: TYPE ~ G3dRender.NatSequence; NatSequenceRep: TYPE ~ G3dBasic.NatSequenceRep; Pair: TYPE ~ G3dRender.Pair; Triple: TYPE ~ G3dRender.Triple; TripleSequence: TYPE ~ G3dRender.TripleSequence; TripleSequenceRep: TYPE ~ G3dBasic.TripleSequenceRep; RGB: TYPE ~ G3dRender.RGB; RealSequence: TYPE ~ G3dRender.RealSequence; RealSequenceRep: TYPE ~ G3dBasic.RealSequenceRep; Context: TYPE ~ G3dRender.Context; SixSides: TYPE ~ G3dRender.SixSides; Matrix: TYPE ~ G3dRender.Matrix; Shape: TYPE ~ G3dRender.Shape; Vertex: TYPE ~ G3dShape.Vertex; Patch: TYPE ~ G3dRender.Patch; PatchSequence: TYPE ~ G3dRender.PatchSequence; PatchSequenceRep: TYPE ~ G3dRender.PatchSequenceRep; FaceRep: TYPE ~ G3dShape.FaceRep; FaceSequenceRep: TYPE ~ G3dShape.FaceSequenceRep; PatchProc: TYPE ~ G3dRender.PatchProc; CtlPoint: TYPE ~ G3dRender.CtlPoint; CtlPtInfo: TYPE ~ G3dRender.CtlPtInfo; <> CtlPtInfoSequence: TYPE ~ G3dRender.CtlPtInfoSequence; CtlPtInfoProc: TYPE ~ G3dRender.CtlPtInfoProc; Shading: TYPE ~ G3dRender.Shading; ShadingClass: TYPE ~ G3dRender.ShadingClass; RenderStyle: TYPE ~ G3dRender.RenderStyle; RenderData: TYPE ~ G3dRender.RenderData; ShapeClass: TYPE ~ G3dRender.ShapeClass; ShapeProc: TYPE ~ G3dRender.ShapeProc; ClipState: TYPE ~ G3dRender.ClipState; OutCode: TYPE ~ G3dRender.OutCode; AllOut: OutCode ~ G3dRender.AllOut; NoneOut: OutCode ~ G3dRender.NoneOut; FacingDir: TYPE ~ G3dRender.FacingDir; LORA: TYPE = LIST OF REF ANY; MidPtProc: TYPE ~ PROC[v0, v1: REF CtlPtInfo] RETURNS[REF CtlPtInfo]; nullTriple: Triple ~ [0.0, 0.0, 0.0]; Corner: TYPE ~ RECORD [ inVtx, outVtx: NAT _ 0, inDir, outDir, normal, interiorKnot: Triple _ nullTriple, concave: BOOLEAN _ FALSE ]; <> CornerSeq: TYPE ~ RECORD [ length: NAT _ 0, s: SEQUENCE maxLength: NAT OF Corner ]; CornerSeqSeq: TYPE ~ RECORD [ length: NAT _ 0, s: SEQUENCE maxLength: NAT OF REF CornerSeq ]; TangentSet: TYPE ~ RECORD [t0, et0, t1, et1: Triple _ nullTriple]; <> TangentSeq: TYPE ~ RECORD [length: NAT _ 0, s: SEQUENCE maxLength: NAT OF TangentSet]; TangentTriple: TYPE ~ ARRAY [0..3) OF TangentSet; TangentQuad: TYPE ~ ARRAY [0..4) OF TangentSet; TangentSeqSeq: TYPE ~ RECORD [ length: NAT _ 0, s: SEQUENCE maxLength: NAT OF REF TangentSeq]; Triangle: TYPE ~ RECORD [ v: ARRAY[0..3) OF CtlPtInfo, t: ARRAY[0..3) OF TangentSet ]; NatSequenceSequence: TYPE ~ RECORD [ length: NAT _ 0, s: SEQUENCE maxLength: NAT OF NatSequence ]; BoolSequence: TYPE ~ RECORD [length: NAT _ 0, s: SEQUENCE maxLength: NAT OF BOOLEAN]; <> Add: PROC[v1, v2: Triple] RETURNS[Triple] ~ G3dVector.Add; Mul: PROC[v: Triple, s: REAL] RETURNS[Triple] ~ G3dVector.Mul; Cross: PROC[v1, v2: Triple] RETURNS[Triple] ~ G3dVector.Cross; Div: PROC[v: Triple, s: REAL] RETURNS[Triple] ~ G3dVector.Div; Dot: PROC[v1, v2: Triple] RETURNS[REAL] ~ G3dVector.Dot; Length: PROC[v: Triple] RETURNS[REAL] ~ G3dVector.Length; Negate: PROC[v: Triple] RETURNS[Triple] ~ G3dVector.Negate; Nmlize: PROC[v: Triple] RETURNS[Triple] ~ G3dVector.Normalize; Sub3: PROC[v1, v2: Triple] RETURNS[Triple] ~ G3dVector.Sub; ReleasePatch: PROC [p: REF Patch] ~ G3dClipXfmShade.ReleasePatch; GetPatch: PROC [size: NAT] RETURNS [REF Patch] ~ G3dClipXfmShade.GetPatch; GetProp: PROC [propList: Atom.PropList, prop: REF ANY] RETURNS [REF ANY] ~ Atom.GetPropFromList; PutProp: PROC [propList: Atom.PropList, prop: REF ANY, val: REF ANY] RETURNS [Atom.PropList] ~ Atom.PutPropOnList; <> <<-- showLines: BOOLEAN; -- debug and pedagogical aid >> <> Sqr: PROCEDURE [number: REAL] RETURNS [REAL] ~ INLINE { RETURN[number * number]; }; <> DisplayNothing: PatchProc ~ { -- dummy routine for timing tests RETURN[ patch ]; }; DisplayPatchSilhouette: PatchProc ~ { <> shape: Shape _ NARROW[ GetProp[patch.props, $Shape] ]; patchNo: NAT _ NARROW[ GetProp[patch.props, $PatchNo], REF NAT ]^; renderData: REF RenderData _ patch.renderData; RETURN[patch]; }; <> InitClasses: PROC[] ~ { -- register procedures for basic surface types standardClass: ShapeClass _ G3dRender.GetShapeClass[$ConvexPolygon]; standardClass.type _ $PolygonWithSilhouette; standardClass.validate _ ValidatePolyWSilhouette; standardClass.displayPatch _ DisplayPatchSilhouette; G3dRender.RegisterShapeClass[standardClass, $PolygonWithSilhouette]; }; <> ValidatePolyWSilhouette: ShapeProc ~ { <> <> <> ValidateScreenCoords: PROC[] ~ { FOR i: NAT IN [0..shape.vertices.length) DO eVtx: Triple _ G3dMatrix.Transform[ shape.vertices[i].point, xfm]; eVtx _ G3dClipXfmShade.XfmPtToDisplay[ context, eVtx ]; shape.vertices[i].screen.x _ eVtx.x; shape.vertices[i].screen.y _ eVtx.y; ENDLOOP; shape.screenValid _ TRUE; }; render: REF RenderData _ G3dRender.RenderDataFrom[shape]; shapeClass: REF ShapeClass _ render.class; shadingClass: REF ShadingClass _ render.shadingClass; renderStyle: RenderStyle; xfm: Matrix _ G3dMatrix.Mul[shape.matrix, context.eyeSpaceXfm]; allPolysIn, allPolysOut: BOOLEAN _ TRUE; WITH shadingClass.renderMethod SELECT FROM style: REF RenderStyle => renderStyle _ style^; ENDCASE => renderStyle _ smooth; -- default to smooth to get normals and shading <> IF renderStyle # faceted THEN { -- get vertex normals IF NOT shape.vertices.valid.normal THEN G3dClipXfmShade.GetVtxNmls[context, shape]; } ELSE { -- get face normals IF shape.faces = NIL THEN { shape.faces _ NEW[FaceSequenceRep[shape.surfaces.length]]; shape.faces.length _ shape.surfaces.length; FOR i: NAT IN [0..shape.faces.length) DO shape.faces[i] _ NEW[FaceRep]; ENDLOOP; }; IF NOT shape.faces.valid.normal THEN G3dClipXfmShade.GetPolyNmls[context, shape]; }; <> IF GetProp[render.props, $Hidden] # NIL THEN RETURN[shape]; -- don't bother, not visible IF NOT render.patchesValid OR context.changed OR render.patch = NIL THEN { IF render.patch = NIL THEN { <> render.patch _ NEW[PatchSequenceRep[shape.surfaces.length]]; render.patch.length _ shape.surfaces.length; FOR i: NAT IN [0..render.patch.length) DO render.patch[i] _ NEW[Patch[shape.surfaces[i].length]]; FOR j: NAT IN [0..shape.surfaces[i].length) DO vtx: NAT _ shape.surfaces[i][j]; render.patch[i][j].coord.x _ shape.vertices[vtx].point.x; render.patch[i][j].coord.y _ shape.vertices[vtx].point.y; render.patch[i][j].coord.z _ shape.vertices[vtx].point.z; render.patch[i][j].shade.xn _ shape.vertices[vtx].normal.x; render.patch[i][j].shade.yn _ shape.vertices[vtx].normal.y; render.patch[i][j].shade.zn _ shape.vertices[vtx].normal.z; render.patch[i][j].shade.txtrX _ shape.vertices[vtx].texture.x; render.patch[i][j].shade.txtrY _ shape.vertices[vtx].texture.y; render.patch[i][j].vtxPtr _ vtx; ENDLOOP; render.patch[i].nVtces _ shape.surfaces[i].length; render.patch[i].type _ shape.type; render.patch[i].oneSided _ NOT shape.showBackfaces; render.patch[i].renderData _ render; -- store REF to shading information <> render.patch[i].props _ PutProp[render.patch[i].props, $Shape, shape]; render.patch[i].props _ PutProp[render.patch[i].props, $PatchNo, NEW[NAT _ i]]; ENDLOOP; }; <> <> IF renderStyle = lines AND shape.clipState = in AND render.class.display # NIL THEN { ValidateScreenCoords[]; RETURN [ shape ]; }; shape.screenExtent _ [[32768, 32768], [0, 0]]; -- initialize for computing screenExtent FOR i: NAT IN [0..render.patch.length) DO <> andOfCodes: OutCode _ AllOut; -- test for trivially out orOfCodes: OutCode _ NoneOut; -- test for trivially in FOR j: NAT IN [0..render.patch[i].nVtces) DO OPEN render.patch[i][j].coord; -- transform control points to eyespace [ [ex, ey, ez] ] _ G3dMatrix.Transform[ [x, y, z] , xfm]; IF shape.clipState = clipped THEN { clip _ G3dClipXfmShade.GetClipCodeForPt[ context, [ex, ey, ez] ]; orOfCodes _ LOOPHOLE[ Basics.BITOR[LOOPHOLE[orOfCodes], LOOPHOLE[ clip] ], OutCode]; andOfCodes _ LOOPHOLE[ Basics.BITAND[ LOOPHOLE[andOfCodes], LOOPHOLE[ clip] ], OutCode]; } ELSE clip _ NoneOut; <> IF renderStyle = smooth OR renderStyle = shadedLines THEN { OPEN render.patch[i][j].shade; tmpShininess: REAL; vtx: NAT _ render.patch[i][j].vtxPtr; [xn, yn, zn] _ shape.vertices[vtx].normal; [r, g, b] _ shape.vertices[vtx].color; t _ shape.vertices[vtx].transmittance; IF shape.faces # NIL THEN IF shape.faces.valid.color THEN { -- scale by face color clr: Triple _ shape.faces[i].color; r _ clr.x * r; g _ clr.y * g; b _ clr.z * b; }; [[exn, eyn, ezn]] _ G3dMatrix.TransformVec[ [xn, yn, zn] , xfm]; <> tmpShininess _ shadingClass.shininess; shadingClass.shininess _ 0.0; render.patch[i][j] _ shadingClass.shadeVtx[ context, render.patch[i][j], shadingClass ]; shadingClass.shininess _ tmpShininess; }; <> IF renderStyle = lines THEN { OPEN render.patch[i][j].shade; [er, eg, eb] _ shadingClass.color; }; ENDLOOP; <> IF orOfCodes = NoneOut THEN render.patch[i].clipState _ in -- default case ELSE IF andOfCodes # NoneOut THEN render.patch[i].clipState _ out ELSE render.patch[i].clipState _ clipped; IF shape.clipState = clipped THEN { allPolysIn _ allPolysIn AND (render.patch[i].clipState = in); allPolysOut _ allPolysOut AND (render.patch[i].clipState = out); }; <> IF renderStyle = faceted THEN { OPEN render.patch[i][0].shade; [xn, yn, zn] _ shape.faces[i].normal; [r, g, b] _ shape.faces[i].color; t _ shape.faces[i].transmittance; [[exn, eyn, ezn]] _ G3dMatrix.TransformVec[ shape.faces[i].normal, xfm]; render.patch[i][0] _ shadingClass.shadeVtx[context, render.patch[i][0], shadingClass]; FOR j: NAT IN [1..render.patch[i].nVtces) DO render.patch[i][j].shade.exn _ render.patch[i][0].shade.exn; render.patch[i][j].shade.eyn _ render.patch[i][0].shade.eyn; render.patch[i][j].shade.ezn _ render.patch[i][0].shade.ezn; render.patch[i][j].shade.er _ render.patch[i][0].shade.er; render.patch[i][j].shade.eg _ render.patch[i][0].shade.eg; render.patch[i][j].shade.eb _ render.patch[i][0].shade.eb; ENDLOOP; }; <> IF render.patch[i].clipState # out THEN FOR j: NAT IN [0..render.patch[i].nVtces) DO OPEN render.patch[i][j].coord; -- xfm to display, update shape screen extent [ [sx, sy, sz] ] _ G3dClipXfmShade.XfmPtToDisplay[ context, [ex, ey, ez], shape ]; ENDLOOP; ENDLOOP; <> IF shape.clipState = clipped THEN -- refine shape clipstate based on polygon states IF allPolysIn THEN shape.clipState _ in ELSE IF allPolysOut THEN shape.clipState _ out; render.patchesValid _ TRUE; IF shape.clipState = in AND renderStyle = lines AND render.class.display # NIL THEN ValidateScreenCoords[]; -- prepare for unclipped line drawings }; RETURN[shape]; }; InitClasses[]; END.