<> <> <> <> DIRECTORY Atom, Basics, RealFns, G3dVector, G3dMatrix, G3dBasic, G3dSpline, G3dScanConvert, G3dMappedAndSolidTexture, G3dRender, G3dShadeClipXfm, G3dSortandDisplay; PatchFromPolyProcs: CEDAR MONITOR IMPORTS Atom, Basics, G3dMatrix, G3dSpline, G3dVector, G3dMappedAndSolidTexture, RealFns, G3dShadeClipXfm, G3dSortandDisplay, G3dRender = BEGIN <> Ray: TYPE ~ G3dBasic.Ray; NatSequence: TYPE ~ G3dRender.NatSequence; Pair: TYPE ~ G3dRender.Pair; Triple: TYPE ~ G3dRender.Triple; TripleSequence: TYPE ~ G3dRender.TripleSequence; RGB: TYPE ~ G3dRender.RGB; RealSequence: TYPE ~ G3dRender.RealSequence; Context: TYPE ~ G3dRender.Context; SixSides: TYPE ~ G3dRender.SixSides; Xfm3D: TYPE ~ G3dRender.Xfm3D; ShapeInstance: TYPE ~ G3dRender.ShapeInstance; Patch: TYPE ~ G3dRender.Patch; PatchSequence: TYPE ~ G3dRender.PatchSequence; PatchProc: TYPE ~ G3dRender.PatchProc; PtrPatch: TYPE ~ G3dRender.PtrPatch; PtrPatchSequence: TYPE ~ G3dRender.PtrPatchSequence; Vertex: TYPE ~ G3dRender.Vertex; VertexInfo: TYPE ~ G3dRender.VertexInfo; <> VertexInfoSequence: TYPE ~ G3dRender.VertexInfoSequence; VertexInfoProc: TYPE ~ G3dRender.VertexInfoProc; ShadingValue: TYPE ~ G3dRender.ShadingValue; ShapeClass: TYPE ~ G3dRender.ShapeClass; ShapeProc: TYPE ~ G3dRender.ShapeProc; ClipState: TYPE ~ G3dRender.ClipState; OutCode: TYPE ~ G3dRender.OutCode; AllOut: OutCode ~ G3dRender.AllOut; NoneOut: OutCode ~ G3dRender.NoneOut; FacingDir: TYPE ~ G3dRender.FacingDir; LORA: TYPE = LIST OF REF ANY; MidPtProc: TYPE ~ PROC[v0, v1: REF VertexInfo] RETURNS[REF VertexInfo]; nullTriple: Triple ~ [0.0, 0.0, 0.0]; Corner: TYPE ~ RECORD [ inVtx, outVtx: NAT _ 0, inDir, outDir, normal, interiorKnot: Triple _ nullTriple, concave: BOOLEAN _ FALSE ]; <> CornerSeq: TYPE ~ RECORD [ length: NAT _ 0, s: SEQUENCE maxLength: NAT OF Corner ]; CornerSeqSeq: TYPE ~ RECORD [ length: NAT _ 0, s: SEQUENCE maxLength: NAT OF REF CornerSeq ]; TangentSet: TYPE ~ RECORD [t0, et0, t1, et1: Triple _ nullTriple]; <> TangentSeq: TYPE ~ RECORD [length: NAT _ 0, s: SEQUENCE maxLength: NAT OF TangentSet]; TangentTriple: TYPE ~ ARRAY [0..3) OF TangentSet; TangentQuad: TYPE ~ ARRAY [0..4) OF TangentSet; TangentSeqSeq: TYPE ~ RECORD [ length: NAT _ 0, s: SEQUENCE maxLength: NAT OF REF TangentSeq]; Triangle: TYPE ~ RECORD [ v: ARRAY[0..3) OF VertexInfo, t: ARRAY[0..3) OF TangentSet ]; NatSequenceSequence: TYPE ~ RECORD [ length: NAT _ 0, s: SEQUENCE maxLength: NAT OF REF NatSequence ]; BoolSequence: TYPE ~ RECORD [length: NAT _ 0, s: SEQUENCE maxLength: NAT OF BOOLEAN]; <> Add: PROC[v1, v2: Triple] RETURNS[Triple] ~ G3dVector.Add; Mul: PROC[v: Triple, s: REAL] RETURNS[Triple] ~ G3dVector.Mul; Cross: PROC[v1, v2: Triple] RETURNS[Triple] ~ G3dVector.Cross; Div: PROC[v: Triple, s: REAL] RETURNS[Triple] ~ G3dVector.Div; Dot: PROC[v1, v2: Triple] RETURNS[REAL] ~ G3dVector.Dot; Length: PROC[v: Triple] RETURNS[REAL] ~ G3dVector.Length; Negate: PROC[v: Triple] RETURNS[Triple] ~ G3dVector.Negate; Nmlize: PROC[v: Triple] RETURNS[Triple] ~ G3dVector.Normalize; Sub3: PROC[v1, v2: Triple] RETURNS[Triple] ~ G3dVector.Sub; 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; <> maxDeviation: REAL _ .5; -- subdivision tolerance with antialiasing maxJaggyDeviation: REAL _ 2.0; -- jaggy subdivision tolerance (pixels) closenessFactor: REAL _ 10.0; -- times maxDeviation gets clipping cutoff for straightness recurseLimit: NAT _ 14; -- safety valve on recursion minCosToAlign: REAL _ -0.866; -- Align edges if they meet at > 150 degrees minCosToAlignOpen: REAL _ 0.707; -- Align open edges if they meet at > 45 degrees stopIfStraight: BOOLEAN _ TRUE; -- set false to defeat termination algorithm unitizeNormals: BOOLEAN _ FALSE; -- treat all polygons equally when making vertex normal showLines: BOOLEAN; -- debug and pedagogical aid <> 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[ PutProp[ newProps, prop, val ] ]; }; RemPropSafely: PROC[ propList: Atom.PropList, prop: 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.RemPropFromList[ newProps, prop ] ]; }; Sqr: PROCEDURE [number: REAL] RETURNS [REAL] ~ INLINE { RETURN[number * number]; }; Sub: PROC[f, g: REAL] RETURNS[REAL] ~ { << Difference f - g, returns zero if less than 3 decimal places>> IF RealFns.AlmostEqual[f, g, -10] 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]; }; DiffPosns: PROC[vtx1, vtx2: Vertex, 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]]]; }; GetSlopeVec: PROC[normal, edge: Triple, hermite: BOOL _ FALSE] RETURNS[slope: Triple] ~ { <> <> <> slope _ Cross[ Cross[normal, edge], normal ]; IF hermite THEN slope _ G3dVector.Mul[ Nmlize[slope], Length[edge] ] <> ELSE slope _ ScaleTangent[ slope, edge ]; }; GetNmlVec: PROC[vec: Triple, p: VertexInfo, reverse: BOOL _ FALSE] RETURNS[nmlVec: Triple] ~ { <> normal: Triple _ [p.shade.exn, p.shade.eyn, p.shade.ezn]; nmlVec _ IF reverse THEN Nmlize[ Cross[normal, vec] ] ELSE Nmlize[ Cross[vec, normal] ]; }; ScaleTangent: PROC[tangent, edgeDir: Triple] RETURNS[Triple] ~ { <> adjSide, oppSide, scale: REAL; edgeLength: REAL _ Length[edgeDir]; hypotenuse: REAL _ Length[tangent]; IF edgeLength = 0.0 OR hypotenuse = 0.0 THEN RETURN [[0.0, 0.0, 0.0]]; adjSide _ ABS[Dot[edgeDir, tangent] / edgeLength]; oppSide _ IF adjSide >= hypotenuse THEN 0.0 ELSE RealFns.SqRt[hypotenuse*hypotenuse - adjSide*adjSide]; scale _ IF oppSide/hypotenuse > .01 THEN edgeLength * 2.0 * (hypotenuse - adjSide) / (3.0 * oppSide * oppSide) ELSE .333333 * edgeLength / hypotenuse; RETURN[ G3dVector.Mul[ tangent, scale ] ]; }; TooClose: PROC[ context: REF Context, v: Vertex, 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]; }; EdgeStraight: PROC[context: REF Context, v0, v1: VertexInfo, slope1, slope2: Triple, tol: REAL] RETURNS[BOOL] ~ { <> pos1: Triple _ G3dShadeClipXfm.XfmPtToDisplay[ context, -- near slope plus near end Add[[v0.coord.ex, v0.coord.ey, v0.coord.ez], slope1 ] ]; pos2: Triple _ G3dShadeClipXfm.XfmPtToDisplay[ context, -- far slope plus near end Add[[v0.coord.ex, v0.coord.ey, v0.coord.ez], slope2 ] ]; distance: REAL _ ABS[pos1.x - v1.coord.sx] + ABS[pos1.y - v1.coord.sy] + ABS[pos2.x - v1.coord.sx] + ABS[pos2.y - v1.coord.sy]; distance _ distance / 4.0; -- summed manhattan distances / 4 estimates deviation distance _ distance / 2.0; -- heuristic (fudge factor) IF distance <= tol THEN RETURN[TRUE] ELSE RETURN[FALSE]; }; PatchDepthSort: PROC[context: REF Context, p: REF PatchSequence] RETURNS[REF PatchSequence] ~ { z: REF RealSequence _ NEW[RealSequence[p.length]]; pOut: REF PatchSequence _ NEW [PatchSequence[p.length]]; FOR i: NAT IN [0..p.length) DO -- Get average of depths at vertices z[i] _ 0.0; FOR j: NAT IN [0..p[i].nVtces) DO z[i] _ z[i] + p[i][j].coord.ez; ENDLOOP; z[i] _ z[i] / p[i].nVtces; pOut[i] _ p[i]; ENDLOOP; FOR i: NAT IN [1..p.length) DO FOR j: NAT DECREASING IN [0..i) DO -- bubble 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 NOT context.antiAliasing THEN FOR i: NAT IN [0..p.length/2) DO -- re-order back-to-front j: NAT _ p.length-1 - i; tmpP: REF Patch _ pOut[i]; pOut[i] _ pOut[j]; pOut[j] _ tmpP; ENDLOOP; pOut.length _ p.length; RETURN[pOut]; }; ValidatePatchPoly: ShapeProc ~ { <> doAlways: BOOLEAN _ IF data # NIL THEN TRUE ELSE FALSE; IF GetProp[shape.shadingProps, $VtxInfoComputed] = NIL THEN G3dShadeClipXfm.GetVtxNmls[context, shape]; -- calculate normals if not read in <> IF shape.vtcesInValid THEN { xfm: Xfm3D _ G3dMatrix.Mul[shape.position, context.eyeSpaceXfm]; << Transform vertices to eyespace, get clip state of vertices and whole shape>> shape.clipState _ G3dShadeClipXfm.XfmToEyeSpace[ context, shape ]; <> IF shape.clipState # out THEN FOR i: NAT IN [0..shape.shade.length) DO OPEN shape.shade[i]; -- transform normal vectors [ [exn, eyn, ezn] ] _ G3dMatrix.TransformVec[ [xn, yn, zn] , xfm]; ENDLOOP; IF shape.surface # NIL THEN { -- get clipping info for patches patch: REF PtrPatchSequence _ NARROW[shape.surface]; FOR i: NAT IN [0..shape.numSurfaces) DO IF patch[i] # NIL THEN IF shape.clipState = in THEN patch[i].clipState _ in -- unclipped, inside ELSE IF shape.clipState = clipped -- evaluate clipping tags THEN G3dSortandDisplay.GetPtrPatchClipState[ shape, patch[i] ] ELSE patch[i].clipState _ out; ENDLOOP; }; shape.vtcesInValid _ FALSE; }; IF shape.shadingInValid AND (shape.clipState # out OR doAlways) THEN G3dShadeClipXfm.GetVtxShades[ context, shape ]; shape.shadingInValid _ FALSE; RETURN[shape]; }; <> DisplayNothing: PatchProc ~ { -- dummy routine for timing tests shape: REF ShapeInstance _ NARROW[ GetProp[patch.props, $Shape] ]; IF patch # NIL THEN G3dShadeClipXfm.ReleasePatch[patch]; -- end of life for patch RETURN[ NIL ]; }; DisplayPatchEdges: PatchProc ~ { <> shape: REF ShapeInstance _ NARROW[ GetProp[patch.props, $Shape] ]; patchNo: NAT _ NARROW[ GetProp[patch.props, $PatchNo], REF NAT ]^; tangents: REF TangentSeqSeq _ NARROW[ GetProp[shape.fixedProps, $PatchTangents] ]; corners: REF CornerSeqSeq _ NARROW[ GetProp[shape.fixedProps, $PatchCorners] ]; xfm: Xfm3D _ G3dMatrix.Mul[shape.position, context.eyeSpaceXfm]; clr: RGB _ shape.shadingClass.color; npts: NAT _ IF data # NIL THEN NARROW[data, REF NAT]^ ELSE 8; FOR i: NAT IN [0..patch.nVtces) DO j: NAT _ (i+1) MOD patch.nVtces; outP: REF Patch _ G3dShadeClipXfm.GetPatch[npts]; outState: OutCode _ AllOut; inState: OutCode _ NoneOut; coeffs: G3dSpline.Coeffs; SELECT patch.type FROM $PolygonWithNormals => coeffs _ GetEdgeCurveNmls[ patch[i], patch[j] ]; $PolygonWithTangents, $PolygonToTnsrPatch => coeffs _ GetEdgeCurveTngs[ patch[i], patch[j], tangents[patchNo][i] ]; ENDCASE => SIGNAL G3dRender.Error[[$Unimplemented, "Unknown surface type"]]; FOR k: NAT IN [0..npts) DO OPEN outP[k].coord; t: REAL _ 1.0 * k / (npts-1); [[x, y, z]] _ G3dSpline.Position[coeffs, t]; [[ex, ey, ez], clip] _ G3dShadeClipXfm.XfmPtToEyeSpace[context, [x, y, z], xfm]; clip _ G3dShadeClipXfm.GetClipCodeForPt[ context, [ex, ey, ez] ]; IF clip = NoneOut THEN [[sx, sy, sz]] _ G3dShadeClipXfm.XfmPtToDisplay[context, [ex, ey, ez]]; outState _ LOOPHOLE[ Basics.BITAND[LOOPHOLE[outState], LOOPHOLE[clip]] ]; inState _ LOOPHOLE[ Basics.BITOR[LOOPHOLE[inState], LOOPHOLE[clip]] ]; [outP[k].shade.er, outP[k].shade.eg, outP[k].shade.eb] _ clr; -- use shape color ENDLOOP; outP.type _ $PolyLine; outP.props _ patch.props; outP.nVtces _ npts; outP.clipState _ IF inState = NoneOut THEN in ELSE IF outState # NoneOut THEN out ELSE clipped; [outP] _ G3dSortandDisplay.OutputPolygon[context, outP]; ENDLOOP; RETURN[NIL]; }; <> InitClasses: PROC[] ~ { -- register procedures for basic surface types standardClass: ShapeClass _ G3dRender.GetSurfaceType[$ConvexPolygon]; standardClass.type _ $PolygonWithNormals; standardClass.validate _ ValidatePatchPoly; standardClass.displayPatch _ DisplayPatchNmls; G3dRender.RegisterSurfaceType[standardClass, $PolygonWithNormals]; standardClass.type _ $PolygonWithTangents; standardClass.validate _ ValidatePolyWTangents; standardClass.displayPatch _ DisplayPatchTngs; G3dRender.RegisterSurfaceType[standardClass, $PolygonWithTangents]; standardClass.type _ $PolygonToTnsrPatch; standardClass.displayPatch _ DisplayPatchTnsr; G3dRender.RegisterSurfaceType[standardClass, $PolygonToTnsrPatch]; standardClass.type _ $PolygonNoImage; standardClass.validate _ G3dSortandDisplay.ValidatePolyhedron; standardClass.displayPatch _ DisplayNothing; G3dRender.RegisterSurfaceType[standardClass, $PolygonNoImage]; }; <> TriangulateAndDisplay: PatchProc ~ { <> tol: REAL _ IF context.antiAliasing THEN maxDeviation ELSE maxJaggyDeviation; tangents: REF TangentSeq _ NARROW[ data ]; IF patch.nVtces < 3 THEN SIGNAL G3dRender.Error[[$MisMatch, "Not enough vertices"]]; IF patch.nVtces = 3 THEN { IF patch.type = $PolygonWithTangents THEN patch.props _ PutPropSafely[ patch.props, $Tangents, NEW[TangentTriple _ [ NmlizeTangentSet[ patch[0], patch[1], tangents[0] ], NmlizeTangentSet[ patch[1], patch[2], tangents[1] ], NmlizeTangentSet[ patch[2], patch[0], tangents[2] ] ]] ]; TriangleDisplay[context, patch, 0, tol]; patch _ NIL; -- nil out patch so it won't be returned by caller } ELSE { outPatch: REF PatchSequence _ NEW [PatchSequence[patch.nVtces]]; midPt: VertexInfo _ GetCenterPt[context, patch, tangents]; midTangents: REF TangentSeq _ NARROW[ GetProp[midPt.props, $Tangents] ]; FOR i: NAT IN [0..patch.nVtces) DO j: NAT _ (i + 1) MOD patch.nVtces; -- next vertex outPatch[i] _ G3dShadeClipXfm.GetPatch[3]; -- released by display action outPatch[i].type _ patch.type; outPatch[i].oneSided _ patch.oneSided; outPatch[i].nVtces _ 3; outPatch[i].clipState _ patch.clipState; outPatch[i].dir _ undetermined; outPatch[i].props _ patch.props; outPatch[i][0] _ patch[i]; outPatch[i][1] _ patch[(i+1) MOD patch.nVtces]; outPatch[i][2] _ midPt; IF patch.type = $PolygonWithTangents THEN outPatch[i].props _ PutPropSafely[ outPatch[i].props, $Tangents, NEW[TangentTriple _ [ NmlizeTangentSet[ outPatch[i][0], outPatch[i][1], tangents[i] ], -- orig. edge midTangents[j], -- in to middle, from next vertex [ t0: midTangents[i].t1, et0: midTangents[i].et1, -- back out to current vertex t1: midTangents[i].t0, et1: midTangents[i].et0 ] ]] ]; IF outPatch[i].clipState # in THEN G3dShadeClipXfm.GetPatchClipState[ outPatch[i] ]; ENDLOOP; outPatch.length _ patch.nVtces; outPatch _ PatchDepthSort[ context, outPatch ]; -- sort to depth order FOR i: NAT IN [0..patch.nVtces) DO TriangleDisplay[context, outPatch[i], 0, tol]; ENDLOOP; }; RETURN[patch]; }; GetCenterPt: PROC[context: REF Context, patch: REF Patch, tangents: REF TangentSeq _ NIL] RETURNS[midPt: VertexInfo] ~ { <> shape: REF ShapeInstance _ NARROW[ GetProp[patch.props, $Shape] ]; tol: REAL _ IF context.antiAliasing THEN maxDeviation ELSE maxJaggyDeviation; IF patch.nVtces <= 3 THEN SIGNAL G3dRender.Error[[$MisMatch, "Not enough vertices"]]; { midTangents: REF TangentSeq _ IF patch.type = $PolygonWithTangents OR patch.type = $PolygonToTnsrPatch THEN NEW[ TangentSeq[patch.nVtces] ] ELSE NIL; lerpProc: VertexInfoProc _ IF shape # NIL THEN shape.shadingClass.lerpVtxAux ELSE NIL; halfVtces: NAT _ patch.nVtces / 2; loopEnd: NAT _ IF halfVtces * 2 # patch.nVtces THEN patch.nVtces ELSE patch.nVtces/2; midPt.shade.r _ midPt.shade.g _ midPt.shade.b _ midPt.shade.t _ 0.0; FOR i: NAT IN [0..loopEnd) DO -- guess at middle point, (odd # of Vtces will be too flat) pt: VertexInfo; flat: BOOLEAN; -- sum vertices given by each opposing vertex pair t, t0, t1: TangentSet; j: NAT _ (i + halfVtces) MOD patch.nVtces; -- vertex across poly from this one IF patch.type = $PolygonWithTangents OR patch.type = $PolygonToTnsrPatch THEN { -- get sum of outgoing and incoming tangents at vertex for midpt direction offst0, offst1: Triple; k: NAT _ (i + patch.nVtces-1) MOD patch.nVtces; -- previous vertex in polygon offst0 _ tangents[i].et0; t.et0 _ Add[ tangents[i].et0, tangents[k].et1 ]; k _ (j + patch.nVtces-1) MOD patch.nVtces; -- previous vertex across polygon offst1 _ tangents[k].et1; t.et1 _ Add[ tangents[j].et0, tangents[k].et1 ]; t _ NmlizeTangentSet[ patch[i], patch[j], t ]; [pt, t0, t1, flat] _ CurveDivideTan[ context, patch[i], patch[j], t.et0, t.et1, offst0, offst1, 0.5, tol]; <> midTangents[i] _ t0; midTangents[j] _ [t0: t1.t1, et0: t1.et1, t1: t1.t0, et1: t1.et0 ]; -- reverse order } ELSE { [pt, flat] _ TriangleCurveDivideNml[context, patch[i], patch[j], 0, tol]; midPt.shade.exn _ midPt.shade.exn + pt.shade.exn; midPt.shade.eyn _ midPt.shade.eyn + pt.shade.eyn; midPt.shade.ezn _ midPt.shade.ezn + pt.shade.ezn; }; midPt.coord.ex _ midPt.coord.ex + pt.coord.ex; midPt.coord.ey _ midPt.coord.ey + pt.coord.ey; midPt.coord.ez _ midPt.coord.ez + pt.coord.ez; midPt.shade.r _ midPt.shade.r + pt.shade.r; midPt.shade.g _ midPt.shade.g + pt.shade.g; midPt.shade.b _ midPt.shade.b + pt.shade.b; midPt.shade.t _ midPt.shade.t + pt.shade.t; IF shape.shadingClass.texture # NIL THEN { -- for textures midPt.coord.x _ midPt.coord.x + pt.coord.x; midPt.coord.y _ midPt.coord.y + pt.coord.y; midPt.coord.z _ midPt.coord.z + pt.coord.z; midPt.shade.xn _ midPt.shade.xn + pt.shade.xn; midPt.shade.yn _ midPt.shade.yn + pt.shade.yn; midPt.shade.zn _ midPt.shade.zn + pt.shade.zn; IF lerpProc # NIL AND pt.aux # NIL THEN IF i = 0 -- get aux. info from class proc THEN midPt _ shape.shadingClass.loadVtxAux[ NIL, midPt, pt.aux] ELSE { data: LORA _ LIST[ midPt.aux, pt.aux, NEW[REAL _ 1.0], NEW[REAL _ 1.0] ]; midPt _ lerpProc[ NIL, midPt, data]; }; }; ENDLOOP; midPt.coord.ex _ midPt.coord.ex / loopEnd; -- get average by dividing by no. edges midPt.coord.ey _ midPt.coord.ey / loopEnd; midPt.coord.ez _ midPt.coord.ez / loopEnd; IF patch.type = $PolygonWithTangents OR patch.type = $PolygonToTnsrPatch THEN { -- get mid-normal by cross-products on mid-tangents FOR i: NAT IN [0..loopEnd-1) DO [[midPt.shade.exn, midPt.shade.eyn, midPt.shade.ezn]] _ Add[ [midPt.shade.exn, midPt.shade.eyn, midPt.shade.ezn], Cross[midTangents[i].et1, midTangents[i+1].et1] ]; ENDLOOP; [[midPt.shade.exn, midPt.shade.eyn, midPt.shade.ezn]] _ Nmlize[ [midPt.shade.exn, midPt.shade.eyn, midPt.shade.ezn] ]; FOR i: NAT IN [0..patch.nVtces) DO -- Rebuild midtangents using middle point midTangents[i].et0 _ GetSlopeVec[ [patch[i].shade.exn, patch[i].shade.eyn, patch[i].shade.ezn], DiffPosns[midPt.coord, patch[i].coord, $Eye] ]; midTangents[i].et1 _ GetSlopeVec[ [midPt.shade.exn, midPt.shade.eyn, midPt.shade.ezn], DiffPosns[patch[i].coord, midPt.coord, $Eye] ]; ENDLOOP; } ELSE { midPt.shade.exn _ midPt.shade.exn / loopEnd; midPt.shade.eyn _ midPt.shade.eyn / loopEnd; midPt.shade.ezn _ midPt.shade.ezn / loopEnd; }; midPt.shade.r _ midPt.shade.r / loopEnd; midPt.shade.g _ midPt.shade.g / loopEnd; midPt.shade.b _ midPt.shade.b / loopEnd; midPt.shade.t _ midPt.shade.t / loopEnd; IF shape.shadingClass.texture # NIL THEN { -- for solid textures midPt.coord.x _ midPt.coord.x / loopEnd; midPt.coord.y _ midPt.coord.y / loopEnd; midPt.coord.z _ midPt.coord.z / loopEnd; IF patch.type = $PolygonWithTangents OR patch.type = $PolygonToTnsrPatch THEN { -- get mid-normal by cross-products on mid-tangents FOR i: NAT IN [0..loopEnd-1) DO [[midPt.shade.xn, midPt.shade.yn, midPt.shade.zn]] _ Add[ [midPt.shade.xn, midPt.shade.yn, midPt.shade.zn], Cross[midTangents[i].t1, midTangents[i+1].t1] ]; ENDLOOP; [[midPt.shade.xn, midPt.shade.yn, midPt.shade.zn]] _ Nmlize[ [midPt.shade.xn, midPt.shade.yn, midPt.shade.zn] ]; FOR i: NAT IN [0..patch.nVtces) DO -- Rebuild midtangents using middle point midTangents[i].t0 _ GetSlopeVec[ [patch[i].shade.xn, patch[i].shade.yn, patch[i].shade.zn], DiffPosns[midPt.coord, patch[i].coord] ]; midTangents[i].t1 _ GetSlopeVec[ [midPt.shade.xn, midPt.shade.yn, midPt.shade.zn], DiffPosns[patch[i].coord, midPt.coord] ]; ENDLOOP; } ELSE { midPt.shade.xn _ midPt.shade.xn / loopEnd; midPt.shade.yn _ midPt.shade.yn / loopEnd; midPt.shade.zn _ midPt.shade.zn / loopEnd; }; IF lerpProc # NIL AND midPt.aux # NIL THEN { -- get aux. info from supplied proc data: LORA _ LIST[midPt.aux, midPt.aux, NEW[REAL _ 1.0/loopEnd], NEW[REAL _0.0]]; midPt _ lerpProc[ NIL, midPt, data]; }; }; { OPEN midPt.coord; clip _ G3dShadeClipXfm.GetClipCodeForPt[context, [ ex, ey, ez] ]; IF clip = NoneOut THEN [ [sx, sy, sz] ] _ G3dShadeClipXfm.XfmPtToDisplay[ context, [ex, ey, ez], shape ]; }; midPt.props _ patch[0].props; midPt.props _ PutProp[ midPt.props, $Tangents, midTangents ]; }; }; TriangleDisplay: PROC[ context: REF Context, p: REF Patch, level: NAT, tol: REAL] ~ { <> subP: REF PatchSequence _ NIL; IF context.stopMe^ THEN RETURN[]; -- shut down if stop signal received IF p.dir = undetermined AND p.type = $PolygonWithTangents THEN [] _ TriangleTngsBackFacing[p]; -- backface test IF ( p.clipState # out ) AND ( NOT p.oneSided OR NOT p.dir = back ) THEN { IF level < recurseLimit THEN subP _ SubdivideTriangle[context, p, level, tol]; IF subP = NIL THEN { -- recursion limit hit or edges all straight IF showLines THEN p.type _ $PolyLine ELSE p.type _ $ConvexPolygon; G3dShadeClipXfm.ShadePoly[context, p]; [p] _ G3dSortandDisplay.OutputPolygon[context, p]; -- display } ELSE { subP _ PatchDepthSort[ context, subP ]; -- sort to display order TriangleDisplay[context, subP[0], level+1, tol]; TriangleDisplay[context, subP[1], level+1, tol]; TriangleDisplay[context, subP[2], level+1, tol]; TriangleDisplay[context, subP[3], level+1, tol]; }; }; IF p # NIL THEN G3dShadeClipXfm.ReleasePatch[p]; -- end of life for patch }; SubdivideTriangle: PROC[context: REF Context, p: REF Patch, level: NAT, tol: REAL] RETURNS[REF PatchSequence] ~ { <> shape: REF ShapeInstance _ NARROW[ GetProp[p.props, $Shape] ]; v0, v1, v2: VertexInfo; flat0, flat1, flat2: BOOLEAN; t: REF TangentTriple _ NARROW[GetProp[p.props, $Tangents] ]; t00, t01, t10, t11, t20, t21, tm0, tm1, tm2: TangentSet; outPatch: REF PatchSequence _ NEW[PatchSequence[4]]; IF p.type = $PolygonWithTangents AND t # NIL THEN { -- get midpoints and edge tangents [v0, t00, t01, flat0] _ CurveDivideTan[ context, p[0], p[1], t[0].et0, t[0].et1, GetNmlVec[t[0].et0, p[0]], GetNmlVec[t[0].et1, p[1], TRUE], 0.5, tol ]; [v1, t10, t11, flat1] _ CurveDivideTan[ context, p[1], p[2], t[1].et0, t[1].et1, GetNmlVec[t[1].et0, p[1]], GetNmlVec[t[1].et1, p[2], TRUE], 0.5, tol]; [v2, t20, t21, flat2] _ CurveDivideTan[ context, p[2], p[0], t[2].et0, t[2].et1, GetNmlVec[t[2].et0, p[2]], GetNmlVec[t[2].et1, p[0], TRUE], 0.5, tol]; <> tm0.et0 _ GetSlopeVec[ [v0.shade.exn, v0.shade.eyn, v0.shade.ezn], DiffPosns[v1.coord, v0.coord, $Eye] ]; tm0.et1 _ GetSlopeVec[ [v1.shade.exn, v1.shade.eyn, v1.shade.ezn], DiffPosns[v0.coord, v1.coord, $Eye] ]; tm1.et0 _ GetSlopeVec[ [v1.shade.exn, v1.shade.eyn, v1.shade.ezn], DiffPosns[v2.coord, v1.coord, $Eye] ]; tm1.et1 _ GetSlopeVec[ [v2.shade.exn, v2.shade.eyn, v2.shade.ezn], DiffPosns[v1.coord, v2.coord, $Eye] ]; tm2.et0 _ GetSlopeVec[ [v2.shade.exn, v2.shade.eyn, v2.shade.ezn], DiffPosns[v0.coord, v2.coord, $Eye] ]; tm2.et1 _ GetSlopeVec[ [v0.shade.exn, v0.shade.eyn, v0.shade.ezn], DiffPosns[v2.coord, v0.coord, $Eye] ]; } ELSE { [v0, flat0] _ TriangleCurveDivideNml[context, p[0], p[1], level, tol]; [v1, flat1] _ TriangleCurveDivideNml[context, p[1], p[2], level, tol]; [v2, flat2] _ TriangleCurveDivideNml[context, p[2], p[0], level, tol]; }; IF flat0 AND flat1 AND flat2 AND stopIfStraight THEN RETURN[NIL]; -- if all straight, done <<>> <<{ -- Reverse mid-normals if on wrong side of triangle plane by > 30 deg.>> <> <> <<] ];>> <> <> <> <> <> <> <> <<};>> FOR i: NAT IN [0..4) DO outPatch[i] _ G3dShadeClipXfm.GetPatch[3]; -- 3 point patch, released by display action outPatch[i].type _ p.type; outPatch[i].oneSided _ p.oneSided; outPatch[i].nVtces _ 3; outPatch[i].clipState _ p.clipState; outPatch[i].dir _ p.dir; outPatch[i].props _ p.props; ENDLOOP; { OPEN v0.coord; clip _ G3dShadeClipXfm.GetClipCodeForPt[context, [ ex, ey, ez] ]; IF clip = NoneOut THEN [ [sx, sy, sz] ] _ G3dShadeClipXfm.XfmPtToDisplay[ context, [ex, ey, ez], shape ]; }; { OPEN v1.coord; clip _ G3dShadeClipXfm.GetClipCodeForPt[context, [ ex, ey, ez] ]; IF clip = NoneOut THEN [ [sx, sy, sz] ] _ G3dShadeClipXfm.XfmPtToDisplay[ context, [ex, ey, ez], shape ]; }; { OPEN v2.coord; clip _ G3dShadeClipXfm.GetClipCodeForPt[context, [ ex, ey, ez] ]; IF clip = NoneOut THEN [ [sx, sy, sz] ] _ G3dShadeClipXfm.XfmPtToDisplay[ context, [ex, ey, ez], shape ]; }; outPatch[0][0] _ p[0]; outPatch[0][1] _ v0; outPatch[0][2] _ v2; outPatch[1][0] _ p[1]; outPatch[1][1] _ v1; outPatch[1][2] _ v0; outPatch[2][0] _ p[2]; outPatch[2][1] _ v2; outPatch[2][2] _ v1; outPatch[3][0] _ v0; outPatch[3][1] _ v1; outPatch[3][2] _ v2; IF p.type = $PolygonWithTangents AND t # NIL THEN { outPatch[0].props _ PutPropSafely[ outPatch[0].props, $Tangents, NEW[TangentTriple _ [t00, [tm2.t1, tm2.et1, tm2.t0, tm2.et0], t21] ] ]; outPatch[1].props _ PutPropSafely[ outPatch[1].props, $Tangents, NEW[TangentTriple _ [t10, [tm0.t1, tm0.et1, tm0.t0, tm0.et0], t01] ] ]; outPatch[2].props _ PutPropSafely[ outPatch[2].props, $Tangents, NEW[TangentTriple _ [t20, [tm1.t1, tm1.et1, tm1.t0, tm1.et0], t11] ] ]; outPatch[3].props _ PutPropSafely[ outPatch[3].props, $Tangents, NEW[TangentTriple _ [tm0, tm1, tm2] ] ]; <> <> }; FOR i: NAT IN [0..4) DO G3dShadeClipXfm.GetPatchClipState[ outPatch[i] ]; ENDLOOP; --bad!! outPatch.length _ 4; RETURN[ outPatch ]; -- return four sub-patches }; <> DisplayPatchNmls: PatchProc ~ { shape: REF ShapeInstance _ NARROW[ GetProp[patch.props, $Shape] ]; patch.type _ $PolygonWithNormals; SELECT shape.shadingClass.shadingType FROM $Lines => { [] _ DisplayPatchEdges[context, patch]; RETURN[NIL]; }; ENDCASE; <> IF shape.shadingClass.texture # NIL THEN { txtrRange: REF Pair _ NARROW[ GetProp[shape.shadingProps, $TxtrCoordRange] ]; IF txtrRange # NIL -- fix texture seams THEN G3dMappedAndSolidTexture.AdjustTexture[ patch, shape.shadingClass.texture, txtrRange.x, txtrRange.y ] ELSE G3dMappedAndSolidTexture.AdjustTexture[patch, shape.shadingClass.texture]; }; patch _ TriangulateAndDisplay[context, patch]; -- make triangles, recursively subdivide IF patch # NIL THEN G3dShadeClipXfm.ReleasePatch[patch]; -- end of life for patch RETURN[ NIL ]; }; GetEdgeCurveNmls: PROC[p1, p2: VertexInfo] RETURNS[coeffs: G3dSpline.Coeffs] ~ { edge: Triple _ DiffPosns[p2.coord, p1.coord, $Object]; pos1: Triple _ [p1.coord.x, p1.coord.y, p1.coord.z]; pos2: Triple _ [p2.coord.x, p2.coord.y, p2.coord.z]; slope1: Triple _ GetSlopeVec[[p1.shade.xn, p1.shade.yn, p1.shade.zn], edge, TRUE]; slope2: Triple _ GetSlopeVec[[p2.shade.xn, p2.shade.yn, p2.shade.zn], edge, TRUE]; coeffs _ G3dSpline.CoeffsFromHermite[[pos1, pos2, slope1, slope2]]; }; TriangleNmlsBackFacing: PROC[p: REF Patch] RETURNS [BOOLEAN] ~ { <> FacingFromTrio: PROC[p0, p1, p2: Triple] ~ { direction: Triple _ Cross[ [p1.x - p0.x, p1.y - p0.y, p1.z - p0.z], [p2.x - p0.x, p2.y - p0.y, p2.z - p0.z] ]; dotDir: REAL _ Dot[p0 , direction]; IF dotDir > 0.0 THEN back _ TRUE ELSE IF dotDir < 0.0 THEN front _ TRUE ELSE undetermined _ TRUE; }; back, front, undetermined: BOOLEAN _ FALSE; t: REF TangentTriple _ NARROW[ GetProp[p.props, $Tangents] ]; pts: ARRAY [0..9) OF Triple; FOR i: NAT IN [0..3) DO pts[i*3] _ [ p[i].coord.ex, p[i].coord.ey, p[i].coord.ez ]; ENDLOOP; FOR i: NAT IN [0..3) DO i1: NAT _ i*3+1; i2: NAT _ i*3+2; pts[i*3+1] _ Add[ pts[i*3], t[i].et0]; pts[i*3+2] _ Add[ pts[((i+1) MOD 3) * 3], t[i].et1]; ENDLOOP; FOR i: NAT IN [0..3) DO j: NAT _ i*3; j1: NAT _ j+1; j2: NAT _ (j+8) MOD 9; FacingFromTrio[ pts[j], pts[j1], pts[j2] ]; FacingFromTrio[ pts[j1], pts[j1+1], pts[j2] ]; FacingFromTrio[ pts[j2], pts[j1], pts[j2-1] ]; ENDLOOP; IF (back AND front) OR undetermined THEN p.dir _ undetermined ELSE IF back THEN p.dir _ back ELSE IF front THEN p.dir _ front ELSE p.dir _ undetermined; IF p.dir = back THEN RETURN[TRUE] ELSE RETURN[FALSE]; }; TriangleCurveDivideNml: PROC[ context: REF Context, vtx0, vtx1: VertexInfo, level: NAT, tol: REAL ] RETURNS[pt: VertexInfo, flat: BOOLEAN] ~ { <> t: REAL ~ 0.5; t2: REAL ~ 0.25; t3: REAL ~ 0.125; b0: REAL ~ 1.0 + 0.0*t - 3.*t2 + 2.*t3; -- value of basis functions at t=1/2 b1: REAL ~ 0.0 + 0.0*t + 3.*t2 - 2.*t3; b2: REAL ~ 0.0 + 1.0*t - 2.*t2 + 1.*t3; b3: REAL ~ 0.0 + 0.0*t - 1.*t2 + 1.*t3; s0: REAL ~ 0.0 - 3.*2.*t + 2.*3.*t2; -- slope of basis functions at t=1/2 s1: REAL ~ 0.0 + 3.*2.*t - 2.*3.*t2; s2: REAL ~ 1.0 - 2.*2.*t + 1.*3.*t2; s3: REAL ~ 0.0 - 1.*2.*t + 1.*3.*t2; pos1, pos2, slope1, slope2, slopeMid: Triple; shape: REF ShapeInstance _ NARROW[ GetProp[vtx0.props, $Shape] ]; <> v0: VertexInfo _ IF vtx0.coord.x < vtx1.coord.x THEN vtx0 ELSE vtx1; v1: VertexInfo _ IF vtx0.coord.x < vtx1.coord.x THEN vtx1 ELSE vtx0; clipStraight, tooShort: BOOLEAN _ FALSE; IF v1.coord.clip # NoneOut AND v0.coord.clip # NoneOut THEN clipStraight _ TRUE ELSE IF v0.coord.clip # NoneOut THEN clipStraight _ TooClose[context, v1.coord, v0.coord.clip, tol] ELSE IF v1.coord.clip # NoneOut THEN clipStraight _ TooClose[context, v0.coord, v1.coord.clip, tol]; { edge: Triple _ DiffPosns[v1.coord, v0.coord, $Eye]; IF Length[edge] <= G3dScanConvert.justNoticeable THEN tooShort _ TRUE ELSE { slope1 _ GetSlopeVec[[v0.shade.exn, v0.shade.eyn, v0.shade.ezn], edge, TRUE]; slope2 _ GetSlopeVec[[v1.shade.exn, v1.shade.eyn, v1.shade.ezn], edge, TRUE]; }; }; pos1 _ [v0.coord.ex, v0.coord.ey, v0.coord.ez]; pos2 _ [v1.coord.ex, v1.coord.ey, v1.coord.ez]; IF clipStraight OR tooShort OR EdgeStraight[context, v0, v1, slope1, slope2, tol] THEN { -- straight edge, average endpoints for midpoint pt.coord.ex _ (pos1.x + pos2.x) / 2; pt.shade.exn _ (v0.shade.exn + v1.shade.exn) / 2; pt.coord.ey _ (pos1.y + pos2.y) / 2; pt.shade.eyn _ (v0.shade.eyn + v1.shade.eyn) / 2; pt.coord.ez _ (pos1.z + pos2.z) / 2; pt.shade.ezn _ (v0.shade.ezn + v1.shade.ezn) / 2; flat _ TRUE; } ELSE { -- not straight, evaluate curve slopeMid.x _ pos1.x*s0 + pos2.x*s1 + slope1.x*s2 + slope2.x*s3; slopeMid.y _ pos1.y*s0 + pos2.y*s1 + slope1.y*s2 + slope2.y*s3; slopeMid.z _ pos1.z*s0 + pos2.z*s1 + slope1.z*s2 + slope2.z*s3; pt.coord.ex _ pos1.x*b0 + pos2.x*b1 + slope1.x*b2 + slope2.x*b3; pt.coord.ey _ pos1.y*b0 + pos2.y*b1 + slope1.y*b2 + slope2.y*b3; pt.coord.ez _ pos1.z*b0 + pos2.z*b1 + slope1.z*b2 + slope2.z*b3; [[pt.shade.exn, pt.shade.eyn, pt.shade.ezn]] _ Add[ <> Nmlize[GetSlopeVec[slopeMid, [v0.shade.exn, v0.shade.eyn, v0.shade.ezn], TRUE]], Nmlize[GetSlopeVec[slopeMid, [v1.shade.exn, v1.shade.eyn, v1.shade.ezn], TRUE]] ]; flat _ FALSE; }; pt.shade.r _ (v0.shade.r + v1.shade.r) / 2; pt.shade.g _ (v0.shade.g + v1.shade.g) / 2; pt.shade.b _ (v0.shade.b + v1.shade.b) / 2; pt.shade.t _ (v0.shade.t + v1.shade.t) / 2; IF shape.shadingClass.texture # NIL THEN { -- for solid textures lerpProc: VertexInfoProc _ shape.shadingClass.lerpVtxAux; pos1 _ [v0.coord.x, v0.coord.y, v0.coord.z]; pos2 _ [v1.coord.x, v1.coord.y, v1.coord.z]; SELECT shape.class.type FROM $PolygonWithNormals => { edge: Triple _ DiffPosns[v1.coord, v0.coord, $Object]; slope1 _ GetSlopeVec[[v0.shade.xn, v0.shade.yn, v0.shade.zn], edge, TRUE]; slope2 _ GetSlopeVec[[v1.shade.xn, v1.shade.yn, v1.shade.zn], edge, TRUE]; }; ENDCASE => SIGNAL G3dRender.Error[[$Unimplemented, "Unknown surface type"]]; IF clipStraight OR EdgeStraight[context, v0, v1, slope1, slope2, tol] THEN { -- straight edge, average endpoints for midpoint pt.coord.x _ (pos1.x + pos2.x) / 2; pt.shade.xn _ (v0.shade.xn + v1.shade.xn) / 2; pt.coord.y _ (pos1.y + pos2.y) / 2; pt.shade.yn _ (v0.shade.yn + v1.shade.yn) / 2; pt.coord.z _ (pos1.z + pos2.z) / 2; pt.shade.zn _ (v0.shade.zn + v1.shade.zn) / 2; } ELSE { -- not straight, evaluate curve slopeMid.x _ pos1.x*s0 + pos2.x*s1 + slope1.x*s2 + slope2.x*s3; slopeMid.y _ pos1.y*s0 + pos2.y*s1 + slope1.y*s2 + slope2.y*s3; slopeMid.z _ pos1.z*s0 + pos2.z*s1 + slope1.z*s2 + slope2.z*s3; pt.coord.x _ pos1.x*b0 + pos2.x*b1 + slope1.x*b2 + slope2.x*b3; pt.coord.y _ pos1.y*b0 + pos2.y*b1 + slope1.y*b2 + slope2.y*b3; pt.coord.z _ pos1.z*b0 + pos2.z*b1 + slope1.z*b2 + slope2.z*b3; [[pt.shade.xn, pt.shade.yn, pt.shade.zn]] _ Add[ <> GetSlopeVec[slopeMid, [v0.shade.xn, v0.shade.yn, v0.shade.zn], TRUE], GetSlopeVec[slopeMid, [v1.shade.xn, v1.shade.yn, v1.shade.zn], TRUE] ]; }; IF lerpProc # NIL AND v0.aux # NIL THEN { -- get auxiliary info from supplied proc data: LORA _ LIST[ v0.aux, v1.aux, NEW[REAL _ 0.5], NEW[REAL _ 0.5] ]; pt _ lerpProc[ NIL, pt, data]; -- average, for lack of better strategy }; }; pt.props _ vtx0.props; }; <> DisplayPatchTngs: PatchProc ~ { shape: REF ShapeInstance _ NARROW[ GetProp[patch.props, $Shape] ]; SELECT shape.shadingClass.shadingType FROM $Lines => { [] _ DisplayPatchEdges[context, patch]; RETURN[NIL]; }; ENDCASE; patch.type _ $PolygonWithTangents; IF data = NIL THEN { patchNo: NAT _ NARROW[ GetProp[patch.props, $PatchNo], REF NAT ]^; tangents: REF TangentSeqSeq _ NARROW[ GetProp[shape.fixedProps, $PatchTangents] ]; patch _ TriangulateAndDisplay[ context, patch, tangents[patchNo] ]; -- subdivide proc. } ELSE { tangents: REF TangentSeq _ NARROW[ data ]; patch _ TriangulateAndDisplay[ context, patch, tangents ]; -- subdivide proc. }; IF patch # NIL THEN G3dShadeClipXfm.ReleasePatch[patch]; -- end of life for patch RETURN[ NIL ]; }; ValidatePolyWTangents: ShapeProc ~ { <> doEyeSpace: BOOLEAN _ shape.vtcesInValid; -- grab before reset by ValidatePatchPoly shape _ ValidatePatchPoly[context, shape]; -- Update shading and transform matrices IF GetProp[shape.fixedProps, $PatchTangents] = NIL THEN shape _ GetTangents[context, shape]; -- calculate tangent vectors if not read in IF doEyeSpace AND shape.clipState # out THEN { xfm: Xfm3D _ G3dMatrix.Mul[shape.position, context.eyeSpaceXfm]; tangent: REF TangentSeqSeq _NARROW[GetProp[shape.fixedProps, $PatchTangents]]; FOR i: NAT IN [0..shape.numSurfaces) DO -- transform tangent vectors FOR j: NAT IN [0..tangent[i].length) DO OPEN tangent[i][j]; [ et0 ] _ G3dMatrix.TransformVec[ t0, xfm]; [ et1 ] _ G3dMatrix.TransformVec[ t1, xfm]; ENDLOOP; ENDLOOP; }; RETURN[ shape ]; }; GetEdgeCurveTngs: PROC[p1, p2: VertexInfo, tangent: TangentSet] RETURNS[coeffs: G3dSpline.Coeffs] ~ { b0, b1, b2, b3: Triple; [b0, b1, b2, b3] _ TngsToBezKnots[ p1, p2, tangent ]; coeffs _ G3dSpline.CoeffsFromBezier[ [b0, b1, b2, b3] ]; }; TngsToBezKnots: PROC[p1, p2: VertexInfo, tangent: TangentSet] RETURNS[b0, b1, b2, b3: Triple] ~ { <> edgeDir: Triple; b0 _ [ p1.coord.x, p1.coord.y, p1.coord.z ]; b3 _ [ p2.coord.x, p2.coord.y, p2.coord.z ]; edgeDir _ Sub3[b3, b0]; b1 _ Add[ b0, ScaleTangent[tangent.t0, edgeDir] ]; b2 _ Add[ b3, ScaleTangent[tangent.t1, Negate[edgeDir]] ]; }; TriangleTngsBackFacing: PROC[p: REF Patch] RETURNS [BOOLEAN] ~ { <> FacingFromTrio: PROC[p0, p1, p2: Triple] ~ { direction: Triple _ Cross[ [p1.x - p0.x, p1.y - p0.y, p1.z - p0.z], [p2.x - p0.x, p2.y - p0.y, p2.z - p0.z] ]; dotDir: REAL _ Dot[p0 , direction]; IF dotDir > 0.0 THEN back _ TRUE ELSE IF dotDir < 0.0 THEN front _ TRUE ELSE undetermined _ TRUE; }; back, front, undetermined: BOOLEAN _ FALSE; t: REF TangentTriple _ NARROW[ GetProp[p.props, $Tangents] ]; pts: ARRAY [0..9) OF Triple; FOR i: NAT IN [0..3) DO pts[i*3] _ [ p[i].coord.ex, p[i].coord.ey, p[i].coord.ez ]; ENDLOOP; FOR i: NAT IN [0..3) DO i1: NAT _ i*3+1; i2: NAT _ i*3+2; pts[i*3+1] _ Add[ pts[i*3], t[i].et0]; pts[i*3+2] _ Add[ pts[((i+1) MOD 3) * 3], t[i].et1]; ENDLOOP; FOR i: NAT IN [0..3) DO j: NAT _ i*3; j1: NAT _ j+1; j2: NAT _ (j+8) MOD 9; FacingFromTrio[ pts[j], pts[j1], pts[j2] ]; FacingFromTrio[ pts[j1], pts[j1+1], pts[j2] ]; FacingFromTrio[ pts[j2], pts[j1], pts[j2-1] ]; ENDLOOP; IF (back AND front) OR undetermined THEN p.dir _ undetermined ELSE IF back THEN p.dir _ back ELSE IF front THEN p.dir _ front ELSE p.dir _ undetermined; IF p.dir = back THEN RETURN[TRUE] ELSE RETURN[FALSE]; }; NmlizeTangentSet: PROC[v0, v1: VertexInfo, tangent: TangentSet] RETURNS[TangentSet] ~ { <> ep0: Triple _ [ v0.coord.ex, v0.coord.ey, v0.coord.ez ]; ep1: Triple _ [ v1.coord.ex, v1.coord.ey, v1.coord.ez ]; p0: Triple _ [ v0.coord.x, v0.coord.y, v0.coord.z ]; p1: Triple _ [ v1.coord.x, v1.coord.y, v1.coord.z ]; edgeDir: Triple _ Sub3[p1, p0]; t0: Triple _ ScaleTangent[ tangent.t0, edgeDir ]; t1: Triple _ ScaleTangent[ tangent.t1, Negate[edgeDir] ]; et0, et1: Triple; edgeDir _ Sub3[ep1, ep0]; et0 _ ScaleTangent[ tangent.et0, edgeDir ]; et1 _ ScaleTangent[ tangent.et1, Negate[edgeDir] ]; RETURN[ [t0, et0, t1, et1] ]; }; IsStraight: PROC[context: REF Context, p0, p1, s0, s1: Triple, tol: REAL] RETURNS[BOOLEAN] ~{ DistOffEdge: PROC[v: Triple, line: Ray] RETURNS[REAL] ~ { ptOnEdge: Triple _ G3dShadeClipXfm.XfmPtToDisplay[ context, G3dVector.NearestToLine[line, v] ]; vs: Triple _ G3dShadeClipXfm.XfmPtToDisplay[ context, v ]; RETURN[ RealFns.SqRt[ Sqr[ptOnEdge.x - vs.x] + Sqr[ptOnEdge.y - vs.y] ] ]; }; line: Ray _ [ p0, Sub3[p1, p0] ]; <> dist0: REAL _ DistOffEdge[Add[p0, s0], line]; dist1: REAL _ DistOffEdge[Add[p1, s1], line]; IF dist0 > tol OR dist1 > tol THEN RETURN[FALSE] ELSE RETURN[TRUE]; }; CurveDivideTan: PROC[ context: REF Context, vtx0, vtx1: VertexInfo, slope1, slope2, offst1, offst2: Triple, parameter, tol: REAL ] RETURNS[ pt: VertexInfo, t0, t1: TangentSet, flat: BOOLEAN ] ~ { <> p1: REAL _ 1.0 - parameter; p2: REAL _ parameter; pos1, pos2, edge, oPt: Triple; shape: REF ShapeInstance _ NARROW[ GetProp[vtx0.props, $Shape] ]; lerpProc: VertexInfoProc _ IF shape # NIL THEN shape.shadingClass.lerpVtxAux ELSE NIL; v0: VertexInfo _ vtx0; v1: VertexInfo _ vtx1; clipStraight, tooShort: BOOLEAN _ FALSE; IF v1.coord.clip # NoneOut AND v0.coord.clip # NoneOut -- not a good test!! THEN clipStraight _ TRUE ELSE IF v0.coord.clip # NoneOut THEN clipStraight _ TooClose[context, v1.coord, v0.coord.clip, tol] ELSE IF v1.coord.clip # NoneOut THEN clipStraight _ TooClose[context, v0.coord, v1.coord.clip, tol]; edge _ DiffPosns[v1.coord, v0.coord, $Eye]; IF Length[edge] <= G3dScanConvert.justNoticeable THEN tooShort _ TRUE; pos1 _ [v0.coord.ex, v0.coord.ey, v0.coord.ez]; pos2 _ [v1.coord.ex, v1.coord.ey, v1.coord.ez]; IF clipStraight OR tooShort OR IsStraight[ context, pos1, pos2, slope1, slope2, tol ] THEN { -- straight edge, average endpoints for midpoint [[pt.coord.ex, pt.coord.ey, pt.coord.ez]] _ Add[ Mul[pos1, p1], Mul[pos2, p2] ]; t0.et0 _ Mul[ Sub3[pos2, pos1], p2/3.0]; t1.et0 _ Mul[ Sub3[pos2, pos1], p1/3.0]; t0.et1 _ Negate[ t0.et0 ]; t1.et1 _ Negate[ t1.et0 ]; oPt _ Add[ Mul[Add[pos1, offst1], p1], Mul[Add[pos2, offst2], p2] ]; flat _ TRUE; } ELSE { -- not straight, evaluate midpoint by subdivision b0: Triple _ pos1; b3: Triple _ pos2; b1: Triple _ Add[pos1, slope1]; b2: Triple _ Add[pos2, slope2]; m01, m12, m23, mm0, mm1: Triple; m01 _ Add[Mul[b0, p1], Mul[b1, p2]]; m12 _ Add[Mul[b1, p1], Mul[b2, p2]]; m23 _ Add[Mul[b2, p1], Mul[b3, p2]]; mm0 _ Add[Mul[m01, p1], Mul[m12, p2]]; mm1 _ Add[Mul[m23, p1], Mul[m12, p2]]; [[pt.coord.ex, pt.coord.ey, pt.coord.ez]] _ Add[ Mul[mm1, p1], Mul[mm0, p2] ]; t0.et0 _ Mul[ slope1, p2 ]; t0.et1 _ Sub3[mm0, [pt.coord.ex, pt.coord.ey, pt.coord.ez] ]; t1.et0 _ Sub3[mm1, [pt.coord.ex, pt.coord.ey, pt.coord.ez] ]; t1.et1 _ Mul[ slope2, p1 ]; b0 _ Add[pos1, offst1]; b3 _ Add[pos2, offst2]; -- get offset midpoint b1 _ Add[b0, slope1]; b2 _ Add[b3, slope2]; m01 _ Add[Mul[b0, p1], Mul[b1, p2]]; m12 _ Add[Mul[b1, p1], Mul[b2, p2]]; m23 _ Add[Mul[b2, p1], Mul[b3, p2]]; mm0 _ Add[Mul[m01, p1], Mul[m12, p2]]; mm1 _ Add[Mul[m23, p1], Mul[m12, p2]]; oPt _ Add[ Mul[mm1, p1], Mul[mm0, p2] ]; flat _ FALSE; }; [[pt.shade.exn, pt.shade.eyn, pt.shade.ezn]] _ Nmlize[ Cross[ Sub3[ oPt, [pt.coord.ex, pt.coord.ey, pt.coord.ez] ], t1.et0 ] ]; pt.shade.r _ p1 * v0.shade.r + p2 * v1.shade.r; pt.shade.g _ p1 * v0.shade.g + p2 * v1.shade.g; pt.shade.b _ p1 * v0.shade.b + p2 * v1.shade.b; pt.shade.t _ p1 * v0.shade.t + p2 * v1.shade.t; <> <> <> <> <