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. hShapeTwiddleImpl.mesa Last Edited by: Crow, May 3, 1989 2:35:45 pm PDT Types Renamed Procedures Utility Procedures Shape Manipulations delete excess vertices, join identical ones, etc. Procedures for expansion of/to polygons Expands a whole shape, calling procedures supplied by the surface type Subdivide a whole shape once, calling procedures supplied by the surface type Get normals at control points, given by cross product of vectors formed from adjacent control points (limited to patch bounds, of course) Get special output type for weird things like displacement mapping Get normals at control points Expand patch by evaluating control point rows, then spitting out polygons columnwise DeCasteljau subdivision algorithm PROC[v0, v1: REF CtlPtInfo] RETURNS[REF CtlPtInfo] Expand patch by evaluating control point rows, then spitting out polygons columnwise Κ(˜head™J™0IdefaultšΟk œO˜X—head2šœœ˜Jšœ;˜BJšœ ˜J˜Jšœ˜J˜—head3šΠbi™Iaš œœœœœœ˜!Ošœ œ˜&Jšœœ œ˜Ošœ œ˜'Icodešœœ˜4Ošœ œ˜)Ošœœ˜/Ošœœ ˜7Lšœ œ˜#Pšœœ˜0Pšœ œ˜*Pšœœ˜-Ošœ œ˜!Ošœ œ˜"Ošœœ˜/Ošœ œ˜+Pšœ œΟc˜>P˜—šΟb™JšΟnœœ!œœœœœ.˜uJš‘œœ!œœœœ œ&˜x—š ™š‘ œœœ ˜9Jšœ8Ÿ˜MJ˜——š ™š‘ œœœœ œ*œœœ˜›Lšœ4˜4šœœœœ˜,Lšœ˜Lšœ œ˜šœ ˜ LšœA˜ELšœ$˜(—L˜L˜L˜Lšœ˜—Lšœ@œ˜\L˜—š ‘ œœœœ œ%œ ˜}Lšœ4˜4šœœœ˜+LšœK˜KLšœK˜KLšœ˜—Lšœ(œ˜,L˜Lšœ œœ œ˜)Lš œœœœ œœœ ˜B—š ‘œ œœ œœœ ˜wLšŸ1™1Lšœ œ˜Lšœ4˜4Lšœœ'˜?Lšœ œœ.˜MLšœ œ#˜2Lšœ˜š‘œœ˜šœœ˜Lšœœ˜L˜%L˜3šœ˜Lšœœ3˜>—š œœœœŸ˜ELšœœ4˜>šœœ˜šœ˜Lšœœ ˜$Lšœ/Ÿ"˜QLšœ&Ÿ˜Lšœ˜—Lšœ˜—L˜ š œœœœŸ-˜Sšœœœ˜L˜(L˜&L˜L˜L˜—Lšœ˜—Lšœ œ:˜FLšœ œ;˜Iš œœœœŸ+˜LL˜L˜Lšœ˜—Lšœ œ˜L˜š œœœœŸ˜Ešœœœ˜'L˜3Lšœ˜—Lšœ˜—L˜—L˜0L˜—š ‘ œ œ+œœœ˜fJšœ œ˜.Pšœ œ+˜:Lšœœ˜5Jšœ˜Jšœ œ˜Jšœœ˜+P˜,P˜&P˜&P˜&P˜$P˜&P˜2Pšœœ6˜KP˜0šœœœœ˜-P˜*Pšœ˜—Pšœœ+˜?P˜.š œœœœŸ˜-P˜(Pšœ˜—Pšœœ+˜APšœ œœ˜<šœœœœ˜,P˜ Pšœ˜—P˜,P˜.P˜ Pšœ ˜P˜—š ‘œ œ.œ œœ˜cJšœœ˜+Pšœœ(˜5Pšœœ(˜5Lšœ œœ˜8Lšœ œœ˜8Lšœ œ˜P˜PšœœŸœU˜P˜-šœœœ˜*P˜%˜SP˜;P˜P˜—Pšœ˜—šœœœœ˜,P˜:˜SP˜;P˜P˜—Pšœ˜—PšœœŸœH˜uP˜+š œœœœŸ˜+P˜#Pšœ˜—šœœœœ˜+P˜7Pšœ˜—P˜O˜,Ošœ˜—Ošœ œ%Ÿ)˜`Ošœ Ÿ"˜BO˜O˜O˜#Jšœ œ˜$Ošœ˜Ošœ˜Ošœ˜O˜—š‘ œœœœœœ œœœ˜‹O™FJšœœ(˜4Ošœœ˜