<> <> <> DIRECTORY Atom USING [ GetPropFromList ], Basics USING [ BITOR, BITAND ], Real USING [ FixC, Float ], RealFns USING [ SqRt ], Vector3d USING [ Normalize, Cross, Dot, Null ], ScanConvert USING [ justNoticeable ], ThreeDBasics USING [ ClipState, Context, NatSequence, NoneOut, OutCode, ShadingSequence, ShadingValue, ShapeInstance, SixSides, Triple, Vertex, VertexInfo, VertexInfoProc, VertexInfoSequence, VertexSequence ], ThreeDScenes USING [ GetClipCodeForPt, XfmPtToDisplay ], ThreeDSurfaces USING [ GetPatch, GetPatchClipState, Patch, PtrPatchSequence, PtrPatch, PatchSequence, PatchExpandProc, PatchDisplayProc, ReleasePatch, ShadePoly ], StandardPatches; StandardPatchesImpl: CEDAR MONITOR IMPORTS Atom, Basics, Real, RealFns, Vector3d, ThreeDScenes, ThreeDSurfaces EXPORTS StandardPatches = BEGIN <> StandardPatchesError: PUBLIC SIGNAL [reason: ATOM] = CODE; Context: TYPE ~ ThreeDBasics.Context; SixSides: TYPE ~ ThreeDBasics.SixSides; Triple: TYPE ~ ThreeDBasics.Triple; NatSequence: TYPE ~ ThreeDBasics.NatSequence; ShapeInstance: TYPE ~ ThreeDBasics.ShapeInstance; Patch: TYPE ~ ThreeDSurfaces.Patch; PatchSequence: TYPE ~ ThreeDSurfaces.PatchSequence; PtrPatch: TYPE ~ ThreeDSurfaces.PtrPatch; PtrPatchSequence: TYPE ~ ThreeDSurfaces.PtrPatchSequence; Vertex: TYPE ~ ThreeDBasics.Vertex; VertexInfo: TYPE ~ ThreeDBasics.VertexInfo; VertexInfoSequence: TYPE ~ ThreeDBasics.VertexInfoSequence; MidPtProc: TYPE ~ PROC[v0, v1: REF VertexInfo] RETURNS[REF VertexInfo]; vertexStore: REF VertexInfoSequence _ NEW[ VertexInfoSequence[96] ]; -- Vertex Cache vertexStoreLength: NAT _ 32; vertexStorePtr: NAT _ 0; -- place to return next free record <> Sqr: PROCEDURE [number: REAL] RETURNS [REAL] ~ INLINE { RETURN[number * number]; }; GetVertexInfo: ENTRY PROC[] RETURNS[REF VertexInfo] ~ { ENABLE UNWIND => NULL; vtx: REF VertexInfo; IF vertexStorePtr = 0 THEN vtx _ NEW[VertexInfo] ELSE { vertexStorePtr _ vertexStorePtr - 1; vtx _ vertexStore[vertexStorePtr]; vertexStore[vertexStorePtr] _ NIL; }; RETURN[ vtx ]; }; ReleaseVertexInfo: ENTRY PROC[vtx: REF VertexInfo] ~ { ENABLE UNWIND => NULL; IF vertexStorePtr = vertexStoreLength THEN { vertexStore _ NEW[ VertexInfoSequence[vertexStoreLength + 32] ]; vertexStoreLength _ vertexStoreLength + 32; vertexStorePtr _ 0; }; vertexStore[vertexStorePtr] _ vtx; vertexStorePtr _ vertexStorePtr + 1; }; DiffPosns: PROC[vtx1, vtx2: Vertex, space: ATOM _ NIL] RETURNS[Triple] ~ { SELECT space FROM $Eye => RETURN[[vtx1.ex - vtx2.ex, vtx1.ey - vtx2.ey, vtx1.ez - vtx2.ez]]; $Screen => RETURN[[vtx1.sx - vtx2.sx, vtx1.sy - vtx2.sy, vtx1.sz - vtx2.sz]]; ENDCASE => RETURN[[vtx1.x - vtx2.x, vtx1.y - vtx2.y, vtx1.z - vtx2.z]]; -- object space }; VtxShapeMidPt: MidPtProc ~ { v: REF VertexInfo _ GetVertexInfo[]; v.coord.x _ (v0.coord.x + v1.coord.x) / 2; v.coord.y _ (v0.coord.y + v1.coord.y) / 2; v.coord.z _ (v0.coord.z + v1.coord.z) / 2; v.shade.xn _ (v0.shade.xn + v1.shade.xn) / 2; v.shade.yn _ (v0.shade.yn + v1.shade.yn) / 2; v.shade.zn _ (v0.shade.zn + v1.shade.zn) / 2; v.shade.r _ (v0.shade.r + v1.shade.r) / 2; v.shade.g _ (v0.shade.g + v1.shade.g) / 2; v.shade.b _ (v0.shade.b + v1.shade.b) / 2; v.shade.t _ (v0.shade.t + v1.shade.t) / 2; IF v0.aux # NIL AND v1.aux # NIL THEN { -- get auxiliary info from supplied proc ref: REF ANY _ Atom.GetPropFromList[v0.props, $ShadeVtx]; IF ref # NIL THEN { -- call custom interpolator lerpProc: REF ThreeDBasics.VertexInfoProc _ NARROW[ref]; data: LIST OF REF ANY _ LIST[ v0.aux, v1.aux, NEW[REAL _ .5], NEW[REAL _ .5] ]; [] _ lerpProc^[ NIL, v, data]; } ELSE v.aux _ v0.aux; }; RETURN[v]; }; VtxDisplayMidPt: MidPtProc ~ { v: REF VertexInfo _ GetVertexInfo[]; v.coord.x _ (v0.coord.x + v1.coord.x) / 2; v.coord.y _ (v0.coord.y + v1.coord.y) / 2; v.coord.z _ (v0.coord.z + v1.coord.z) / 2; v.coord.ex _ (v0.coord.ex + v1.coord.ex) / 2; v.coord.ey _ (v0.coord.ey + v1.coord.ey) / 2; v.coord.ez _ (v0.coord.ez + v1.coord.ez) / 2; v.coord.sx _ (v0.coord.sx + v1.coord.sx) / 2; v.coord.sy _ (v0.coord.sy + v1.coord.sy) / 2; v.coord.sz _ (v0.coord.sz + v1.coord.sz) / 2; v.shade.exn _ (v0.shade.exn + v1.shade.exn) / 2; v.shade.eyn _ (v0.shade.eyn + v1.shade.eyn) / 2; v.shade.ezn _ (v0.shade.ezn + v1.shade.ezn) / 2; v.shade.r _ (v0.shade.r + v1.shade.r) / 2; v.shade.g _ (v0.shade.g + v1.shade.g) / 2; v.shade.b _ (v0.shade.b + v1.shade.b) / 2; v.shade.t _ (v0.shade.t + v1.shade.t) / 2; v.shade.er _ (v0.shade.er + v1.shade.er) / 2; v.shade.eg _ (v0.shade.eg + v1.shade.eg) / 2; v.shade.eb _ (v0.shade.eb + v1.shade.eb) / 2; v.shade.et _ (v0.shade.et + v1.shade.et) / 2; IF v0.aux # NIL AND v1.aux # NIL THEN { -- get auxiliary info from supplied proc ref: REF ANY _ Atom.GetPropFromList[v0.props, $ShadeVtx]; IF ref # NIL THEN { -- call custom interpolator lerpProc: REF ThreeDBasics.VertexInfoProc _ NARROW[ref]; data: LIST OF REF ANY _ LIST[ v0.aux, v1.aux, NEW[REAL _ .5], NEW[REAL _ .5] ]; [] _ lerpProc^[ NIL, v, data]; } ELSE v.aux _ v0.aux; }; RETURN[v]; }; ApplyCubicBasis: PROC[ v0, v1, v2, v3: REF VertexInfo, f0, f1, f2, f3: REAL] RETURNS[vOut: REF VertexInfo] ~ { vOut _ GetVertexInfo[]; vOut.coord.x _ f0*v0.coord.x + f1*v1.coord.x + f2*v2.coord.x + f3*v3.coord.x; vOut.coord.y _ f0*v0.coord.y + f1*v1.coord.y + f2*v2.coord.y + f3*v3.coord.y; vOut.coord.z _ f0*v0.coord.z + f1*v1.coord.z + f2*v2.coord.z + f3*v3.coord.z; vOut.shade.xn _ f0*v0.shade.xn + f1*v1.shade.xn + f2*v2.shade.xn + f3*v3.shade.xn; vOut.shade.yn _ f0*v0.shade.yn + f1*v1.shade.yn + f2*v2.shade.yn + f3*v3.shade.yn; vOut.shade.zn _ f0*v0.shade.zn + f1*v1.shade.zn + f2*v2.shade.zn + f3*v3.shade.zn; vOut.shade.r _ f0*v0.shade.r + f1*v1.shade.r + f2*v2.shade.r + f3*v3.shade.r; vOut.shade.g _ f0*v0.shade.g + f1*v1.shade.g + f2*v2.shade.g + f3*v3.shade.g; vOut.shade.b _ f0*v0.shade.b + f1*v1.shade.b + f2*v2.shade.b + f3*v3.shade.b; vOut.shade.t _ f0*v0.shade.t + f1*v1.shade.t + f2*v2.shade.t + f3*v3.shade.t; IF v0.aux # NIL THEN { -- get auxiliary info from supplied proc ref: REF ANY _ Atom.GetPropFromList[v0.props, $ShadeVtx]; IF ref # NIL THEN { -- call custom interpolator 4 times to sum basis fns lerpProc: REF ThreeDBasics.VertexInfoProc _ NARROW[ref]; data: LIST OF REF ANY _ LIST[v0.aux, vOut.aux, NEW[REAL _ f0], NEW[REAL _ 0.0]]; [] _ lerpProc^[ NIL, vOut, data]; data _ LIST[ v1.aux, vOut.aux, NEW[REAL _ f1], NEW[REAL _ 1.0]]; [] _ lerpProc^[ NIL, vOut, data]; data _ LIST[ v2.aux, vOut.aux, NEW[REAL _ f2], NEW[REAL _ 1.0]]; [] _ lerpProc^[ NIL, vOut, data]; data _ LIST[ v3.aux, vOut.aux, NEW[REAL _ f3], NEW[REAL _ 1.0]]; [] _ lerpProc^[ NIL, vOut, data]; } ELSE vOut.aux _ v0.aux; }; }; LerpToVtx: PROC[ v0, v1, vMid: VertexInfo] RETURNS[VertexInfo] ~ { <> a, b, alpha: REAL; v: VertexInfo; <> mag: REAL _ RealFns.SqRt[ Sqr[v1.coord.sx - v0.coord.sx] + Sqr[v1.coord.sy - v0.coord.sy] ]; IF mag < ScanConvert.justNoticeable THEN RETURN[v0]; -- effectively invisible detail alpha _ ( (vMid.coord.sx - v0.coord.sx) * (v1.coord.sx - v0.coord.sx) -- vMid-v0 dot v1-v0 + (vMid.coord.sy - v0.coord.sy) * (v1.coord.sy - v0.coord.sy) ) / Sqr[mag]; a _ 1.0 - alpha; b _ alpha; v.coord.x _ a * v0.coord.x + b * v1.coord.x ; v.coord.y _ a * v0.coord.y + b * v1.coord.y ; v.coord.z _ a * v0.coord.z + b * v1.coord.z ; v.coord.ex _ a * v0.coord.ex + b * v1.coord.ex ; v.coord.ey _ a * v0.coord.ey + b * v1.coord.ey ; v.coord.ez _ a * v0.coord.ez + b * v1.coord.ez ; v.coord.sx _ a * v0.coord.sx + b * v1.coord.sx ; v.coord.sy _ a * v0.coord.sy + b * v1.coord.sy ; v.coord.sz _ a * v0.coord.sz + b * v1.coord.sz ; v.shade.exn _ a * v0.shade.exn + b * v1.shade.exn ; v.shade.eyn _ a * v0.shade.eyn + b * v1.shade.eyn ; v.shade.ezn _ a * v0.shade.ezn + b * v1.shade.ezn ; v.shade.r _ a * v0.shade.r + b * v1.shade.r ; v.shade.g _ a * v0.shade.g + b * v1.shade.g ; v.shade.b _ a * v0.shade.b + b * v1.shade.b ; v.shade.t _ a * v0.shade.t + b * v1.shade.t ; v.shade.er _ a * v0.shade.er + b * v1.shade.er ; v.shade.eg _ a * v0.shade.eg + b * v1.shade.eg ; v.shade.eb _ a * v0.shade.eb + b * v1.shade.eb ; v.shade.et _ a * v0.shade.et + b * v1.shade.et ; IF v0.aux # NIL AND v1.aux # NIL THEN { -- get auxiliary info from supplied proc ref: REF ANY _ Atom.GetPropFromList[v0.props, $ShadeVtx]; IF ref # NIL THEN { -- call custom interpolator lerpProc: REF ThreeDBasics.VertexInfoProc _ NARROW[ref]; data: LIST OF REF ANY _ LIST[ v0.aux, v1.aux, NEW[REAL _ a], NEW[REAL _ b] ]; v _ lerpProc^[ NIL, NEW[VertexInfo _ v], data]^; } ELSE v.aux _ v0.aux; }; RETURN[v]; }; StraightWithin: PROC[ p: REF Patch, v0, v1, v2, v3: NAT, tol: REAL] RETURNS[BOOLEAN] ~ { dist, a, b, mag: REAL; IF p[v3].coord.sx < p[v0].coord.sx THEN { -- enforce consistent evaluation t: NAT _ v3; v3 _ v0; v0 _ t; t _ v2; v2 _ v1; v1 _ t; }; a _ p[v3].coord.sy - p[v0].coord.sy; b _ p[v3].coord.sx - p[v0].coord.sx; mag _ RealFns.SqRt[a*a + b*b]; IF mag > tol / 1000.0 THEN { a _ a/mag; b _ b/mag; dist _ a * (p[v1].coord.sx - p[v0].coord.sx) + b * (p[v0].coord.sy - p[v1].coord.sy); IF ABS[dist] > tol THEN RETURN[FALSE]; dist _ a * (p[v2].coord.sx - p[v0].coord.sx) + b * (p[v0].coord.sy - p[v2].coord.sy); IF ABS[dist] > tol THEN RETURN[FALSE]; }; <> p[v1] _ LerpToVtx[ p[v0], p[v3], p[v1] ]; p[v2] _ LerpToVtx[ p[v0], p[v3], p[v2] ]; RETURN[TRUE]; }; PatchSort: PROC[ context: REF Context, p0, p1, p2, p3: REF Patch] RETURNS[REF Patch, REF Patch, REF Patch, REF Patch] ~ { z: ARRAY[0..4) OF REAL; pOut: ARRAY[0..4) OF REF Patch; <> z[0] _ (p0[0].coord.ez + p0[12].coord.ez + p0[15].coord.ez + p0[3].coord.ez)/4; pOut[0] _ p0; z[1] _ (p1[0].coord.ez + p1[12].coord.ez + p1[15].coord.ez + p1[3].coord.ez)/4; pOut[1] _ p1; z[2] _ (p2[0].coord.ez + p2[12].coord.ez + p2[15].coord.ez + p2[3].coord.ez)/4; pOut[2] _ p2; z[3] _ (p3[0].coord.ez + p3[12].coord.ez + p3[15].coord.ez + p3[3].coord.ez)/4; pOut[3] _ p3; FOR i: NAT IN [1..4) DO FOR j: NAT DECREASING IN [0..i) DO -- sort to increasing depth order IF z[j+1] < z[j] THEN { t: REAL _ z[j+1]; p: REF Patch _ pOut[j+1]; z[j+1] _ z[j]; pOut[j+1] _ pOut[j]; z[j] _ t; pOut[j] _ p; }; ENDLOOP; ENDLOOP; IF context.alphaBuffer THEN RETURN[ pOut[0], pOut[1], pOut[2], pOut[3] ] ELSE RETURN[ pOut[3], pOut[2], pOut[1], pOut[0] ]; }; <> BezierExpand: PUBLIC ThreeDSurfaces.PatchExpandProc ~ { <> nPerSide: NAT _ Real.FixC[limit]; IF limitType # NIL THEN SIGNAL StandardPatchesError[$Unimplemented]; [v, p] _ BezierParametricExpand[shape, patch, nPerSide]; RETURN[ v, p ]; }; BezierSubdivide: PUBLIC ThreeDSurfaces.PatchExpandProc ~ { <> IF limitType # NIL THEN SIGNAL StandardPatchesError[$Unimplemented]; [v, p] _ BezierSubdivideExpand[shape, patch]; RETURN[ v, p ]; }; minLimit: REAL _ .25; -- subdivision precision limit recurseLimit: NAT _ 14; -- safety valve BezierDisplay: PUBLIC ThreeDSurfaces.PatchDisplayProc ~ { <> Display: PROC[ p: REF Patch, level: NAT] ~ { allStraight: BOOLEAN _ TRUE; <> IF (p.clipState = out) OR BezierBackFacing[p] THEN RETURN[]; <> IF NOT StraightWithin[p, 0, 1, 2, 3, limit] THEN allStraight _ FALSE; IF NOT StraightWithin[p, 0, 4, 8, 12, limit] THEN allStraight _ FALSE; IF NOT StraightWithin[p, 3, 7, 11,15, limit] THEN allStraight _ FALSE; IF NOT StraightWithin[p, 12,13,14,15, limit] THEN allStraight _ FALSE; IF allStraight OR level >= recurseLimit THEN { -- outer edges straight, display as polygon p.nVtces _ 4; p[1] _ p[12]; p[2] _ p[15]; -- copy corners (p[0], p[3] stay put) p.type _ outputType; -- type of output patch ThreeDSurfaces.ShadePoly[context, p]; action[context, p]; -- display } ELSE { -- not straight enough, subdivide and recurse p0, p1, p2, p3: REF Patch; [p0, p1, p2, p3] _ BezierPatchDivide[ context, p, VtxDisplayMidPt ]; -- subdivide IF NOT context.depthBuffer THEN [p0, p1, p2, p3] _ PatchSort[ context, p0, p1, p2, p3]; -- sort to display order Display[p0, level+1]; Display[p1, level+1]; Display[p2, level+1]; Display[p3, level+1]; -- display in order }; }; <> outputType: ATOM _ NARROW[ Atom.GetPropFromList[ patch.props, $InterimPatchType ] ]; IF outputType = NIL THEN outputType _ $ConvexPolygon; IF limitType # NIL AND limitType # $StraightEdges THEN SIGNAL StandardPatchesError[$Unimplemented]; limit _ MAX[limit, minLimit]; -- straight to precision limit is sufficient (it says here) BezierPatchNormals[patch]; -- Get Normals at control points Display[patch, 0]; -- Divide till all outside patch edges are straight or recursion exceeded ThreeDSurfaces.ReleasePatch[patch]; -- end of life for patch }; BezierDisplayLines: PUBLIC ThreeDSurfaces.PatchDisplayProc ~ { EvalCoords: PROC[ context: REF Context, patch: REF Patch, v0, v1, v2, v3: NAT, numPts: NAT _ 8 ] RETURNS [outP: REF Patch] ~ { clipState: ThreeDBasics.ClipState; IF patch.clipState = in -- get clipState of convex hull THEN clipState _ in ELSE { outCode: ThreeDBasics.OutCode _ LOOPHOLE[ Basics.BITOR[ Basics.BITOR[ LOOPHOLE[patch[v0].coord.clip], LOOPHOLE[patch[v1].coord.clip] ], Basics.BITOR[ LOOPHOLE[patch[v2].coord.clip], LOOPHOLE[patch[v3].coord.clip] ] ], ThreeDBasics.OutCode ]; IF outCode = ThreeDBasics.NoneOut THEN clipState _ in ELSE { outCode _ LOOPHOLE[ Basics.BITAND[ Basics.BITAND[ LOOPHOLE[patch[v0].coord.clip], LOOPHOLE[patch[v1].coord.clip] ], Basics.BITAND[ LOOPHOLE[patch[v2].coord.clip], LOOPHOLE[patch[v3].coord.clip] ] ], ThreeDBasics.OutCode ]; IF outCode # ThreeDBasics.NoneOut THEN clipState _ out ELSE clipState _ clipped; }; }; outP _ ThreeDSurfaces.GetPatch[numPts]; -- will be released by action proc FOR i: NAT IN [0..numPts) DO t: REAL _ Real.Float[i] / (numPts-1); t2: REAL _ t * t; t3: REAL _ t2 * t; f0: REAL _ -1.0*t3 + 3.0*t2 - 3.0*t + 1; f1: REAL _ 3.0*t3 - 6.0*t2 + 3.0*t; f2: REAL _ -3.0*t3 + 3.0*t2; f3: REAL _ 1.0*t3; IF clipState # in OR context.alphaBuffer THEN { outP[i].coord.ex _ f0*patch[v0].coord.ex + f1*patch[v1].coord.ex + f2*patch[v2].coord.ex + f3*patch[v3].coord.ex; outP[i].coord.ey _ f0*patch[v0].coord.ey + f1*patch[v1].coord.ey + f2*patch[v2].coord.ey + f3*patch[v3].coord.ey; outP[i].coord.ez _ f0*patch[v0].coord.ez + f1*patch[v1].coord.ez + f2*patch[v2].coord.ez + f3*patch[v3].coord.ez; IF clipState # in THEN outP[i].coord.clip _ ThreeDScenes.GetClipCodeForPt[ context, [outP[i].coord.ex, outP[i].coord.ey, outP[i].coord.ez] ]; IF context.alphaBuffer THEN { -- set up color for display outP[i].shade.er _ f0*patch[v0].shade.r + f1*patch[v1].shade.r + f2*patch[v2].shade.r + f3*patch[v3].shade.r; outP[i].shade.eg _ f0*patch[v0].shade.g + f1*patch[v1].shade.g + f2*patch[v2].shade.g + f3*patch[v3].shade.g; outP[i].shade.eb _ f0*patch[v0].shade.b + f1*patch[v1].shade.b + f2*patch[v2].shade.b + f3*patch[v3].shade.b; }; } ELSE { outP[i].coord.sx _ f0*patch[v0].coord.sx + f1*patch[v1].coord.sx + f2*patch[v2].coord.sx + f3*patch[v3].coord.sx; outP[i].coord.sy _ f0*patch[v0].coord.sy + f1*patch[v1].coord.sy + f2*patch[v2].coord.sy + f3*patch[v3].coord.sy; outP[i].coord.sz _ f0*patch[v0].coord.sz + f1*patch[v1].coord.sz + f2*patch[v2].coord.sz + f3*patch[v3].coord.sz; }; ENDLOOP; outP.type _ $Path; outP.clipState _ clipState; outP.nVtces _ numPts; }; path: REF Patch; path _ EvalCoords[ context, patch, 0, 1, 2, 3 ]; action[context, path ]; path _ EvalCoords[ context, patch, 12, 13, 14, 15 ]; action[context, path ]; path _ EvalCoords[ context, patch, 0, 4, 8, 12 ]; action[context, path ]; path _ EvalCoords[ context, patch, 3, 7, 11, 15 ]; action[context, path ]; ThreeDSurfaces.ReleasePatch[patch]; -- end of life for patch }; BezierDisplayCtrlPts: PUBLIC ThreeDSurfaces.PatchDisplayProc ~ { EvalCoords: PROC[ context: REF Context, patch: REF Patch, v0, v1, v2, v3: NAT, numPts: NAT _ 4 ] RETURNS [outP: REF Patch] ~ { clipState: ThreeDBasics.ClipState; IF patch.clipState = in -- get clipState of convex hull THEN clipState _ in ELSE { outCode: ThreeDBasics.OutCode _ LOOPHOLE[ Basics.BITOR[ Basics.BITOR[ LOOPHOLE[patch[v0].coord.clip], LOOPHOLE[patch[v1].coord.clip] ], Basics.BITOR[ LOOPHOLE[patch[v2].coord.clip], LOOPHOLE[patch[v3].coord.clip] ] ], ThreeDBasics.OutCode ]; IF outCode = ThreeDBasics.NoneOut THEN clipState _ in ELSE { outCode _ LOOPHOLE[ Basics.BITAND[ Basics.BITAND[ LOOPHOLE[patch[v0].coord.clip], LOOPHOLE[patch[v1].coord.clip] ], Basics.BITAND[ LOOPHOLE[patch[v2].coord.clip], LOOPHOLE[patch[v3].coord.clip] ] ], ThreeDBasics.OutCode ]; IF outCode # ThreeDBasics.NoneOut THEN clipState _ out ELSE clipState _ clipped; }; }; outP _ ThreeDSurfaces.GetPatch[4]; -- will be released by action proc outP[0] _ patch[v0]; outP[1] _ patch[v1]; outP[2] _ patch[v2]; outP[3] _ patch[v3]; outP.type _ $Path; outP.clipState _ clipState; outP.nVtces _ numPts; }; path: REF Patch; path _ EvalCoords[ context, patch, 0, 1, 2, 3 ]; action[context, path ]; path _ EvalCoords[ context, patch, 4, 5, 6, 7 ]; action[context, path ]; path _ EvalCoords[ context, patch, 8, 9, 10, 11 ]; action[context, path ]; path _ EvalCoords[ context, patch, 12, 13, 14, 15 ]; action[context, path ]; path _ EvalCoords[ context, patch, 0, 4, 8, 12 ]; action[context, path ]; path _ EvalCoords[ context, patch, 3, 7, 11, 15 ]; action[context, path ]; ThreeDSurfaces.ReleasePatch[patch]; -- end of life for patch }; BezierBackFacing: PROC[p: REF Patch] RETURNS [BOOLEAN] ~ { frontFacing: BOOLEAN _ FALSE; FOR i: NAT IN [0..16) DO -- this is not an adequate test!!!! cosAng: REAL _ Vector3d.Dot[ [p[i].coord.ex, p[i].coord.ey, p[i].coord.ez], [p[i].shade.exn, p[i].shade.eyn, p[i].shade.ezn] ]; IF cosAng <= 0.0 THEN frontFacing _ TRUE; ENDLOOP; IF NOT frontFacing THEN RETURN[ TRUE ] ELSE RETURN[ FALSE ]; }; BezierPatchDivide: PROC[context: REF Context, p: REF Patch, midPt: MidPtProc] RETURNS[REF Patch, REF Patch, REF Patch, REF Patch] ~ { <> outPatch: ARRAY [0..4) OF REF Patch; row: ARRAY [0..7) OF REF VertexInfoSequence; col: ARRAY [0..4) OF REF VertexInfoSequence; <> FOR i: NAT IN [0..4) DO p0, p1, p2, p3: REF VertexInfo; p0 _ GetVertexInfo[]; p0^ _ p[i]; p1 _ GetVertexInfo[]; p1^ _ p[i+4]; p2 _ GetVertexInfo[]; p2^ _ p[i+8]; p3 _ GetVertexInfo[]; p3^ _ p[i+12]; col[i] _ BezierCurveDivide[ p0, p1, p2, p3, midPt ]; ReleaseVertexInfo[p0]; ReleaseVertexInfo[p1]; ReleaseVertexInfo[p2]; ReleaseVertexInfo[p3]; ENDLOOP; FOR i: NAT IN [0..7) DO row[i] _ BezierCurveDivide[ col[0][i], col[1][i], col[2][i], col[3][i], midPt ]; ENDLOOP; FOR i: NAT IN [0..4) DO rowBase: NAT _ (i MOD 2) * 3; colBase: NAT _ (i / 2) * 3; outPatch[i] _ ThreeDSurfaces.GetPatch[16]; -- 16 point patch, released by display action outPatch[i].type _ p.type; outPatch[i].oneSided _ p.oneSided; outPatch[i].nVtces _ 16; outPatch[i].clipState _ p.clipState; outPatch[i].dir _ p.dir; outPatch[i].props _ p.props; FOR j: NAT IN [0..4) DO FOR k: NAT IN [0..4) DO outPatch[i][j*4 + k] _ row[colBase+j][rowBase+k]^; -- count along rows ENDLOOP; ENDLOOP; IF outPatch[i].clipState # in THEN { FOR j: NAT IN [0..16) DO OPEN outPatch[i][j].coord; clip _ ThreeDScenes.GetClipCodeForPt[ context, [ex, ey, ez] ]; [ [sx, sy, sz] ] _ ThreeDScenes.XfmPtToDisplay[ context, NIL, [ex, ey, ez] ]; ENDLOOP; ThreeDSurfaces.GetPatchClipState[ outPatch[i] ]; }; ENDLOOP; FOR i: NAT IN [0..7) DO FOR j: NAT IN [0..row[i].length) DO -- return borrowed storage ReleaseVertexInfo[ row[i][j] ]; ENDLOOP; ENDLOOP; FOR i: NAT IN [0..4) DO FOR j: NAT IN [0..col[i].length) DO ReleaseVertexInfo[ col[i][j] ]; ENDLOOP; ENDLOOP; RETURN[ outPatch[0], outPatch[1], outPatch[2], outPatch[3] ]; -- return four sub-patches }; BezierCurveDivide: PROC[v0, v1, v2, v3: REF VertexInfo, midPt: MidPtProc] RETURNS[pt: REF VertexInfoSequence] ~ { tempPt: REF VertexInfo; pt _ NEW[ VertexInfoSequence[7] ]; pt[0] _ GetVertexInfo[]; pt[0]^ _ v0^; pt[6] _ GetVertexInfo[]; pt[6]^ _ v3^; pt[1] _ midPt[v0, v1]; pt[5] _ midPt[v2, v3]; tempPt _ midPt[v1, v2]; pt[2] _ midPt[pt[1], tempPt]; pt[4] _ midPt[tempPt, pt[5]]; pt[3] _ midPt[pt[2], pt[4]]; ReleaseVertexInfo[ tempPt ]; }; BezierPatchNormals: PROC[ patch: REF Patch] ~ { <> d1, d2: Triple; GetXProd: PROC[v1b, v1e, v2b, v2e: NAT] RETURNS[Triple] ~ { d1 _ DiffPosns[ patch[v1e].coord, patch[v1b].coord, $Eye ]; d2 _ DiffPosns[ patch[v2e].coord, patch[v2b].coord, $Eye ]; IF Vector3d.Null[d1] OR Vector3d.Null[d2] THEN SIGNAL StandardPatchesError[$DegeneratePatch]; RETURN [ Vector3d.Normalize[ Vector3d.Cross[ d2, d1 ] ] ]; -- in eye space, left-handed }; FOR i: INTEGER IN [0..4) DO FOR j: INTEGER IN [0..4) DO k: NAT _ j + i*4; v1Beg: NAT _ 4*i + MAX[j-1, 0]; v1End: NAT _ 4*i + MIN[j+1, 3]; v2Beg: NAT _ 4*MAX[i-1, 0] + j; v2End: NAT _ 4*MIN[i+1, 3] + j; { [[ patch[k].shade.exn, patch[k].shade.eyn, patch[k].shade.ezn ]] _ GetXProd[ v1Beg, v1End, v2Beg, v2End ! ANY => GO TO FixUp ]; EXITS FixUp => { -- degenerate edges, use alternate edges IF NOT Vector3d.Null[d1] THEN { l: NAT _ IF j = 3 THEN 2 ELSE j+1; -- use parallel pair of control points v2Beg _ 4*MAX[i-1, 0] + l; v2End _ 4*MIN[i+1, 3] + l; } ELSE IF NOT Vector3d.Null[d2] THEN { l: NAT _ IF i = 3 THEN 2 ELSE i+1; -- use parallel pair of control points v1Beg _ 4*l + MAX[j-1, 0]; v1End _ 4*l + MIN[j+1, 3]; } ELSE SIGNAL StandardPatchesError[$DegeneratePatch]; -- failure [[ patch[k].shade.exn, patch[k].shade.eyn, patch[k].shade.ezn ]] _ GetXProd[ v1Beg, v1End, v2Beg, v2End ]; -- try again with alternate edges }; }; ENDLOOP; ENDLOOP; }; BezierPtrPatchNormals: PROC[ shape: REF ShapeInstance, patch: REF PtrPatch] ~ { <> GetXProd: PROC[vertex: REF ThreeDBasics.VertexSequence, v1b, v1e, v2b, v2e: NAT] RETURNS[Triple] ~ { RETURN [ Vector3d.Normalize[ Vector3d.Cross[ -- in object space so do right-handed DiffPosns[ vertex[v1e]^, vertex[v1b]^ ], DiffPosns[ vertex[v2e]^, vertex[v2b]^ ] ] ] ]; }; vertex: REF ThreeDBasics.VertexSequence _ shape.vertex; shade: REF ThreeDBasics.ShadingSequence _ shape.shade; FOR i: INTEGER IN [0..4) DO FOR j: INTEGER IN [0..4) DO k: NAT _ patch.vtxPtr[j + i*4]; IF shade[k].xn = 0.0 AND shade[k].yn = 0.0 AND shade[k].zn = 0.0 THEN { -- untouched v1Beg: NAT _ 4*i + MAX[j-1, 0]; v1End: NAT _ 4*i + MIN[j+1, 3]; v2Beg: NAT _ 4*MAX[i-1, 0] + j; v2End: NAT _ 4*MIN[i+1, 3] + j; IF patch.vtxPtr[v1Beg] = patch.vtxPtr[v1End] THEN { -- try to fix coalesced points v1Beg _ 4*MAX[i-1, 0] + MAX[j-1, 0]; v1End _ 4*MIN[i+1, 3] + MIN[j+1, 3]; IF patch.vtxPtr[v1Beg] = patch.vtxPtr[v1End] THEN SIGNAL StandardPatchesError[$DegeneratePatch]; }; IF patch.vtxPtr[v2Beg] = patch.vtxPtr[v2End] THEN { -- try to fix coalesced points v2Beg _ 4*MAX[i-1, 0] + MAX[j-1, 0]; v2End _ 4*MIN[i+1, 3] + MIN[j+1, 3]; IF patch.vtxPtr[v2Beg] = patch.vtxPtr[v2End] THEN SIGNAL StandardPatchesError[$DegeneratePatch]; }; [[ shade[k].xn, shade[k].yn, shade[k].zn ]] _ GetXProd[ vertex, patch.vtxPtr[v1Beg], patch.vtxPtr[v1End], patch.vtxPtr[v2Beg], patch.vtxPtr[v2End] ]; }; ENDLOOP; ENDLOOP; }; BezierParametricExpand: PROC[shape: REF ShapeInstance, patch: REF PtrPatch, nPrSide: NAT] RETURNS [REF VertexInfoSequence, REF PtrPatchSequence] ~ { vertex: REF ThreeDBasics.VertexSequence _ shape.vertex; shade: REF ThreeDBasics.ShadingSequence _ shape.shade; RowEval: PROC[ v0, v1, v2, v3: REF VertexInfo, numPts: NAT ] RETURNS [pt: REF VertexInfoSequence] ~ { pt _ NEW[ VertexInfoSequence[numPts] ]; FOR i: NAT IN [0..numPts) DO t: REAL _ Real.Float[i] / (numPts-1); t2: REAL _ t * t; t3: REAL _ t2 * t; f0: REAL _ -1.0*t3 + 3.0*t2 - 3.0*t + 1; f1: REAL _ 3.0*t3 - 6.0*t2 + 3.0*t; f2: REAL _ -3.0*t3 + 3.0*t2; f3: REAL _ 1.0*t3; pt[i] _ ApplyCubicBasis[v0, v1, v2, v3, f0, f1, f2, f3]; ENDLOOP; }; row: ARRAY [0..4) OF REF VertexInfoSequence; colA, colB: REF VertexInfoSequence; vtxCnt, ptchCnt: NAT _ 0; nVtces: NAT _ (nPrSide+1) * (nPrSide+1); outVtx: REF VertexInfoSequence _ NEW[ VertexInfoSequence[nVtces] ]; outPatch: REF PtrPatchSequence _ NEW[ PtrPatchSequence[nPrSide*nPrSide] ]; ctlPtRow: ARRAY [0..4) OF REF VertexInfo; <> outputType: ATOM _ NARROW[ Atom.GetPropFromList[ patch.props, $InterimPatchType ] ]; IF outputType = NIL THEN outputType _ $ConvexPolygon; <> BezierPtrPatchNormals[shape, patch]; <> FOR i: NAT IN [0..4) DO FOR j: NAT IN [0..4) DO IF ctlPtRow[j] = NIL THEN ctlPtRow[j] _ NEW[ VertexInfo ]; ctlPtRow[j].coord _ vertex[patch.vtxPtr[i*4 + j]]^; ctlPtRow[j].shade _ shade[patch.vtxPtr[i*4 + j]]^; ENDLOOP; row[i] _ RowEval[ ctlPtRow[0], ctlPtRow[1], ctlPtRow[2], ctlPtRow[3], nPrSide+1 ]; ENDLOOP; colA _ RowEval[ row[0][0], row[1][0], row[2][0], row[3][0], nPrSide+1 ]; FOR i: NAT IN [0..nPrSide] DO outVtx[i+vtxCnt] _ colA[i]; ENDLOOP; -- store vertices vtxCnt _ vtxCnt + nPrSide+1; FOR i: NAT IN [1..nPrSide] DO colB _ RowEval[ row[0][i], row[1][i], row[2][i], row[3][i], nPrSide+1 ]; FOR j: NAT IN [0..nPrSide] DO outVtx[j+vtxCnt] _ colB[j]; ENDLOOP; -- store vertices vtxCnt _ vtxCnt + nPrSide+1; FOR j: NAT IN [1..nPrSide] DO IF outPatch[ptchCnt] = NIL THEN outPatch[ptchCnt] _ NEW[PtrPatch]; outPatch[ptchCnt].vtxPtr _ NEW[NatSequence[4]]; outPatch[ptchCnt].vtxPtr[0] _ (nPrSide+1) * (i-1) + j-1; outPatch[ptchCnt].vtxPtr[1] _ (nPrSide+1) * (i-1) + j; outPatch[ptchCnt].vtxPtr[2] _ (nPrSide+1) * i + j; outPatch[ptchCnt].vtxPtr[3] _ (nPrSide+1) * i + j-1; outPatch[ptchCnt].type _ outputType; outPatch[ptchCnt].oneSided _ patch.oneSided; outPatch[ptchCnt].nVtces _ 4; outPatch[ptchCnt].clipState _ patch.clipState; outPatch[ptchCnt].props _ patch.props; ptchCnt _ ptchCnt + 1; ENDLOOP; colA _ colB; ENDLOOP; RETURN[ outVtx, outPatch ]; }; BezierSubdivideExpand: PROC [shape: REF ThreeDBasics.ShapeInstance, patch: REF PtrPatch] RETURNS [REF VertexInfoSequence, REF PtrPatchSequence] ~{ vertex: REF ThreeDBasics.VertexSequence _shape.vertex; shade: REF ThreeDBasics.ShadingSequence _ shape.shade; outVtx: REF VertexInfoSequence _ NEW [ VertexInfoSequence [7*7] ]; outPatch: REF PtrPatchSequence _ NEW[PtrPatchSequence [4]]; row: ARRAY [0..4) OF REF VertexInfoSequence; ctlPtRow: ARRAY [0..4) OF REF VertexInfo; <> FOR i: NAT IN [0..4) DO FOR j: NAT IN [0..4) DO IF ctlPtRow[j] = NIL THEN ctlPtRow[j] _ NEW[ VertexInfo ]; ctlPtRow[j].coord _ vertex[patch.vtxPtr[i*4 + j]]^; ctlPtRow[j].shade _ shade[patch.vtxPtr[i*4 + j]]^; ENDLOOP; row[i] _ BezierCurveDivide[ ctlPtRow[0], ctlPtRow[1], ctlPtRow[2], ctlPtRow[3], VtxShapeMidPt ]; ENDLOOP; FOR i: NAT IN [0..7) DO vtces: REF VertexInfoSequence _ BezierCurveDivide[ row[0][i], row[1][i], row[2][i], row[3][i], VtxShapeMidPt ]; FOR j: NAT IN [0..7) DO outVtx[i*7 + j] _ NEW[ VertexInfo _ vtces[j]^ ]; ENDLOOP; ENDLOOP; FOR i: NAT IN [0..4) DO base: NAT _ (i / 2) * 21 + (i MOD 2) * 3; -- {0, 3, 21, 24}, least corners of 7x7 array outPatch[i].vtxPtr _ NEW [NatSequence[16]]; outPatch[i].type _ patch.type; outPatch[i].oneSided _ patch.oneSided; outPatch[i].nVtces _ 16; outPatch[i].clipState _ patch.clipState; outPatch[i].props _ patch.props; FOR j: NAT IN [0..4) DO FOR k: NAT IN [0..4) DO outPatch[i].vtxPtr[(3-j)*4+k] _ base + 7*j + k; -- count along rows, jump by 7 to next row ENDLOOP; ENDLOOP; ENDLOOP; RETURN[outVtx, outPatch]; }; <> BSplineExpand: PUBLIC ThreeDSurfaces.PatchExpandProc ~ { RETURN[ NIL, NIL ]; }; BSplineSubdivide: PUBLIC ThreeDSurfaces.PatchExpandProc ~ { RETURN[ NIL, NIL ]; }; BSplineDisplay: PUBLIC ThreeDSurfaces.PatchDisplayProc ~ { }; BSplineDisplayLines: PUBLIC ThreeDSurfaces.PatchDisplayProc ~ { }; <> PolygonExpand: PUBLIC ThreeDSurfaces.PatchExpandProc ~ { RETURN[ NIL, NIL ]; }; PolygonSubdivide: PUBLIC ThreeDSurfaces.PatchExpandProc ~ { RETURN[ NIL, NIL ]; }; PolygonDisplay: PUBLIC ThreeDSurfaces.PatchDisplayProc ~ { }; PolygonDisplayLines: PUBLIC ThreeDSurfaces.PatchDisplayProc ~ { }; <> NonConvexDisplay: PUBLIC ThreeDSurfaces.PatchDisplayProc ~ { }; END.