<> <> DIRECTORY Atom, CedarProcess, G3dMatrix, G3dRender, G3dVector, Real, Rope, ShapeTwiddle; ShapeTwiddleImpl: CEDAR PROGRAM IMPORTS Atom, CedarProcess, G3dMatrix, G3dRender, G3dVector, Real EXPORTS ShapeTwiddle ~ BEGIN <> LORA: TYPE ~ LIST OF REF ANY; Context: TYPE ~ G3dRender.Context; RGB: TYPE ~ G3dRender.RGB; CtlPoint: TYPE ~ G3dRender.CtlPoint; CtlPointSequence: TYPE ~ G3dRender.CtlPointSequence; CtlPtInfo: TYPE ~ G3dRender.CtlPtInfo; CtlPtInfoProc: TYPE ~ G3dRender.CtlPtInfoProc; CtlPtInfoSequence: TYPE ~ G3dRender.CtlPtInfoSequence; Triple: TYPE ~ G3dRender.Triple; TripleSequence: TYPE ~ G3dRender.TripleSequence; NatSequence: TYPE ~ G3dRender.NatSequence; PairSequence: TYPE ~ G3dRender.PairSequence; Shape: TYPE ~ G3dRender.Shape; Patch: TYPE ~ G3dRender.Patch; PatchSequence: TYPE ~ G3dRender.PatchSequence; ShapeClass: TYPE ~ G3dRender.ShapeClass; NatPair: TYPE ~ ShapeTwiddle.NatPair; -- RECORD [x, y: NAT]; <> 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; <> DiffPosns: PROC[vtx1, vtx2: CtlPoint] RETURNS[Triple] ~ { RETURN[[vtx1.x - vtx2.x, vtx1.y - vtx2.y, vtx1.z - vtx2.z]]; -- object space }; <> ScaleShape: PUBLIC PROC [context: Context, name: Rope.ROPE, scale: REAL, xRatio, yRatio, zRatio: REAL _ 1.0, applyXfm: BOOLEAN _ FALSE] ~ { shape: Shape _ G3dRender.FindShape[ context, name ]; FOR i: NAT IN [0..shape.vertices.length) DO OPEN shape.vertices[i]; tx, ty, tz: REAL; IF applyXfm THEN [ [tx, ty, tz] ] _ G3dMatrix.Transform[ [x, y, z], shape.matrix] ELSE { tx _ x; ty _ y; tz _ z; }; x _ tx * scale * xRatio; y _ ty * scale * yRatio; z _ tz * scale * zRatio; ENDLOOP; shape.sphereExtent.radius _ shape.sphereExtent.radius * scale * MAX[xRatio, yRatio, zRatio]; }; ScaleTexture: PUBLIC PROC [context: Context, name: Rope.ROPE, scale: REAL, xRatio, yRatio: REAL _ 1.0] ~ { shape: Shape _ G3dRender.FindShape[ context, name ]; FOR i: NAT IN [0..shape.vertices.length) DO shape.vertices[i].texture.x _ shape.vertices[i].texture.x * scale * xRatio; shape.vertices[i].texture.y _ shape.vertices[i].texture.y * scale * yRatio; ENDLOOP; G3dRender.RenderDataFrom[shape].patch _ NIL; }; SortPair: TYPE ~ RECORD [vtx, next: NAT]; SortSequence: TYPE ~ RECORD[SEQUENCE length: NAT OF REF SortPair]; CleanUp: PUBLIC PROC [context: Context, name: Rope.ROPE, deSeam: BOOLEAN _ FALSE, tolerance: REAL _ 0.0] ~ { <> currPos: NAT _ 0; shape: Shape _ G3dRender.FindShape[ context, name ]; table: NatSequence _ NEW[ NatSequence[shape.vertices.length] ]; buckets: REF SortSequence _ NEW[SortSequence[5 * shape.vertices.length / 4]]; nextBucket: NAT _ (shape.vertices.length / 4) + 1; newVtx: VertexSequence; Action: PROC ~ { IF deSeam THEN { minX, maxX, scale: REAL; [minX, maxX] _ Bounds[context, name]; scale _ (shape.vertex.length / 4) / (maxX - minX); IF 1.0 / scale < tolerance THEN SIGNAL G3dRender.Error[$Mismatch, "Tolerance too large"]; FOR i: NAT IN [0..shape.vertex.length) DO -- bucket sort vertices index: NAT _ Real.Fix[ (shape.vertex[i].x - minX) * scale ]; IF buckets[index] # NIL THEN { buckets[nextBucket] _ NEW[SortPair]; buckets[nextBucket].next _ buckets[index].next; -- put on next-to-head of list buckets[index].next _ nextBucket; -- reset first pointer index _ nextBucket; nextBucket _ nextBucket + 1; } ELSE { buckets[index] _ NEW[SortPair]; -- bucket hit for first time buckets[index].next _ 0; }; buckets[index].vtx _ i; -- store vertex pointer ENDLOOP; FOR i: NAT IN [1..table.length) DO -- Coalesce nearby vertices vtx: REF CtlPoint _ shape.vertex[i]; xBucket: NAT _ MAX[ 1, MIN[shape.vertex.length-1, INTEGER[ Real.Fix[ (vtx.x - minX)*scale] ]] ]; table[i] _ i; -- set to identity value FOR j: NAT IN [xBucket-1 .. xBucket+1] DO -- search this and last and next buckets index: NAT _ j; WHILE buckets[index] # NIL DO k: NAT _ buckets[index].vtx; diff: REAL _ G3dVector.Length[ G3dVector.Sub[ -- check distance [vtx.x, vtx.y, vtx.z], [shape.vertex[k].x, shape.vertex[k].y, shape.vertex[k].z] ] ]; IF diff <= tolerance THEN IF k < table[i] THEN table[i] _ k; -- reset if smaller index _ buckets[index].next; IF index = 0 THEN EXIT; ENDLOOP; ENDLOOP; ENDLOOP; FOR i: NAT IN [0..shape.numSurfaces) DO -- retarget vertex pointers IF surface[i] # NIL THEN FOR j: NAT IN [0..surface[i].nVtces) DO surface[i].vtxPtr[j] _ table[surface[i].vtxPtr[j]]; ENDLOOP; ENDLOOP; IF shape.class.type = $ConvexPolygon THEN { FOR i: NAT IN [0..shape.numSurfaces) DO -- eliminate duplicated polygon vertices vtx: NAT _ 0; IF surface[i] # NIL THEN FOR j: NAT IN [0..surface[i].nVtces) DO k: NAT _ (j-1 + surface[i].nVtces) MOD surface[i].nVtces; IF surface[i].vtxPtr[j] # surface[i].vtxPtr[k] THEN { surface[i].vtxPtr[vtx] _ surface[i].vtxPtr[j]; vtx _ vtx + 1; }; ENDLOOP; surface[i].nVtces _ vtx; ENDLOOP; }; }; -- end deSeaming block FOR i: NAT IN [0..table.length) DO table[i] _ 0; ENDLOOP; FOR i: NAT IN [0..shape.numSurfaces) DO -- build vertex reference count IF surface[i] # NIL THEN FOR j: NAT IN [0..surface[i].nVtces) DO table[surface[i].vtxPtr[j]] _ table[surface[i].vtxPtr[j]] + 1; ENDLOOP; ENDLOOP; currPos _ 0; FOR i: NAT IN [0..table.length) DO -- collapse vertex array and keep audit trail IF table[i] # 0 THEN { shape.vertex[currPos] _ shape.vertex[i]; shape.shade[currPos] _ shape.shade[i]; table[i] _ currPos; currPos _ currPos + 1; }; ENDLOOP; newVtx _ NEW[ CtlPointSequence[currPos] ]; newVtx.length _ currPos; newShade _ NEW[ ShadingSequence[currPos] ]; newShade.length _ currPos; FOR i: NAT IN [0..currPos) DO -- copy into new sequences of proper length newVtx[i] _ shape.vertex[i]; newShade[i] _ shape.shade[i]; ENDLOOP; shape.vertex _ newVtx; shape.shade _ newShade; FOR i: NAT IN [0..shape.numSurfaces) DO -- retarget vertex pointers FOR j: NAT IN [0..surface[i].nVtces) DO surface[i].vtxPtr[j] _ table[surface[i].vtxPtr[j]]; ENDLOOP; ENDLOOP; }; CedarProcess.DoWithPriority[background, Action]; }; CopyShape: PUBLIC PROC [context: Context, dstName, srcName: Rope.ROPE] RETURNS[Shape] ~ { dstShape: Shape _ G3dRender.NewShape[dstName]; srcShape: Shape _ G3dRender.FindShape[ context, srcName ]; srcSurface: PatchSequence _ NARROW[srcShape.surface]; dstSurface: PatchSequence; currPos: NAT _ 0; shape: Shape _ G3dRender.NewShape[dstName]; dstShape.orientation _ srcShape.orientation; dstShape.location _ srcShape.location; dstShape.rotation _ srcShape.rotation; dstShape.axisBase _ srcShape.axisBase; dstShape.axisEnd _ srcShape.axisEnd; dstShape.centroid _ srcShape.centroid; dstShape.boundingRadius _ srcShape.boundingRadius; dstShape.vertex _ NEW[G3dRender.CtlPointSequence[srcShape.vertex.length] ]; dstShape.vertex.length _ srcShape.vertex.length; FOR i: NAT IN [0..srcShape.vertex.length) DO dstShape.vertex[i] _ srcShape.vertex[i]; ENDLOOP; dstShape.shade _ NEW[ ShadingSequence[srcShape.shade.length] ]; dstShape.shade.length _ srcShape.shade.length; FOR i: NAT IN [0..srcShape.shade.length) DO dstShape.shade[i] _ srcShape.shade[i]; ENDLOOP; dstShape.surface _ NEW[ PtrPatchSequence[srcShape.numSurfaces] ]; dstSurface _ NARROW[dstShape.surface, REF PtrPatchSequence]; FOR i: NAT IN [0..srcShape.numSurfaces) DO dstSurface[i] _ srcSurface[i]; ENDLOOP; dstShape.numSurfaces _ srcShape.numSurfaces; dstShape.shadingProps _ srcShape.shadingProps; dstShape.props _ srcShape.props; RETURN[dstShape]; }; Combine: PUBLIC PROC [context: Context, dstName, src1, src2: Rope.ROPE] RETURNS[Shape] ~ { shape: Shape _ G3dRender.NewShape[dstName]; shape1: Shape _ G3dRender.FindShape[ context, src1 ]; shape2: Shape _ G3dRender.FindShape[ context, src2 ]; surface1: REF PtrPatchSequence _ NARROW[shape1.surface]; surface2: REF PtrPatchSequence _ NARROW[shape2.surface]; surface: REF PtrPatchSequence; shape.vertex _ NEW[ -- copy vertices G3dRender.CtlPointSequence[shape1.vertex.length + shape2.vertex.length] ]; shape.vertex.length _ shape.vertex.maxLength; FOR i: NAT IN [0..shape1.vertex.length) DO shape.vertex[i] _ shape1.vertex[i]; [[shape.vertex[i].x, shape.vertex[i].y, shape.vertex[i].z]] _ G3dMatrix.Transform[ [shape.vertex[i].x, shape.vertex[i].y, shape.vertex[i].z], shape1.position ]; ENDLOOP; FOR i: NAT IN [0..shape2.vertex.length) DO shape.vertex[i+shape1.vertex.length] _ shape2.vertex[i]; [[shape.vertex[i].x, shape.vertex[i].y, shape.vertex[i].z]] _ G3dMatrix.Transform[ [shape.vertex[i].x, shape.vertex[i].y, shape.vertex[i].z], shape2.position ]; ENDLOOP; shape.shade _ NEW[ -- copy shading values ShadingSequence[shape1.shade.length + shape2.shade.length] ]; shape.shade.length _ shape.shade.maxLength; FOR i: NAT IN [0..shape1.shade.length) DO shape.shade[i] _ shape1.shade[i]; ENDLOOP; FOR i: NAT IN [0..shape2.shade.length) DO shape.shade[i+shape1.shade.length] _ shape2.shade[i]; ENDLOOP; shape.numSurfaces _ shape1.numSurfaces + shape2.numSurfaces; shape.surface _ NEW[ -- copy surfaces PtrPatchSequence[shape1.numSurfaces + shape2.numSurfaces] ]; surface _ NARROW[shape.surface, REF PtrPatchSequence]; FOR i: NAT IN [0..shape1.numSurfaces) DO surface[i] _ surface1[i]; ENDLOOP; FOR i: NAT IN [0..shape2.numSurfaces) DO base: NAT _ shape1.numSurfaces; surface[i+base] _ surface2[i]; FOR j: NAT IN [0..surface[i+base].nVtces) DO -- redirect vertex pointers surface[i+base].vtxPtr[j] _ surface[i+base].vtxPtr[j] + shape1.vertex.length ENDLOOP; ENDLOOP; shape.shadingProps _ shape1.shadingProps; shape.props _ shape1.props; RETURN[shape]; }; DeletePatches: PUBLIC PROC [context: Context, dstName, srcName: Rope.ROPE, patchList: LIST OF NatPair] RETURNS[Shape] ~ { currPos: NAT _ 0; oldShape: Shape _ G3dRender.FindShape[ context, srcName ]; shape: Shape _ CopyShape[context, dstName, srcName]; surface: REF PtrPatchSequence; surface _ NARROW[shape.surface, REF PtrPatchSequence]; FOR list: LIST OF NatPair _ patchList, list.rest UNTIL list = NIL DO FOR i: NAT IN [list.first.x..list.first.y] DO surface[i] _ NIL; ENDLOOP; ENDLOOP; FOR i: NAT IN [0..shape.numSurfaces) DO -- collapse array IF surface[i] # NIL THEN { surface[currPos] _ surface[i]; currPos _ currPos + 1; }; ENDLOOP; shape.numSurfaces _ currPos; RETURN[ shape ]; }; Bounds: PUBLIC PROC [context: Context, name: Rope.ROPE] RETURNS[xMin, xMax, yMin, yMax, zMin, zMax: REAL] ~ { shape: Shape _ G3dRender.FindShape[ context, name ]; xMin _ xMax _ shape.vertex[0].x; yMin _ yMax _ shape.vertex[0].y; zMin _ zMax _ shape.vertex[0].z; FOR i: NAT IN (0..shape.vertex.length) DO -- get min and max in x, y, and z IF shape.vertex[i].x < xMin THEN xMin _ shape.vertex[i].x ELSE IF shape.vertex[i].x > xMax THEN xMax _ shape.vertex[i].x; IF shape.vertex[i].y < yMin THEN yMin _ shape.vertex[i].y ELSE IF shape.vertex[i].y > yMax THEN yMax _ shape.vertex[i].y; IF shape.vertex[i].z < zMin THEN zMin _ shape.vertex[i].z ELSE IF shape.vertex[i].z > zMax THEN zMax _ shape.vertex[i].z; ENDLOOP; }; <> ShapeDo: PROC[ context: Context, shape: Shape, fullExpand: BOOLEAN, limitType: ATOM, limit: REAL] RETURNS [Shape] ~ { MakeRoom: PROC [] ~ { -- ensure enough space for storing expanded shape IF vertex = NIL -- first we attempt to guess at how much space will be needed THEN vertex _ NEW[ CtlPointSequence[newPts.length * shape.numSurfaces] ] ELSE IF vertex.maxLength < newPts.length + ptIndex -- then we expand as needed THEN { vertex2: CtlPointSequence; IF vertex.length * 2 > LAST[CARDINAL] THEN SIGNAL G3dRender.Error[$Unimplemented, "Sequence too long (Dragon needed)"]; vertex2 _ NEW[ CtlPointSequence[vertex.length*2] ]; FOR i: NAT IN [0..vertex.length) DO -- copy old sequence IF vertex[i] # NIL THEN vertex2[i] _ NEW[ CtlPoint _ vertex[i]^ ] ELSE vertex2[i] _ NEW[ CtlPoint ]; ENDLOOP; vertex _ vertex2; }; vertex.length _ vertex.maxLength; IF shade = NIL -- first we attempt to guess at how much space will be needed THEN shade _ NEW[ ShadingSequence[newPts.length * shape.numSurfaces] ] ELSE IF shade.length < newPts.length + ptIndex -- then we expand as needed THEN { shade2: ShadingSequence _ NEW[ ShadingSequence[vertex.length*2] ]; FOR i: NAT IN [0..shade.length) DO -- copy old sequence IF shade[i] # NIL THEN shade2[i] _ NEW[ ShadingValue _ shade[i]^ ] ELSE shade2[i] _ NEW[ ShadingValue ]; ENDLOOP; shade _ shade2; }; shade.length _ shade.maxLength; IF surface = NIL -- first we attempt to guess at how much space will be needed THEN surface _ NEW[ PtrPatchSequence[newPatches.length * shape.numSurfaces] ] ELSE IF surface.length < newPatches.length + patchIndex -- then we expand as needed THEN { surface2: REF PtrPatchSequence; IF surface.length * 2 > LAST[NAT] THEN SIGNAL G3dRender.Error[$Unimplemented, "Sequence too long (Dragon needed)"]; surface2 _ NEW[ PtrPatchSequence[surface.length*2] ]; FOR i: NAT IN [0..surface.length) DO -- copy old sequence IF surface[i] # NIL THEN surface2[i] _ NEW[ PtrPatch _ surface[i]^ ] ELSE surface2[i] _ NEW[ PtrPatch ]; ENDLOOP; surface _ surface2; }; surface.length _ surface.maxLength; }; expand: PROC[shape: Shape, patch: REF PtrPatch, nPrSide: NAT] RETURNS [ CtlPtInfoSequence, PatchSequence]; subdivide: PROC [shape: Shape, patch: REF PtrPatch] RETURNS [CtlPtInfoSequence, REF PtrPatchSequence]; patch: REF PtrPatchSequence _ NARROW[ shape.surface ]; vertex: CtlPointSequence; shade: ShadingSequence; surface: REF PtrPatchSequence; patchInfo: ShadingSequence _ shape.shadingClass.patchShade; newPts: CtlPtInfoSequence; newPatches: PatchSequence; ptIndex, patchIndex: INT _ 0; SELECT shape.class.type FROM $Bezier => { expand _ BezierParametricExpand; subdivide _ BezierSubdivideExpand; }; ENDCASE => SIGNAL G3dRender.Error[$Unimplemented, "Unknown surface type"]; FOR pNum: NAT IN [0..shape.numSurfaces) DO IF fullExpand -- expand patch THEN [newPts, newPatches] _ expand[ shape, patch[pNum], Real.Fix[limit] ] ELSE [newPts, newPatches] _ subdivide[ shape, patch[pNum] ]; MakeRoom[]; -- get storage space for expanded shape FOR i: NAT IN [0..newPts.length) DO -- copy the vertices into the whole vertex[i+ptIndex] _ NEW[ CtlPoint ]; vertex[i+ptIndex]^ _ newPts[i].coord; shade[i+ptIndex] _ NEW[ G3dRender.ShadingValue ]; shade[i+ptIndex]^ _ newPts[i].shade; IF patchInfo # NIL THEN { shade[i+ptIndex].r _ shade[i+ptIndex].r * patchInfo[pNum].r; shade[i+ptIndex].g _ shade[i+ptIndex].g * patchInfo[pNum].g; shade[i+ptIndex].b _ shade[i+ptIndex].b * patchInfo[pNum].b; }; ENDLOOP; FOR i: NAT IN [0..newPatches.length) DO -- copy the polygons into the whole surface[i+patchIndex] _ NEW[ PtrPatch ]; surface[i+patchIndex].vtxPtr _ NEW[ NatSequence[newPatches[i].nVtces] ]; FOR j: NAT IN [0..newPatches[i].nVtces) DO surface[i+patchIndex].vtxPtr[j] _ newPatches[i].vtxPtr[j] + ptIndex; ENDLOOP; surface[i+patchIndex].type _ newPatches[i].type; surface[i+patchIndex].oneSided _ newPatches[i].oneSided; surface[i+patchIndex].dir _ newPatches[i].dir; surface[i+patchIndex].nVtces _ newPatches[i].nVtces; surface[i+patchIndex].clipState _ newPatches[i].clipState; surface[i+patchIndex].props _ newPatches[i].props; ENDLOOP; ptIndex _ ptIndex + newPts.length; -- update the indices patchIndex _ patchIndex + newPatches.length; ENDLOOP; IF fullExpand THEN shape.class.type _ $ConvexPolygon; -- set type to polygons if fully expanded shape.vertex _ vertex; -- point the shape at the new data shape.shade _ shade; shape.surface _ surface; shape.numSurfaces _ surface.length; shape.shadingClass.patchShade _ NIL; shape.shadingInValid _ TRUE; shape.vtcesInValid _ TRUE; RETURN[shape]; }; ShapeExpand: PUBLIC PROC[ context: Context, name: Rope.ROPE, limitType: ATOM _ NIL, limit: REAL _ 0.0] RETURNS [Shape] ~ { <> shape: Shape _ G3dRender.FindShape[ context, name ]; RETURN[ ShapeDo[ context, shape, TRUE, limitType, limit ] ]; }; ShapeSubdivide: PUBLIC PROC[ context: Context, name: Rope.ROPE, limitType: ATOM _ NIL, limit: REAL _ 0.0] RETURNS [Shape] ~ { <> shape: Shape _ G3dRender.FindShape[ context, name ]; RETURN[ ShapeDo[ context, shape, FALSE, limitType, limit ] ]; }; BezierPtrPatchNormals: PROC[ shape: Shape, patch: REF PtrPatch] ~ { <> GetXProd: PROC[vertex: REF G3dRender.CtlPointSequence, v1b, v1e, v2b, v2e: NAT] RETURNS[Triple] ~ { RETURN [ G3dVector.Normalize[ G3dVector.Cross[ -- in object space so do right-handed DiffPosns[ vertex[v1e]^, vertex[v1b]^ ], DiffPosns[ vertex[v2e]^, vertex[v2b]^ ] ] ] ]; }; vertex: CtlPointSequence _ shape.vertex; shade: 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 G3dRender.Error[$MisMatch, "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 G3dRender.Error[$MisMatch, "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; }; ApplyCubicBasis: PROC[ v0, v1, v2, v3: REF CtlPtInfo, f0, f1, f2, f3: REAL ] RETURNS[vOut: REF CtlPtInfo] ~ { shape: Shape _ NARROW[ Atom.GetPropFromList[v0.props, $Shape] ]; lerpProc: CtlPtInfoProc _ IF shape # NIL THEN shape.shadingClass.lerpVtxAux ELSE NIL; vOut _ NEW[CtlPtInfo]; 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; IF lerpProc # NIL THEN { -- get auxiliary info from supplied proc data: LORA _ LIST[v0.aux, vOut.aux, NEW[REAL _ f0], NEW[REAL _ 0.0]]; vOut.aux _ NIL; vOut^ _ lerpProc[ NIL, vOut^, data]; data _ LIST[ v1.aux, vOut.aux, NEW[REAL _ f1], NEW[REAL _ 1.0]]; vOut.aux _ NIL; vOut^ _ lerpProc[ NIL, vOut^, data]; data _ LIST[ v2.aux, vOut.aux, NEW[REAL _ f2], NEW[REAL _ 1.0]]; vOut.aux _ NIL; vOut^ _ lerpProc[ NIL, vOut^, data]; data _ LIST[ v3.aux, vOut.aux, NEW[REAL _ f3], NEW[REAL _ 1.0]]; vOut.aux _ NIL; vOut^ _ lerpProc[ NIL, vOut^, data]; } ELSE vOut.aux _ v0.aux; }; BezierParametricExpand: PROC[shape: Shape, patch: REF PtrPatch, nPrSide: NAT] RETURNS [REF CtlPtInfoSequence, REF PtrPatchSequence] ~ { vertex: REF G3dRender.CtlPointSequence _ shape.vertex; shade: REF G3dRender.ShadingSequence _ shape.shade; RowEval: PROC[ v0, v1, v2, v3: REF CtlPtInfo, numPts: NAT ] RETURNS [pt: REF CtlPtInfoSequence] ~ { pt _ NEW[ CtlPtInfoSequence[numPts] ]; pt.length _ 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 CtlPtInfoSequence; colA, colB: REF CtlPtInfoSequence; vtxCnt, ptchCnt: NAT _ 0; nVtces: NAT _ (nPrSide+1) * (nPrSide+1); outVtx: REF CtlPtInfoSequence _ NEW[ CtlPtInfoSequence[nVtces] ]; outPatch: REF PtrPatchSequence _ NEW[ PtrPatchSequence[nPrSide*nPrSide] ]; ctlPtRow: ARRAY [0..4) OF REF CtlPtInfo; <> 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[ CtlPtInfo ]; 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; outVtx.length _ nVtces; outPatch.length _ nPrSide*nPrSide; RETURN[ outVtx, outPatch ]; }; BezierCurveDivide: PUBLIC PROC[v0, v1, v2, v3: REF CtlPtInfo] RETURNS[pt: REF CtlPtInfoSequence] ~ { <> tempPt: REF CtlPtInfo; pt _ NEW[ CtlPtInfoSequence[7] ]; pt.length _ 7; pt[0] _ NEW[ CtlPtInfo _ v0^ ]; pt[6] _ NEW[ CtlPtInfo _ v3^ ]; pt[1] _ VtxShapeMidPt[v0, v1]; pt[5] _ VtxShapeMidPt[v2, v3]; tempPt _ VtxShapeMidPt[v1, v2]; pt[2] _ VtxShapeMidPt[pt[1], tempPt]; pt[4] _ VtxShapeMidPt[tempPt, pt[5]]; pt[3] _ VtxShapeMidPt[pt[2], pt[4]]; }; VtxShapeMidPt: PROC[v0, v1: REF CtlPtInfo] RETURNS[REF CtlPtInfo] ~ { <> shape: Shape _ NARROW[ Atom.GetPropFromList[v0.props, $Shape] ]; lerpProc: CtlPtInfoProc _ IF shape # NIL THEN shape.shadingClass.lerpVtxAux ELSE NIL; v: REF CtlPtInfo _ NEW[CtlPtInfo]; 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.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; IF lerpProc # NIL THEN { -- get auxiliary info from supplied proc data: LORA _ LIST[ v0.aux, v1.aux, NEW[REAL _ .5], NEW[REAL _ .5] ]; v^ _ lerpProc[ NIL, v^, data]; } ELSE v.aux _ v0.aux; v.props _ v0.props; RETURN[v]; }; BezierSubdivideExpand: PROC [shape: Shape, patch: REF PtrPatch] RETURNS [REF CtlPtInfoSequence, REF PtrPatchSequence] ~{ vertex: REF G3dRender.CtlPointSequence _shape.vertex; shade: REF G3dRender.ShadingSequence _ shape.shade; outVtx: REF CtlPtInfoSequence _ NEW [ CtlPtInfoSequence [7*7] ]; outPatch: REF PtrPatchSequence _ NEW[PtrPatchSequence [4]]; row: ARRAY [0..4) OF REF CtlPtInfoSequence; ctlPtRow: ARRAY [0..4) OF REF CtlPtInfo; <> FOR i: NAT IN [0..4) DO FOR j: NAT IN [0..4) DO IF ctlPtRow[j] = NIL THEN ctlPtRow[j] _ NEW[ CtlPtInfo ]; 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] ]; ENDLOOP; FOR i: NAT IN [0..7) DO vtces: REF CtlPtInfoSequence _ BezierCurveDivide[ row[0][i], row[1][i], row[2][i], row[3][i] ]; FOR j: NAT IN [0..7) DO outVtx[i*7 + j] _ NEW[ CtlPtInfo _ 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 IF outPatch[i] = NIL THEN outPatch[i] _ NEW[PtrPatch]; 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; outVtx.length _ 7*7; outPatch.length _ 4; RETURN[outVtx, outPatch]; }; <<>> END.