<> <> <> DIRECTORY Atom, Basics, G3dBasic, G3dMatrix, G3dRender, G3dScanConvert, G3dShadeClipXfm, G3dSortandDisplay, G3dVector, Real, RealFns; StandardPatchProcs: CEDAR MONITOR IMPORTS Atom, Basics, G3dMatrix, G3dRender, G3dShadeClipXfm, G3dSortandDisplay, G3dVector, Real, RealFns = BEGIN <> Ray: TYPE ~ G3dBasic.Ray; RGB: TYPE ~ G3dRender.RGB; RealSequence: TYPE ~ G3dRender.RealSequence; Context: TYPE ~ G3dRender.Context; SixSides: TYPE ~ G3dRender.SixSides; Pair: TYPE ~ G3dRender.Pair; Triple: TYPE ~ G3dRender.Triple; Matrix: TYPE ~ G3dRender.Matrix; Shape: TYPE ~ G3dRender.Shape; ShapeClass: TYPE ~ G3dRender.ShapeClass; RenderStyle: TYPE ~ G3dRender.RenderStyle; ShapeProc: TYPE ~ G3dRender.ShapeProc; RenderData: TYPE ~ G3dRender.RenderData; Patch: TYPE ~ G3dRender.Patch; PatchSequence: TYPE ~ G3dRender.PatchSequence; PatchSequenceRep: TYPE ~ G3dRender.PatchSequenceRep; PatchProc: TYPE ~ G3dRender.PatchProc; CtlPoint: TYPE ~ G3dRender.CtlPoint; CtlPtInfo: TYPE ~ G3dRender.CtlPtInfo; CtlPtInfoSequence: TYPE ~ G3dRender.CtlPtInfoSequence; CtlPtInfoSequenceRep: TYPE ~ G3dRender.CtlPtInfoSequenceRep; CtlPtInfoProc: TYPE ~ G3dRender.CtlPtInfoProc; ClipState: TYPE ~ G3dRender.ClipState; OutCode: TYPE ~ G3dRender.OutCode; AllOut: OutCode ~ G3dRender.AllOut; NoneOut: OutCode ~ G3dRender.NoneOut; FacingDir: TYPE ~ G3dRender.FacingDir; FacingDirArray: TYPE ~ ARRAY [0..9) OF FacingDir; MidPtProc: TYPE ~ PROC[v0, v1: REF CtlPtInfo, texture: BOOLEAN _ FALSE] RETURNS[REF CtlPtInfo]; LORA: TYPE = LIST OF REF ANY; maxDeviation: REAL _ 0.25; -- subdivision tolerance with antialiasing in pixels maxInteriorDev: REAL _ 0.25; -- (1.0) tolerance on internal edges maxJaggyDeviation: REAL _ 1.0; -- jaggy subdivision tolerance in pixels maxJaggyInteriorDev: REAL _ 1.0; -- (4.0) tolerance on internal edges closenessFactor: REAL _ 20.0; -- times maxDeviation gets clipping cutoff for straightness recurseLimit: NAT _ 14; -- safety valve on recursion stopIfStraight: BOOLEAN _ TRUE; -- set false to defeat termination algorithm stopOnDegenerate: BOOLEAN _ FALSE; -- signals on badly degenerate patches showLines: BOOLEAN _ FALSE; -- debug and pedagogical aid showCtlPts: BOOLEAN _ FALSE; -- debug and pedagogical aid refPt5: REF REAL _ NEW[REAL _ 0.5]; <> GetProp: PROC [propList: Atom.PropList, prop: REF ANY] RETURNS [REF ANY] ~ Atom.GetPropFromList; <> ShowCoords: PROC[poly: REF Patch, space: ATOM _ $Screen] RETURNS[list: LIST OF REF Pair] ~ { FOR i: CARD16 DECREASING IN [0..poly.nVtces) DO p: REF Pair; SELECT space FROM $Screen => p _ NEW[ Pair _ [ poly[i].coord.sx, poly[i].coord.sy ] ]; $Eye => p _ NEW[ Pair _ [ poly[i].coord.ex, poly[i].coord.ey ] ]; $Object => p _ NEW[ Pair _ [ poly[i].coord.x, poly[i].coord.y ] ]; ENDCASE; list _ CONS[p, list]; ENDLOOP; RETURN[list]; }; Sqr: PROCEDURE [number: REAL] RETURNS [REAL] ~ INLINE { RETURN[number * number]; }; CopyVtx: PROC[vtx: CtlPtInfo] RETURNS[vtxOut: REF CtlPtInfo] ~ { vtxOut _ G3dShadeClipXfm.GetCtlPtInfo[]; vtxOut^ _ vtx; }; Sub: PROC[f, g: REAL] RETURNS[REAL] ~ { << Difference f - g, returns zero if less than 6 decimal places>> IF RealFns.AlmostEqual[f, g, -20] THEN RETURN [0.0] ELSE RETURN[f-g]; }; Sub1: PROC[f, g: REAL] RETURNS[REAL] ~ { << Screen space f - g, returns zero if less than noticeable>> result: REAL _ f-g; IF ABS[result] < G3dScanConvert.justNoticeable THEN RETURN [0.0] ELSE RETURN[result]; }; DiffTriple: PROC[v1, v2: Triple] RETURNS[Triple] ~ { RETURN[[Sub[v1.x, v2.x], Sub[v1.y, v2.y], Sub[v1.z, v2.z]]]; }; DiffPosns: PROC[vtx1, vtx2: CtlPoint, space: ATOM _ NIL] RETURNS[Triple] ~ { SELECT space FROM $Eye => RETURN[[Sub[vtx1.ex, vtx2.ex], Sub[vtx1.ey, vtx2.ey], Sub[vtx1.ez, vtx2.ez]]]; $Screen=> RETURN[[Sub1[vtx1.sx, vtx2.sx], Sub1[vtx1.sy, vtx2.sy], Sub1[vtx1.sz, vtx2.sz]]]; ENDCASE => -- object space RETURN[[Sub[vtx1.x, vtx2.x], Sub[vtx1.y, vtx2.y], Sub[vtx1.z, vtx2.z]]]; }; VtxDisplayMidPt: MidPtProc ~ { <> v: REF CtlPtInfo _ G3dShadeClipXfm.GetCtlPtInfo[]; v.coord.sx _ (v0.coord.sx + v1.coord.sx) / 2; -- for position on screen v.coord.sy _ (v0.coord.sy + v1.coord.sy) / 2; v.coord.sz _ (v0.coord.sz + v1.coord.sz) / 2; v.coord.ex _ (v0.coord.ex + v1.coord.ex) / 2; -- for normal-vector shading v.coord.ey _ (v0.coord.ey + v1.coord.ey) / 2; v.coord.ez _ (v0.coord.ez + v1.coord.ez) / 2; <<[[v.coord.sx, v.coord.sy, v.coord.sz]] _ G3dShadeClipXfm.XfmPtToDisplay[>> <> <<];>> v.shade.r _ (v0.shade.r + v1.shade.r) / 2; -- for color-per-vertex (could be avoided) v.shade.g _ (v0.shade.g + v1.shade.g) / 2; v.shade.b _ (v0.shade.b + v1.shade.b) / 2; IF texture THEN { v.shade.txtrX _ (v0.shade.txtrX + v1.shade.txtrX) / 2; v.shade.txtrY _ (v0.shade.txtrY + v1.shade.txtrY) / 2; v.coord.x _ (v0.coord.x + v1.coord.x) / 2; -- original coords (for solid texture) v.coord.y _ (v0.coord.y + v1.coord.y) / 2; v.coord.z _ (v0.coord.z + v1.coord.z) / 2; }; RETURN[v]; }; TooClose: PROC[ context: Context, v: CtlPoint, outCode: OutCode, tol: REAL ] RETURNS[BOOLEAN] ~ { <> maxDist: REAL _ 0.0; IF v.sz = 0.0 THEN RETURN [FALSE]; -- invalid screen coord, ignore IF outCode.left THEN maxDist _ MAX[maxDist, v.sx - context.viewPort.x]; IF outCode.right THEN maxDist _ MAX[maxDist, context.viewPort.w + context.viewPort.x - v.sx]; IF outCode.bottom THEN maxDist _ MAX[maxDist, v.sy - context.viewPort.y]; IF outCode.top THEN maxDist _ MAX[maxDist, context.viewPort.h + context.viewPort.y - v.sy]; IF maxDist < tol * closenessFactor THEN RETURN[TRUE] ELSE RETURN[FALSE]; }; MakeStraight: PROC[ v0, v1, vMid: CtlPtInfo, texture: BOOLEAN _ FALSE ] RETURNS[CtlPtInfo] ~ { <> a, b, alpha: REAL; <> mag: REAL _ RealFns.SqRt[ Sqr[v1.coord.sx - v0.coord.sx] + Sqr[v1.coord.sy - v0.coord.sy] ]; IF mag < G3dScanConvert.justNoticeable -- effectively invisible detail THEN { vMid.coord _ v0.coord; vMid.shade _ v0.shade; RETURN[vMid]; }; 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; vMid.coord.sx _ a * v0.coord.sx + b * v1.coord.sx ; -- screen coords (for scan conversion) vMid.coord.sy _ a * v0.coord.sy + b * v1.coord.sy ; vMid.coord.sz _ a * v0.coord.sz + b * v1.coord.sz ; <> vMid.shade.r _ a * v0.shade.r + b * v1.shade.r ; -- surface color vMid.shade.g _ a * v0.shade.g + b * v1.shade.g ; vMid.shade.b _ a * v0.shade.b + b * v1.shade.b ; IF texture THEN { vMid.shade.txtrX _ a * v0.shade.txtrX + b * v1.shade.txtrX; vMid.shade.txtrY _ a * v0.shade.txtrY + b * v1.shade.txtrY; vMid.coord.x _ a * v0.coord.x + b * v1.coord.x ; -- original coords (for solid texture) vMid.coord.y _ a * v0.coord.y + b * v1.coord.y ; vMid.coord.z _ a * v0.coord.z + b * v1.coord.z ; }; RETURN[vMid]; }; SetStraight: PROC[ p: REF Patch, v0, v1, v2, v3: NAT] ~ { <> p[v1] _ MakeStraight[ p[v0], p[v3], p[v1] ]; p[v2] _ MakeStraight[ p[v0], p[v3], p[v2] ]; p[v0].data _ $Straight; }; StraightWithin: PROC[ context: Context, p: REF Patch, v0, v1, v2, v3: NAT, lilTol, bigTol: REAL] RETURNS[BOOLEAN] ~ { StraightenUp: PROC[] ~ { <> p[v0].data _ $Straight; }; InteriorEdge: PROC[] RETURNS[BOOLEAN] ~ { <> PolyNo: PROC[v: INTEGER] RETURNS[NAT] ~ { i: NAT _ MAX[v / 4 -1 , 0]; j: NAT _ MAX[v MOD 4 -1 , 0]; RETURN[ i*3 + j ]; }; facingDir: FacingDir; subDirs: REF FacingDirArray _ NARROW[ Atom.GetPropFromList[p.props, $FacingDirArray] ]; IF subDirs = NIL THEN RETURN[FALSE]; facingDir _ subDirs[PolyNo[v0]]; IF subDirs[PolyNo[v1]] # facingDir THEN RETURN[FALSE]; IF subDirs[PolyNo[v2]] # facingDir THEN RETURN[FALSE]; IF subDirs[PolyNo[v3]] # facingDir THEN RETURN[FALSE]; IF facingDir = unknown THEN RETURN[FALSE]; RETURN[TRUE]; }; DistOffEdge: PROC[v: CtlPoint, line: Ray] RETURNS[REAL] ~ { ptOnEdge: Triple _ G3dShadeClipXfm.XfmPtToDisplay[ context, G3dVector.NearestToLine[line, [v.ex, v.ey, v.ez]] ]; vs: Triple _ G3dShadeClipXfm.XfmPtToDisplay[ context, [v.ex, v.ey, v.ez] ]; RETURN[ RealFns.SqRt[ Sqr[ptOnEdge.x - vs.x] + Sqr[ptOnEdge.y - vs.y] ] ]; }; dist1, dist2, dist3, baseDist, a, b: REAL _ 0.0; reversed: BOOLEAN _ FALSE; IF p[v0].data = $Straight THEN RETURN[TRUE]; a _ p[v3].coord.sy - p[v0].coord.sy; b _ p[v3].coord.sx - p[v0].coord.sx; baseDist _ RealFns.SqRt[a*a + b*b]; IF baseDist > G3dScanConvert.justNoticeable THEN { a _ a/baseDist; b _ b/baseDist; -- get normalized distances from v0-v3 dist1 _ a * (p[v1].coord.sx - p[v0].coord.sx) + b * (p[v0].coord.sy - p[v1].coord.sy); IF ABS[dist1] > bigTol THEN RETURN[FALSE]; dist2 _ a * (p[v2].coord.sx - p[v0].coord.sx) + b * (p[v0].coord.sy - p[v2].coord.sy); IF ABS[dist2] > bigTol THEN RETURN[FALSE]; IF p.renderData.shadingClass.texture # NIL THEN { <> point: Triple _ [p[v0].coord.ex, p[v0].coord.ey, p[v0].coord.ez]; line: Ray _ [ point, DiffPosns[p[v3].coord, p[v0].coord, $Eye] ]; <> dist1: REAL _ DistOffEdge[p[v1].coord, line]; dist2: REAL _ DistOffEdge[p[v2].coord, line]; IF dist1 > bigTol OR dist2 > bigTol THEN RETURN[FALSE]; }; }; SELECT TRUE FROM MAX[dist1, dist2] < lilTol => { -- inside minimum deviation used onshape boundary StraightenUp[]; RETURN[ TRUE ]; }; p.dir # unknown => { StraightenUp[]; RETURN[ TRUE ]; }; -- interior patch InteriorEdge[] => { StraightenUp[]; RETURN[ TRUE ]; }; -- interior edge ENDCASE => RETURN[ FALSE ]; }; PutPropSafely: PROC[propList: Atom.PropList, prop, val: REF ANY] RETURNS[Atom.PropList] ~{ <> newProps: Atom.PropList _ NIL; FOR list: Atom.PropList _ propList, list.rest UNTIL list = NIL DO -- new proplist element: Atom.DottedPair _ NEW[Atom.DottedPairNode _ list.first^]; newProps _ CONS[element, newProps]; ENDLOOP; RETURN[ Atom.PutPropOnList[ newProps, prop, val ] ]; }; PatchSort: PROC[ context: 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.antiAliasing THEN RETURN[ pOut[0], pOut[1], pOut[2], pOut[3] ] ELSE RETURN[ pOut[3], pOut[2], pOut[1], pOut[0] ]; }; ValidatePatchShape: ShapeProc ~ { -- <> <> render: REF RenderData _ G3dRender.RenderDataFrom[shape]; renderStyle: RenderStyle; xfm: Matrix _ G3dMatrix.Mul[shape.matrix, context.eyeSpaceXfm]; allPolysIn, allPolysOut: BOOLEAN _ TRUE; WITH render.shadingClass.renderMethod SELECT FROM style: REF RenderStyle => renderStyle _ style^; ENDCASE => renderStyle _ smooth; -- default to smooth 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 ENDLOOP; }; 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 _ G3dShadeClipXfm.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 = lines THEN { OPEN render.patch[i][j].shade; [er, eg, eb] _ render.shadingClass.color; } <> ELSE { OPEN render.patch[i][j].shade; vtx: NAT _ render.patch[i][j].vtxPtr; [r, g, b] _ shape.vertices[vtx].color; t _ shape.vertices[vtx].transmittance; IF shape.faces # NIL THEN IF shape.faces.valid.color THEN { -- scale w/ face clr clr: Triple _ shape.faces[i].color; r _ clr.x * r; g _ clr.y * g; b _ clr.z * b; }; }; 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 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] ] _ G3dShadeClipXfm.XfmPtToDisplay[ context, [ex, ey, ez], shape ]; ENDLOOP; ENDLOOP; render.patchesValid _ TRUE; }; RETURN[shape]; }; <> InitClasses: PROC[] ~ { -- register procedures for basic surface types standardClass: ShapeClass _ G3dRender.GetShapeClass[$ConvexPolygon]; standardClass.type _ $Bezier; standardClass.validate _ ValidatePatchShape; standardClass.displayPatch _ BezierDisplay; G3dRender.RegisterShapeClass[standardClass, $Bezier]; standardClass.type _ $BSpline; standardClass.validate _ ValidatePatchShape; standardClass.displayPatch _ BSplineDisplay; G3dRender.RegisterShapeClass[standardClass, $BSpline]; standardClass.type _ $NonConvexPolygon; standardClass.validate _ G3dSortandDisplay.ValidatePolyhedron; standardClass.displayPatch _ NonConvexDisplay; G3dRender.RegisterShapeClass[standardClass, $NonConvexPolygon]; }; <> BezierDisplay: PatchProc ~ { <> renderStyle: RenderStyle; WITH patch.renderData.shadingClass.renderMethod SELECT FROM style: REF RenderStyle => { renderStyle _ style^; SELECT renderStyle FROM lines => { [] _ BezierDisplayLines[context, patch]; RETURN[NIL]; }; controlPoints => { [] _ BezierDisplayCtrlPts[context, patch]; RETURN[NIL]; }; ENDCASE; }; ENDCASE; patch _ BezierClipState[context, patch]; IF patch.nVtces = 16 -- Divide till all outside patch edges are straight or recursion exceeded THEN BezierPatchDisplay[context, patch, 0] ELSE IF patch.nVtces = 10 THEN BezierTriangleDisplay[ context, patch, MIN[6, recurseLimit] ]; RETURN[patch]; -- patch should be unmodified }; BezierPatchDisplay: PROC[ context: Context, p: REF Patch, level: NAT] ~ { allStraight: BOOLEAN _ TRUE; lilTol: REAL _ IF context.antiAliasing THEN maxDeviation ELSE maxJaggyDeviation; bigTol: REAL _ IF context.antiAliasing THEN maxInteriorDev ELSE maxJaggyInteriorDev; <> IF context.stopMe^ THEN RETURN[]; -- shut down if stop signal received IF p.clipState = out THEN RETURN[]; IF p.oneSided AND p.dir = back THEN RETURN[]; -- backfacing on closed surface IF p.clipState = unknown THEN allStraight _ FALSE ELSE { -- Straightness test on patch boundary, if straight enough, force straightness IF NOT StraightWithin[context, p, 0, 4, 8,12, lilTol, bigTol] THEN allStraight _ FALSE; IF NOT StraightWithin[context, p,12,13,14,15, lilTol, bigTol] THEN allStraight _ FALSE; IF NOT StraightWithin[context, p,15,11, 7, 3, lilTol, bigTol] THEN allStraight _ FALSE; IF NOT StraightWithin[context, p, 3, 2, 1, 0, lilTol, bigTol] THEN allStraight _ FALSE; }; IF stopIfStraight AND allStraight OR level >= recurseLimit THEN IF showCtlPts -- outer edges straight, display as polygon THEN p _ BezierDisplayCtrlPts[context, p] ELSE { p _ PolygonFromBezierPatch[p]; -- construct polygon G3dShadeClipXfm.ShadePoly[context, p]; IF showLines THEN p.type _ $PolyLine; p _ G3dSortandDisplay.OutputPolygon[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.depthBuffering THEN [p0, p1, p2, p3] _ PatchSort[ context, p0, p1, p2, p3]; -- sort to display order <> BezierPatchDisplay[context, p0, level+1]; BezierPatchDisplay[context, p1, level+1]; BezierPatchDisplay[context, p2, level+1]; BezierPatchDisplay[context, p3, level+1]; }; IF level > 0 THEN G3dShadeClipXfm.ReleasePatch[p]; -- end of life for sub-patch }; BezierTriangleDisplay: PROC[ context: Context, p: REF Patch, divisions: NAT] ~ { MdPt: PROC[v0, v1, v2: CtlPtInfo, r, s, t: REAL] RETURNS[CtlPtInfo] ~ { v: CtlPtInfo; v.coord.sx _ r * v0.coord.sx + s * v1.coord.sx + t * v2.coord.sx; -- for position on screen v.coord.sy _ r * v0.coord.sy + s * v1.coord.sy + t * v2.coord.sy; v.coord.sz _ r * v0.coord.sz + s * v1.coord.sz + t * v2.coord.sz; v.coord.ex _ r * v0.coord.ex + s * v1.coord.ex + t * v2.coord.ex; -- for nml-vector shading v.coord.ey _ r * v0.coord.ey + s * v1.coord.ey + t * v2.coord.ey; v.coord.ez _ r * v0.coord.ez + s * v1.coord.ez + t * v2.coord.ez; v.shade.r _ r * v0.shade.r + s * v1.shade.r + t * v2.shade.r; -- clr/vtx (could be avoided) v.shade.g _ r * v0.shade.g + s * v1.shade.g + t * v2.shade.g; v.shade.b _ r * v0.shade.b + s * v1.shade.b + t * v2.shade.b; IF p.renderData.shadingClass.texture # NIL THEN { v.shade.txtrX _ r * v0.shade.txtrX + s * v1.shade.txtrX + t * v2.shade.txtrX; v.shade.txtrY _ r * v0.shade.txtrY + s * v1.shade.txtrY + t * v2.shade.txtrY; v.coord.x _ r * v0.coord.x + s * v1.coord.x + t * v2.coord.x; v.coord.y _ r * v0.coord.y + s * v1.coord.y + t * v2.coord.y; v.coord.z _ r * v0.coord.z + s * v1.coord.z + t * v2.coord.z; }; RETURN[v]; }; EvalTriangle: PROC[p: REF Patch, r, s, t: REAL] RETURNS[CtlPtInfo] ~ { << [Artwork node; type 'Artwork on' to command tool] >> p1: ARRAY[0..6) OF CtlPtInfo; p2: ARRAY[0..3) OF CtlPtInfo; p3: CtlPtInfo; p1[0] _ MdPt[p[1], p[8], p[0], r,s,t]; -- p1[0,0,2] _ r*p[1,0,2] + s*p[0,1,2] + t*p[0,0,3]; p1[2] _ MdPt[p[3], p[4], p[2], r,s,t]; -- p1[2,0,0] _ r*p[3,0,0] + s*p[2,1,0] + t*p[2,0,1]; p1[4] _ MdPt[p[5], p[6], p[7], r,s,t]; -- p1[0,2,0] _ r*p[1,2,0] + s*p[0,3,0] + t*p[0,2,1]; p1[1] _ MdPt[p[2], p[9], p[1], r,s,t]; -- p1[1,0,1] _ r*p[2,0,1] + s*p[1,1,1] + t*p[1,0,2]; p1[3] _ MdPt[p[4], p[5], p[9], r,s,t]; -- p1[1,1,0] _ r*p[2,1,0] + s*p[1,2,0] + t*p[1,1,1]; p1[5] _ MdPt[p[9], p[7], p[8], r,s,t]; -- p1[0,1,1] _ r*p[1,1,1] + s*p[0,2,1] + t*p[0,1,2]; p2[0] _ MdPt[p1[1], p1[5], p1[0], r,s,t]; -- p2[0,0,1] _ r*p[1,0,1] + s*p[0,1,1] + t*p[0,0,2]; p2[1] _ MdPt[p1[2], p1[3], p1[1], r,s,t]; -- p2[1,0,0] _ r*p[2,0,0] + s*p[1,1,0] + t*p[1,0,1]; p2[2] _ MdPt[p1[3], p1[4], p1[5], r,s,t]; -- p2[0,1,0] _ r*p[1,1,0] + s*p[0,2,0] + t*p[0,1,1]; p3 _ MdPt[p2[1], p2[2], p2[0], r,s,t]; -- p3[0,0,0] _ r*p[1,0,0] + s*p[0,1,0] + t*p[0,0,1]; RETURN[p3]; }; steps: NAT _ INTEGER[Basics.BITSHIFT[ 1, divisions ]]; rotateBy: NAT; <> IF context.stopMe^ THEN RETURN[]; -- shut down if stop signal received IF p.clipState = out THEN RETURN[]; IF p.oneSided AND p.dir = back THEN RETURN[]; -- backfacing on closed surface IF p[6].coord.ez > p[3].coord.ez -- Reorder outer control points to sort for view THEN IF p[6].coord.ez > p[0].coord.ez THEN rotateBy _ 0 ELSE rotateBy _ 3 ELSE IF p[3].coord.ez > p[0].coord.ez THEN rotateBy _ 6 ELSE rotateBy _ 3; FOR i: NAT IN [0..3) DO -- put vtx 6 farthest from viewer tmpVtx: CtlPtInfo; SELECT rotateBy FROM 3 => { tmpVtx _ p[i]; p[i] _ p[i+3]; p[i+3] _ p[i+6]; p[i+6] _ tmpVtx }; 6 => { tmpVtx _ p[i]; p[i] _ p[i+6]; p[i+6] _ p[i+3]; p[i+3] _ tmpVtx }; ENDCASE; -- no changes needed if 6 already farthest ENDLOOP; FOR i: NAT IN [1..steps] DO -- walk across patch with barycentric coordinates ls: REAL _ IF context.antiAliasing THEN 1.0 * (i-1) / steps ELSE 1.0 * (steps-i+1) / steps; s: REAL _ IF context.antiAliasing THEN 1.0 * i / steps ELSE 1.0 * (steps-i) / steps; <> FOR j: NAT IN [1..i] DO lr: REAL _ 1.0 * (j-1) / steps; -- step from r=0 to r=s r: REAL _ 1.0 * j / steps; tp: REF Patch _ G3dShadeClipXfm.GetPatch[3]; -- will be released by action proc tp.type _ $ConvexPolygon; tp.oneSided _ p.oneSided; tp.nVtces _ 3; tp.clipState _ p.clipState; tp.dir _ p.dir; tp.props _ p.props; tp[0] _ EvalTriangle[p, lr, ls, 1.0 - lr - ls]; tp[1] _ EvalTriangle[p, r, s, 1.0 - r - s]; tp[2] _ EvalTriangle[p, lr, s, 1.0 - lr - s]; G3dShadeClipXfm.ShadePoly[context, tp]; IF showLines THEN tp.type _ $PolyLine; tp _ G3dSortandDisplay.OutputPolygon[context, tp]; -- display IF j # i THEN { ttp: REF Patch _ G3dShadeClipXfm.GetPatch[3]; -- will be released by action proc ttp.type _ $ConvexPolygon; ttp.oneSided _ p.oneSided; ttp.nVtces _ 3; ttp.clipState _ p.clipState; ttp.dir _ p.dir; ttp.props _ p.props; ttp[0] _ EvalTriangle[p, lr, ls, 1.0 - lr - ls]; ttp[1] _ EvalTriangle[p, r, ls, 1.0 - r - ls]; ttp[2] _ EvalTriangle[p, r, s, 1.0 - r - s]; G3dShadeClipXfm.ShadePoly[context, ttp]; IF showLines THEN ttp.type _ $PolyLine; ttp _ G3dSortandDisplay.OutputPolygon[context, ttp]; -- display }; ENDLOOP; ENDLOOP; }; BezierDisplayLines: PatchProc ~ { <> EvalCoords: PROC[ v0, v1, v2, v3: NAT ] RETURNS[ REF Patch ] ~ { clipState: ClipState; IF patch.clipState = in -- get clipState of convex hull THEN clipState _ in ELSE { outCode: 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] ] ], OutCode ]; IF outCode = 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] ] ], OutCode ]; IF outCode # NoneOut THEN clipState _ out ELSE clipState _ clipped; }; }; FOR i: NAT IN [0..8) DO t: REAL _ Real.Float[i] / (8-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.antiAliasing THEN { path[i].coord.ex _ f0*patch[v0].coord.ex + f1*patch[v1].coord.ex + f2*patch[v2].coord.ex + f3*patch[v3].coord.ex; path[i].coord.ey _ f0*patch[v0].coord.ey + f1*patch[v1].coord.ey + f2*patch[v2].coord.ey + f3*patch[v3].coord.ey; path[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 path[i].coord.clip _ G3dShadeClipXfm.GetClipCodeForPt[ context, [path[i].coord.ex, path[i].coord.ey, path[i].coord.ez] ]; IF context.antiAliasing THEN { path[i].shade.r _ f0*patch[v0].shade.r + f1*patch[v1].shade.r + f2*patch[v2].shade.r + f3*patch[v3].shade.r; path[i].shade.g _ f0*patch[v0].shade.g + f1*patch[v1].shade.g + f2*patch[v2].shade.g + f3*patch[v3].shade.g; path[i].shade.b _ f0*patch[v0].shade.b + f1*patch[v1].shade.b + f2*patch[v2].shade.b + f3*patch[v3].shade.b; }; } ELSE { path[i].coord.sx _ f0*patch[v0].coord.sx + f1*patch[v1].coord.sx + f2*patch[v2].coord.sx + f3*patch[v3].coord.sx; path[i].coord.sy _ f0*patch[v0].coord.sy + f1*patch[v1].coord.sy + f2*patch[v2].coord.sy + f3*patch[v3].coord.sy; path[i].coord.sz _ f0*patch[v0].coord.sz + f1*patch[v1].coord.sz + f2*patch[v2].coord.sz + f3*patch[v3].coord.sz; }; path[i].coord.x _ f0*patch[v0].coord.x + f1*patch[v1].coord.x -- needed for display lists + f2*patch[v2].coord.x + f3*patch[v3].coord.x; path[i].coord.y _ f0*patch[v0].coord.y + f1*patch[v1].coord.y + f2*patch[v2].coord.y + f3*patch[v3].coord.y; path[i].coord.z _ f0*patch[v0].coord.z + f1*patch[v1].coord.z + f2*patch[v2].coord.z + f3*patch[v3].coord.z; path[i].shade.er _ f0*patch[v0].shade.er + f1*patch[v1].shade.er + f2*patch[v2].shade.er + f3*patch[v3].shade.er; path[i].shade.eg _ f0*patch[v0].shade.eg + f1*patch[v1].shade.eg + f2*patch[v2].shade.eg + f3*patch[v3].shade.eg; path[i].shade.eb _ f0*patch[v0].shade.eb + f1*patch[v1].shade.eb + f2*patch[v2].shade.eb + f3*patch[v3].shade.eb; ENDLOOP; path.type _ $PolyLine; path.nVtces _ 8; path.clipState _ clipState; path.renderData _ patch.renderData; path.props _ patch.props; RETURN[path]; }; path: REF Patch _ G3dShadeClipXfm.GetPatch[8]; IF patch.nVtces = 16 THEN { path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 0, 1, 2, 3 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[12,13,14,15 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 0, 4, 8,12 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 3, 7,11,15 ]]; } ELSE IF patch.nVtces = 10 THEN { -- triangular patch path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 0, 1, 2, 3 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 3, 4, 5, 6 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 6, 7, 8, 0 ]]; }; G3dShadeClipXfm.ReleasePatch[path]; RETURN[patch]; -- patch should be unmodified }; BezierDisplayCtrlPts: PatchProc ~ { <> EvalCoords: PROC[ v0, v1, v2, v3: NAT ] RETURNS [REF Patch] ~ { clipState: ClipState; IF patch.clipState = in -- get clipState of convex hull THEN clipState _ in ELSE { outCode: 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] ] ], OutCode ]; IF outCode = 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] ] ], OutCode ]; IF outCode # NoneOut THEN clipState _ out ELSE clipState _ clipped; }; }; path[0] _ patch[v0]; path[1] _ patch[v1]; path[2] _ patch[v2]; path[3] _ patch[v3]; path.type _ $PolyLine; path.nVtces _ 4; path.clipState _ clipState; path.renderData _ patch.renderData; path.props _ patch.props; RETURN[path]; }; path: REF Patch _ G3dShadeClipXfm.GetPatch[4]; IF patch.nVtces = 16 THEN { path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 0, 1, 2, 3 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 4, 5, 6, 7 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 8, 9,10,11 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ ,13,14,15 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 0, 4, 8,12 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 1, 5, 9,13 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 2, 6,10,14 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 3, 7,11,15 ]]; } ELSE IF patch.nVtces = 10 THEN { -- triangular Bezier patch path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 0, 1, 2, 3 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 3, 4, 5, 6 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 6, 7, 8, 0 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 9, 8, 1, 9 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 9, 7, 4, 9 ]]; path _ G3dSortandDisplay.OutputPolygon[context, EvalCoords[ 9, 5, 7, 9 ]]; }; G3dShadeClipXfm.ReleasePatch[path]; RETURN[patch]; -- patch should be unmodified }; PolygonFromBezierPatch: PROC[p: REF Patch] RETURNS [REF Patch] ~ { <> leftCrnr: ARRAY [0..16) OF NAT _ [12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 3]; rghtCrnr: ARRAY [0..16) OF NAT _ [3, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12]; GetNorm: PROC[v, lft, nxtLft, rgt, nxtRgt: NAT] RETURNS[CtlPtInfo] ~ { <> normal: Triple; d1: Triple _ DiffPosns[ p[lft].coord, p[v].coord, $Eye ]; -- results zero if < 6 sig. digits d2: Triple _ DiffPosns[ p[rgt].coord, p[v].coord, $Eye ]; IF G3dVector.Null[ d1 ] THEN d1 _ DiffPosns[ p[nxtLft].coord, p[v].coord, $Eye ]; IF G3dVector.Null[ d2 ] THEN d2 _ DiffPosns[ p[nxtRgt].coord, p[v].coord, $Eye ]; normal _ G3dVector.Normalize[ G3dVector.Cross[d2, d1 ] ]; <> IF NOT G3dVector.Null[ DiffTriple[ d2, G3dVector.Add[d2, normal] ] ] THEN [p[v].shade.exn, p[v].shade.eyn, p[v].shade.ezn] _ normal -- significant length ELSE IF p.nVtces = 16 THEN { -- use polygon corners, too many degenerate knots d1 _ DiffPosns[ p[leftCrnr[v]].coord, p[v].coord, $Eye ]; d2 _ DiffPosns[ p[rghtCrnr[v]].coord, p[v].coord, $Eye ]; IF G3dVector.Null[ d1 ] THEN d1 _ DiffPosns[ p[leftCrnr[leftCrnr[v]]].coord, p[v].coord, $Eye ]; IF G3dVector.Null[ d2 ] THEN d2 _ DiffPosns[ p[rghtCrnr[rghtCrnr[v]]].coord, p[v].coord, $Eye ]; normal _ G3dVector.Normalize[ G3dVector.Cross[d2, d1 ] ]; IF NOT G3dVector.Null[ DiffTriple[ d2, G3dVector.Add[d2, normal] ] ] THEN [p[v].shade.exn, p[v].shade.eyn, p[v].shade.ezn] _ normal ELSE [p[v].shade.exn, p[v].shade.eyn, p[v].shade.ezn] _ G3dBasic.origin; } ELSE [p[v].shade.exn, p[v].shade.eyn, p[v].shade.ezn] _ G3dBasic.origin; RETURN [p[v]]; }; <> outputType: ATOM _ NARROW[ Atom.GetPropFromList[ p.props, $InterimPatchType ] ]; IF outputType = NIL THEN outputType _ $ConvexPolygon; p.type _ outputType; -- type of output patch IF p.nVtces = 16 THEN { <> << [Artwork node; type 'Artwork on' to command tool] >> p[0] _ GetNorm[v: 0, lft: 1, nxtLft: 7, rgt: 4, nxtRgt: 13]; -- use next control pt and p[3] _ GetNorm[v: 3, lft: 7, nxtLft: 14, rgt: 2, nxtRgt: 4]; -- pt around next corner p[15] _ GetNorm[v: 15, lft: 14, nxtLft: 8, rgt: 11, nxtRgt: 2]; -- to catch degeneracies p[12] _ GetNorm[v: 12, lft: 8, nxtLft: 1, rgt: 13, nxtRgt: 11]; p.nVtces _ 4; -- copy corners (p[0], p[3] stay put) p[1] _ CopyVtx[ p[12] ]^; p[2] _ CopyVtx[ p[15] ]^; } ELSE IF p.nVtces = 10 THEN { -- triangular patches taken clockwise, last pt in middle p[0] _ GetNorm[v: 0, lft: 1, nxtLft: 4, rgt: 8, nxtRgt: 5]; -- use next control pt and p[3] _ GetNorm[v: 3, lft: 4, nxtLft: 7, rgt: 2, nxtRgt: 8]; -- pt around next corner p[6] _ GetNorm[v: 6, lft: 5, nxtLft: 2, rgt: 7, nxtRgt: 1]; -- to catch degeneracies p.nVtces _ 3; -- copy corners (p[0] stays put) p[1] _ CopyVtx[ p[3] ]^; p[2] _ CopyVtx[ p[6] ]^; }; FOR i: NAT IN [0..p.nVtces) DO -- check for null normals (aligned vertices at corner) IF G3dVector.Null[ [p[i].shade.exn, p[i].shade.eyn, p[i].shade.ezn] ] THEN { j: NAT _ (i+1) MOD p.nVtces; -- found a null normal, try next vertex in order p[i].shade.exn _ p[j].shade.exn; p[i].shade.eyn _ p[j].shade.eyn; p[i].shade.ezn _ p[j].shade.ezn; IF G3dVector.Null[ [p[i].shade.exn, p[i].shade.eyn, p[i].shade.ezn] ] THEN { IF stopOnDegenerate THEN SIGNAL G3dRender.Error[$MisMatch, "Very degenerate Patch"]; p.nVtces _ 2; -- 2 null nmls, polygon will be ignored by scan converter RETURN[p]; }; }; ENDLOOP; IF showLines -- extra vertex closes path for lines THEN { p[p.nVtces] _ CopyVtx[ p[0] ]^; p.nVtces _ p.nVtces+1; }; RETURN[p]; }; BezierBackFacing: PROC[context: Context, p: REF Patch] RETURNS[BOOLEAN] ~ { <> facing: FacingDir; back, front, unknown: BOOLEAN _ FALSE; subdir: FacingDirArray; IF p.dir = front THEN RETURN[FALSE]; IF p.dir = back THEN RETURN[TRUE]; FOR i: NAT IN [0..3) DO FOR j: NAT IN [0..3) DO patch: REF Patch _ G3dShadeClipXfm.GetPatch[3]; patch.type _ $ConvexPolygon; IF p.nVtces = 16 THEN { k: NAT _ 4*i + j; -- index of lower left corner patch[0] _ p[k]; patch[1] _ p[k + 4]; patch[2] _ p[k + 1]; } ELSE IF p.nVtces = 10 THEN { -- triangular patch k: NAT _ 3*j; SELECT i FROM 0 => { patch[0] _ p[k]; patch[1] _ p[k+1]; patch[2] _ p[(k+8) MOD 9]; }; 1 => { patch[0] _ p[k+1]; patch[1] _ p[k+2]; patch[2] _ p[9]; }; 2 => { patch[0] _ p[k+1]; patch[1] _ p[9]; patch[2] _ p[(k+8) MOD 9]; }; ENDCASE; }; facing _ G3dShadeClipXfm.BackFacing[ context, patch, TRUE ! G3dRender.Error => IF code = $Condition THEN { facing _ unknown; CONTINUE; } ]; IF facing = back THEN back _ TRUE ELSE IF facing = front THEN front _ TRUE ELSE IF facing = unknown THEN unknown _ TRUE; G3dShadeClipXfm.ReleasePatch[patch]; subdir[i*3 + j] _ facing; ENDLOOP; ENDLOOP; IF (back AND front) OR unknown THEN p.dir _ unknown ELSE IF back THEN p.dir _ back ELSE IF front THEN p.dir _ front ELSE p.dir _ unknown; IF p.dir = unknown THEN p.props _ PutPropSafely[p.props, $FacingDirArray, NEW[FacingDirArray _ subdir] ]; IF p.dir = back THEN RETURN[TRUE] ELSE RETURN[FALSE]; }; BezierPatchDivide: PROC[context: 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..8) OF CtlPtInfoSequence; col: ARRAY [0..4) OF CtlPtInfoSequence; <> <> <<3 -> 0 3 -> 0 3 2 1 0>> <<^ ^ 7 6 5 4>> <<2 <- 1 15 <- 12 11 10 9 8>> << 15 14 13 12>> <> IF p[ 0].data = $Straight THEN SetStraight[ p, 0, 4, 8,12 ]; IF p[12].data = $Straight THEN SetStraight[ p,12,13,14,15 ]; IF p[15].data = $Straight THEN SetStraight[ p,15,11, 7, 3 ]; IF p[ 3].data = $Straight THEN SetStraight[ p, 3, 2, 1, 0 ]; FOR i: NAT IN [0..4) DO p0, p1, p2, p3: REF CtlPtInfo; p0 _ CopyVtx[ p[i] ]; p1 _ CopyVtx[ p[i+4] ]; p2 _ CopyVtx[ p[i+8] ]; p3 _ CopyVtx[ p[i+12] ]; <<[p0, p1, p2, p3] => [col[0], col[1], col[2], col[3], col[4], col[5], col[6], col[7]]>> <> SELECT i FROM 0 => col[0] _ BezierCurveDivide[ p0, p1, p2, p3, midPt, front ]; -- p[0]-p[12], 0 leads 3 => col[3] _ BezierCurveDivide[ p0, p1, p2, p3, midPt, back ]; -- p[3]-p[15], 15 leads ENDCASE => col[i] _ BezierCurveDivide[ p0, p1, p2, p3, midPt, unknown ]; G3dShadeClipXfm.ReleaseCtlPtInfo[p0]; G3dShadeClipXfm.ReleaseCtlPtInfo[p1]; G3dShadeClipXfm.ReleaseCtlPtInfo[p2]; G3dShadeClipXfm.ReleaseCtlPtInfo[p3]; ENDLOOP; FOR i: NAT IN [0..8) DO < row[0],row[1],row[2],row[3], row[4],row[5],row[6],row[7]>> <> SELECT i FROM 0 => -- p[0] - p[3], 3 leads row[0] _ BezierCurveDivide[ col[0][0], col[1][0], col[2][0], col[3][0], midPt, back ]; 7 => -- p[12] - p[15], 12 leads row[7] _ BezierCurveDivide[ col[0][7], col[1][7], col[2][7], col[3][7], midPt, front ]; ENDCASE => row[i] _ BezierCurveDivide[ col[0][i], col[1][i], col[2][i], col[3][i], midPt, unknown ]; ENDLOOP; FOR i: NAT IN [0..4) DO rowBase: NAT _ (i MOD 2) * 4; colBase: NAT _ (i / 2) * 4; outPatch[i] _ G3dShadeClipXfm.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].renderData _ p.renderData; 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 _ G3dShadeClipXfm.GetClipCodeForPt[ context, [ex, ey, ez] ]; IF clip = NoneOut THEN [ [sx, sy, sz] ] _ G3dShadeClipXfm.XfmPtToDisplay[ context, [ex, ey, ez] ]; ENDLOOP; outPatch[i] _ BezierClipState[context, outPatch[i] ]; }; IF outPatch[i].dir = unknown THEN [] _ BezierBackFacing[context, outPatch[i]]; ENDLOOP; FOR i: NAT IN [0..8) DO FOR j: NAT IN [0..row[i].length) DO -- return borrowed storage G3dShadeClipXfm.ReleaseCtlPtInfo[ row[i][j] ]; ENDLOOP; ENDLOOP; FOR i: NAT IN [0..4) DO FOR j: NAT IN [0..col[i].length) DO G3dShadeClipXfm.ReleaseCtlPtInfo[ 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 CtlPtInfo, midPt: MidPtProc, dir: FacingDir] RETURNS[pt: CtlPtInfoSequence] ~ { <> tempPt: REF CtlPtInfo; pt _ NEW[ CtlPtInfoSequenceRep[8] ]; pt.length _ 8; pt[0] _ CopyVtx[ v0^ ]; pt[7] _ CopyVtx[ v3^ ]; -- preserve endpoints pt[1] _ midPt[v1, v0]; pt[6] _ midPt[v2, v3]; -- tangent midpoints tempPt _ midPt[v1, v2]; -- midway between inner knots pt[2] _ midPt[pt[1], tempPt]; pt[5] _ midPt[pt[6], tempPt]; -- midway between midpoints pt[3] _ midPt[pt[2], pt[5]]; -- midway between midways between midpoints pt[4] _ CopyVtx[ pt[3]^ ]; IF dir = front -- inner ends inherit outer straightness data if on directed patch boundary THEN pt[4].data _ pt[0].data ELSE IF dir = back THEN pt[3].data _ pt[7].data; G3dShadeClipXfm.ReleaseCtlPtInfo[ tempPt ]; }; BezierClipState: PatchProc ~ { <> G3dShadeClipXfm.GetPatchClipState[ patch ]; IF patch.clipState = clipped THEN { FOR i: NAT IN [0..patch.nVtces) DO IF patch[i].coord.clip # NoneOut THEN { OPEN patch[i].coord; sx _ context.eyeToNdc.scaleX * ex / ez + context.eyeToNdc.addX; sy _ context.eyeToNdc.scaleY * ey / ez + context.eyeToNdc.addY; sz _ context.eyeToNdc.scaleZ / ez + context.eyeToNdc.addZ; IF sx < -0.5 OR sx > 1.5 OR sy < -0.5 OR sy > 1.5 OR sz < 0.0 THEN patch.clipState _ unknown -- too far out for stability (arbitrary) ELSE { -- convert to screen coordinates sx _ context.ndcToPixels.scaleX * sx + context.ndcToPixels.addX; sy _ context.ndcToPixels.scaleY * sy + context.ndcToPixels.addY; sz _ context.ndcToPixels.scaleZ * sz + context.ndcToPixels.addZ; IF context.antiAliasing THEN { sx _ sx - 0.5; sy _ sy - 0.5; }; }; }; ENDLOOP; }; RETURN[patch]; }; <> BSplineDisplay: PatchProc ~ { RETURN[NIL]; }; BSplineDisplayLines: PatchProc ~ { RETURN[NIL]; }; <> NonConvexDisplay: PatchProc ~ { RETURN[NIL]; }; InitClasses[]; END.