DIRECTORY Atom, Basics, BasicTime, CedarProcess, Convert, FS, G3dMatrix, G3dRender, G3dScanConvert, G3dShade, G3dClipXfmShade, G3dShape, G3dSortandDisplay, G3dVector, ImagerPixel, ImagerSample, IO, Process, Real, RealFns, Rope; G3dSortandDisplayImpl: CEDAR MONITOR IMPORTS Atom, Basics, BasicTime, CedarProcess, Convert, FS, G3dMatrix, G3dRender, G3dShade, G3dClipXfmShade, G3dShape, G3dVector, ImagerPixel, ImagerSample, IO, Process, Real, RealFns, Rope EXPORTS G3dSortandDisplay ~ BEGIN ROPE: TYPE ~ Rope.ROPE; Context: TYPE ~ G3dRender.Context; ContextProc: TYPE ~ G3dRender.ContextProc; RGB: TYPE ~ G3dRender.RGB; -- [ r, g, b: REAL]; Pixel: TYPE ~ G3dRender.Pixel; IntegerPair: TYPE ~ G3dRender.IntegerPair; Pair: TYPE ~ G3dRender.Pair; Box: TYPE ~ G3dRender.Box; Rectangle: TYPE ~ G3dRender.Rectangle; SixSides: TYPE ~ G3dRender.SixSides; OutCode: TYPE ~ G3dRender.OutCode; Triple: TYPE ~ G3dRender.Triple; NatSequence: TYPE ~ G3dRender.NatSequence; NatSequenceRep: TYPE ~ G3dRender.NatSequenceRep; Shape: TYPE ~ G3dShape.Shape; ShapeRep: TYPE ~ G3dShape.ShapeRep; ShapeSequence: TYPE ~ G3dShape.ShapeSequence; ShapeSequenceRep: TYPE ~ G3dShape.ShapeSequenceRep; FaceRep: TYPE ~ G3dShape.FaceRep; FaceSequenceRep: TYPE ~ G3dShape.FaceSequenceRep; FacingDir: TYPE ~ G3dRender.FacingDir; Patch: TYPE ~ G3dRender.Patch; PatchProc: TYPE ~ G3dRender.PatchProc; PatchSequence: TYPE ~ G3dRender.PatchSequence; PatchSequenceRep: TYPE ~ G3dRender.PatchSequenceRep; SortRecord: TYPE ~ G3dSortandDisplay.SortRecord; SortSequence: TYPE ~ G3dSortandDisplay.SortSequence; SortSequenceRep: TYPE ~ G3dSortandDisplay.SortSequenceRep; CtlPoint: TYPE ~ G3dRender.CtlPoint; CtlPointSequence: TYPE ~ G3dRender.CtlPointSequence; CtlPtInfo: TYPE ~ G3dRender.CtlPtInfo; CtlPtInfoProc: TYPE ~ G3dRender.CtlPtInfoProc; CtlPtInfoSequence: TYPE ~ G3dRender.CtlPtInfoSequence; Shading: TYPE ~ G3dRender.Shading; ShadingSequence: TYPE ~ G3dRender.ShadingSequence; Matrix: TYPE ~ G3dRender.Matrix; Order: TYPE ~ {inFront, behind, coincident, intersects}; Bounds: TYPE ~ RECORD[left, right, bottom, top, near, far: REAL]; BoundsSequence: TYPE ~ RECORD[SEQUENCE length: NAT OF Bounds]; RenderStyle: TYPE ~ G3dRender.RenderStyle; RenderData: TYPE ~ G3dRender.RenderData; ShadingClass: TYPE ~ G3dRender.ShadingClass; ShapeClass: TYPE ~ G3dRender.ShapeClass; ShapeProc: TYPE ~ G3dRender.ShapeProc; -- PROC[ Context, Shape ] NoneOut: OutCode ~ G3dRender.NoneOut; AllOut: OutCode ~ G3dRender.AllOut; debug: BOOL _ FALSE; 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; nullPtr: INT ~ 0; -- handy name for zero in sort sequences progressReportsPerFrame: INT _ 0; -- set to non zero for progress reports on long frames Sqr: PROCEDURE [number: REAL] RETURNS [REAL] ~ INLINE { RETURN[number * number]; }; RectangleFromBox: PUBLIC PROC[box: ImagerSample.Box] RETURNS[ Rectangle] ~ { RETURN [[ x: Real.Float[box.min.f], y: Real.Float[box.min.s], w: Real.Float[box.max.f - box.min.f], h: Real.Float[box.max.s - box.min.s] ]]; }; BoxFromRectangle: PUBLIC PROC[ rect: Rectangle ] RETURNS[ ImagerSample.Box ] ~ { RETURN [[ min: [ f: Real.Fix[rect.x], s: Real.Fix[rect.y] ], max: [ f: Real.Fix[rect.x + rect.w], s: Real.Fix[rect.y + rect.h] ] ]]; }; ElapsedTime: PROC[startTime: REAL] RETURNS[ROPE] ~ { timeX100: REAL _ 100.0 * (CurrentTime[] - startTime); RETURN[ Rope.Cat[ Convert.RopeFromReal[ Real.Fix[timeX100] / 100.0 ], "s," ] ]; }; CurrentTime: PROC[] RETURNS[REAL] ~ { RETURN[ BasicTime.PulsesToSeconds[BasicTime.GetClockPulses[]] ]; }; OpenLogCarefully: PROC[fileName: ROPE] RETURNS [IO.STREAM] ~ { conflict: BOOLEAN _ FALSE; report: IO.STREAM; DO -- keep trying until others release file report _ FS.StreamOpen[ "FrameProgress.log", $append ! FS.Error => IF error.group = lock -- filed locked elsewhere? THEN {conflict _ TRUE; CONTINUE} ]; IF NOT conflict THEN EXIT; Process.PauseMsec[5000]; -- wait 5 sec. before trying again ENDLOOP; RETURN[report]; }; ValidateContext: PUBLIC PROC[ context: Context ] ~ { j: CARDINAL _ 0; IF context.viewer = NIL AND context.pixels = NIL AND context.viewPort = NIL THEN SIGNAL G3dRender.Error[$Fatal, "Image Area Undefined"]; IF context.class = NIL THEN Process.PauseMsec[3000]; -- wait 3s. (maybe viewers settling) IF context.class = NIL THEN G3dRender.Error[$Fatal, "No Class for Context"]; IF context.viewer # NIL AND context.viewPort = NIL AND (context.pixels = NIL OR context.pixels.samplesPerPixel = 1) THEN context.class.updateViewer[context]; IF context.viewPort = NIL AND context.pixels # NIL THEN { ValidateView[context]; ValidateDisplay[context]; } ELSE { ValidateDisplay[context]; ValidateView[context]; }; IF context.lightSources = NIL THEN G3dRender.Error[$Fatal, "No Light Sources"]; IF context.changed THEN { SetEyeSpace[context]; -- compute eyespace matrix FOR n: NAT IN [0..context.lightSources.length) DO -- update lightSource eyepositions context.lightSources[n].eyePosition _ G3dMatrix.Transform[ context.lightSources[n].position, context.eyeSpaceXfm ]; ENDLOOP; }; IF context.shapes # NIL THEN { j _ 0; IF context.visibleShapes = NIL OR context.visibleShapes.length < context.shapes.length THEN context.visibleShapes _ NEW[ ShapeSequenceRep[context.shapes.length] ]; FOR i: NAT IN [0..context.shapes.length) DO shape: Shape _ ValidateShape[context, context.shapes[i]]; IF shape # NIL THEN { context.visibleShapes[j] _ shape; j _ j + 1; }; ENDLOOP; context.visibleShapes.length _ j; }; context.changed _ FALSE; }; ValidateDisplay: PUBLIC PROC[ context: Context ] ~ { IF context.displayInValid THEN { context.class.validateDisplay[context]; context.changed _ TRUE; G3dRender.SetView[ -- get new screen dimensions into transformations context: context, eyePoint: context.eyePoint, lookAt: context.lookAt, fieldOfView: context.fieldOfView, rollAngle: context.rollAngle, upDirection: context.upDirection, hitherLimit: context.hitherLimit, yonLimit: context.yonLimit ]; IF context.window = NIL THEN context.window _ WindowFromViewPort[context]; -- fit to viewprt }; context.screenExtent _ [[0,0], [0,0]]; -- clear coverage (new frame, alpha buffer, or display) context.displayInValid _ FALSE; }; ValidateView: PUBLIC PROC[ context: Context ] ~ { IF context.viewer = NIL OR context.viewPort = NIL THEN { s: ARRAY [0..5) OF ImagerSample.SampleMap _ ALL[NIL]; pixels: ImagerPixel.PixelMap _ NARROW[ Atom.GetPropFromList[ context.displayProps, $FullDisplayMemory ] ]; IF pixels # NIL THEN context.pixels _ pixels ELSE IF context.pixels = NIL THEN SIGNAL G3dRender.Error[$MisMatch, "no pixel memory"]; IF context.viewPort = NIL THEN context.viewPort _ NEW[ Rectangle _ RectangleFromBox[ ImagerSample.GetBox[context.pixels[0]] ] ]; context.viewPort^ _ G3dRender.IntersectRectangles[ context.viewPort^, context.preferredViewPort -- clip viewPort to preferredViewPort ]; context.ndcToPixels _ [ -- going to VM so render with origin at top left context.viewPort.w-1.0, -(context.viewPort.h-1.0), REAL[LAST[NAT]], 0.0, context.viewPort.h-1.0, 0.0 -- last line scales, this line adds ]; FOR i: NAT IN [0..context.pixels.samplesPerPixel) DO -- re-address pixelmap s[i] _ ImagerSample.Clip[ context.pixels[i], BoxFromRectangle[context.viewPort^] ]; ENDLOOP; IF context.pixels.samplesPerPixel > 0 THEN context.pixels _ ImagerPixel.MakePixelMap[s[0], s[1], s[2], s[3], s[4]]; }; IF context.window = NIL THEN { context.window _ WindowFromViewPort[context]; context.changed _ TRUE; }; IF context.changed THEN G3dRender.SetView[ context, context.eyePoint, context.lookAt, context.fieldOfView, context.rollAngle, context.upDirection, context.hitherLimit, context.yonLimit ]; -- check for mismatched viewPort and pixel storage IF context.pixels # NIL AND context.pixels.samplesPerPixel > 0 THEN IF context.pixels.box.max.f < context.viewPort.w OR context.pixels.box.max.s < context.viewPort.h THEN SIGNAL G3dRender.Error[$MisMatch, "pixel memory smaller than viewport"]; }; ValidateShape: PUBLIC ShapeProc ~ { render: REF RenderData _ G3dRender.RenderDataFrom[shape]; shapeClass: REF ShapeClass _ render.class; shadingClass: REF ShadingClass _ render.shadingClass; xfm: Matrix; IF context.stopMe^ THEN RETURN[shape]; IF context.changed OR NOT shape.renderValid THEN { xfm _ G3dMatrix.Mul[shape.matrix, context.eyeSpaceXfm]; IF shapeClass = NIL THEN -- load class { G3dRender.LoadShapeClass[shape, shape.type]; shapeClass _ render.class }; IF shadingClass = NIL THEN -- load default shading { G3dShade.LoadShadingClass[shape]; shadingClass _ render.shadingClass }; IF NOT shape.visible THEN RETURN[NIL]; -- invisible IF shape.sphereExtent.radius = 0.0 -- validate bounding Sphere THEN shape.sphereExtent _ G3dShape.BoundingSphere[ shape ]; shape.clipState _ G3dClipXfmShade.ClipBoundingSphere[context, shape, xfm]; -- clip state IF shape.clipState # out THEN [] _ shapeClass.validate[context, shape, data]; }; IF shape.clipState # out AND shape.surfaces # NIL -- clipstate can be refined in validate THEN RETURN[shape] ELSE RETURN[NIL]; }; ValidatePolyhedron: PUBLIC ShapeProc ~ { ValidateScreenCoords: PROC[] ~ { FOR i: NAT IN [0..shape.vertices.length) DO eVtx: Triple _ G3dMatrix.Transform[ shape.vertices[i].point, xfm]; eVtx _ G3dClipXfmShade.XfmPtToDisplay[ context, eVtx ]; shape.vertices[i].screen.x _ eVtx.x; shape.vertices[i].screen.y _ eVtx.y; ENDLOOP; shape.screenValid _ TRUE; }; render: REF RenderData _ G3dRender.RenderDataFrom[shape]; shapeClass: REF ShapeClass _ render.class; shadingClass: REF ShadingClass _ render.shadingClass; renderStyle: RenderStyle; xfm: Matrix _ G3dMatrix.Mul[shape.matrix, context.eyeSpaceXfm]; allPolysIn, allPolysOut: BOOLEAN _ TRUE; WITH shadingClass.renderMethod SELECT FROM style: REF RenderStyle => renderStyle _ style^; ENDCASE => renderStyle _ smooth; -- default to smooth to get normals and shading IF renderStyle # faceted THEN -- get vertex normals IF NOT shape.vertices.valid.normal THEN G3dClipXfmShade.GetVtxNmls[context, shape]; IF shape.faces = NIL THEN { -- always get face normals shape.faces _ NEW[FaceSequenceRep[shape.surfaces.length]]; shape.faces.length _ shape.surfaces.length; FOR i: NAT IN [0..shape.faces.length) DO shape.faces[i] _ NEW[FaceRep]; ENDLOOP; }; IF NOT shape.faces.valid.normal THEN G3dClipXfmShade.GetPolyNmls[context, shape]; IF NOT shape.visible THEN RETURN[shape]; -- don't bother, not visible 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].vertices.length]]; FOR j: NAT IN [0..shape.surfaces[i].vertices.length) DO vtx: NAT _ shape.surfaces[i].vertices[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].vertices.length; render.patch[i].type _ shape.type; render.patch[i].oneSided _ NOT shape.showBackfaces; render.patch[i].renderData _ render; -- store REF to shading information render.patch[i].props _ PutProp[render.patch[i].props, $Shape, shape]; render.patch[i].props _ PutProp[render.patch[i].props, $PatchNo, NEW[NAT _ i]]; ENDLOOP; }; IF renderStyle = lines AND shape.clipState = in AND render.class.display # NIL THEN { ValidateScreenCoords[]; RETURN [ shape ]; }; 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 _ G3dClipXfmShade.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 = smooth OR renderStyle = shadedLines THEN { OPEN render.patch[i][j].shade; tmpShininess: REAL; vtx: NAT _ render.patch[i][j].vtxPtr; [xn, yn, zn] _ shape.vertices[vtx].normal; [r, g, b] _ shape.vertices[vtx].color; t _ shape.vertices[vtx].transmittance; IF shape.faces # NIL THEN IF shape.faces.valid.color THEN { -- scale by face color clr: Triple _ shape.faces[i].color; r _ clr.x * r; g _ clr.y * g; b _ clr.z * b; }; [[exn, eyn, ezn]] _ G3dMatrix.TransformVec[ [xn, yn, zn] , xfm]; tmpShininess _ shadingClass.shininess; shadingClass.shininess _ 0.0; render.patch[i][j] _ shadingClass.shadeVtx[ context, render.patch[i][j], shadingClass ]; shadingClass.shininess _ tmpShininess; }; IF renderStyle = lines THEN { OPEN render.patch[i][j].shade; [er, eg, eb] _ shadingClass.color; }; 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 { eyeNorm: Triple _ G3dMatrix.TransformVec[ shape.faces[i].normal, xfm]; awayness: REAL _ G3dVector.Dot[ -- normal front or back facing? G3dVector.Unit[[render.patch[i][0].coord.ex, render.patch[i][0].coord.ey, render.patch[i][0].coord.ez]], G3dVector.Unit[eyeNorm] ]; IF render.patch[i].type = $ConvexPolygon THEN { IF awayness > 0.0 THEN render.patch[i].dir _ back ELSE IF awayness < 0.0 THEN render.patch[i].dir _ front ELSE render.patch[i].dir _ unknown; IF (render.patch[i].oneSided AND render.patch[i].dir = back) THEN LOOP; }; IF renderStyle = faceted THEN { OPEN render.patch[i][0].shade; [xn, yn, zn] _ shape.faces[i].normal; [r, g, b] _ shape.faces[i].color; t _ shape.faces[i].transmittance; [[exn, eyn, ezn]] _ G3dMatrix.TransformVec[ shape.faces[i].normal, xfm]; render.patch[i][0] _ shadingClass.shadeVtx[context, render.patch[i][0], shadingClass]; FOR j: NAT IN [1..render.patch[i].nVtces) DO render.patch[i][j].shade.exn _ render.patch[i][0].shade.exn; render.patch[i][j].shade.eyn _ render.patch[i][0].shade.eyn; render.patch[i][j].shade.ezn _ render.patch[i][0].shade.ezn; render.patch[i][j].shade.r _ render.patch[i][0].shade.r; render.patch[i][j].shade.g _ render.patch[i][0].shade.g; render.patch[i][j].shade.b _ render.patch[i][0].shade.b; render.patch[i][j].shade.t _ render.patch[i][0].shade.t; render.patch[i][j].shade.er _ render.patch[i][0].shade.er; render.patch[i][j].shade.eg _ render.patch[i][0].shade.eg; render.patch[i][j].shade.eb _ render.patch[i][0].shade.eb; render.patch[i][j].shade.et _ render.patch[i][0].shade.et; ENDLOOP; }; 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] ] _ G3dClipXfmShade.XfmPtToDisplay[ context, [ex, ey, ez], shape ]; ENDLOOP; }; ENDLOOP; IF shape.clipState = clipped THEN -- refine shape clipstate based on polygon states IF allPolysIn THEN shape.clipState _ in ELSE IF allPolysOut THEN shape.clipState _ out; render.patchesValid _ TRUE; IF shape.clipState = in AND renderStyle = lines AND render.class.display # NIL THEN ValidateScreenCoords[]; -- prepare for unclipped line drawings shape.renderValid _ TRUE; RETURN[shape]; }; DummyValidate: PUBLIC ShapeProc ~ { render: REF RenderData _ G3dRender.RenderDataFrom[shape]; render.patchesValid _ TRUE; RETURN[shape]; }; SetEyeSpace: PUBLIC PROC[ context: Context ] ~ { in, right, up, normal: Triple; mtx: Matrix; wndw: Rectangle; viewSize, xSize, ySize, aspectRatio: REAL; IF context.viewPort.h <= 0 OR context.viewPort.w <= 0 THEN RETURN[]; in _ G3dVector.Unit[G3dVector.Sub[context.lookAt, context.eyePoint]]; IF G3dVector.Null[in] THEN SIGNAL G3dRender.Error[$MisMatch, "Eye and Pt of Interest identical"]; right _ G3dVector.Unit[G3dVector.Cross[in, context.upDirection]]; IF G3dVector.Null[right] THEN right _ [1.0, 0.0, 0.0]; -- looking straight down up _ G3dVector.Unit[G3dVector.Cross[right, in]]; context.eyeSpaceXfm _ G3dMatrix.Identity[]; context.eyeSpaceXfm[0][0] _ right.x; context.eyeSpaceXfm[1][0] _ right.y; context.eyeSpaceXfm[2][0] _ right.z; context.eyeSpaceXfm[3][0] _ -G3dVector.Dot[right, context.eyePoint]; context.eyeSpaceXfm[0][1] _ up.x; context.eyeSpaceXfm[1][1] _ up.y; context.eyeSpaceXfm[2][1] _ up.z; context.eyeSpaceXfm[3][1] _ -G3dVector.Dot[up, context.eyePoint]; context.eyeSpaceXfm[0][2] _ in.x; context.eyeSpaceXfm[1][2] _ in.y; context.eyeSpaceXfm[2][2] _ in.z; context.eyeSpaceXfm[3][2] _ -G3dVector.Dot[in, context.eyePoint]; mtx _ G3dMatrix.Identity[]; -- Roll about z-axis, clockwise mtx[0][0] _ RealFns.CosDeg[context.rollAngle]; mtx[0][1] _ -RealFns.SinDeg[context.rollAngle]; mtx[1][0] _ RealFns.SinDeg[context.rollAngle]; mtx[1][1] _ RealFns.CosDeg[context.rollAngle]; context.eyeSpaceXfm _ G3dMatrix.Mul[context.eyeSpaceXfm, mtx]; IF NOT (context.window.w > 0.0 AND context.window.h > 0.0) THEN RETURN[]; aspectRatio _ context.pixelAspectRatio * (context.viewPort.w/context.viewPort.h); xSize _ IF aspectRatio > 1.0 THEN 1.0 ELSE aspectRatio; ySize _ IF aspectRatio < 1.0 THEN 1.0 ELSE 1.0 / aspectRatio; wndw.x _ MIN[xSize, MAX[-xSize, context.window.x] ]; wndw.w _ MIN[xSize-wndw.x, context.window.w ]; wndw.y _ MIN[ySize, MAX[-ySize, context.window.y] ]; wndw.h _ MIN[ySize-wndw.y, context.window.h ]; viewSize _ RealFns.TanDeg[context.fieldOfView/2.0]; -- scale factor for angle of view context.clippingPlanes[Near] _ [0., 0., 1., -context.hitherLimit]; context.clippingPlanes[Far] _ [0., 0., -1., context.yonLimit]; normal _ G3dVector.Unit[[1., 0., -(wndw.x * viewSize)]]; context.clippingPlanes[Left] _ [normal.x, 0., normal.z, 0.]; normal _ G3dVector.Unit[[-1., 0., (wndw.x + wndw.w) * viewSize]]; context.clippingPlanes[Right] _ [normal.x, 0., normal.z, 0.]; normal _ G3dVector.Unit[[0., 1., -(wndw.y * viewSize)]]; context.clippingPlanes[Bottom] _ [0., normal.y, normal.z, 0.]; normal _ G3dVector.Unit[[0., -1., (wndw.y + wndw.h) * viewSize]]; context.clippingPlanes[Top] _ [0., normal.y, normal.z, 0.]; context.eyeToNdc.scaleX _ (2.0 / wndw.w) * (1.0 / viewSize) / 2.0; context.eyeToNdc.addX _ -(wndw.x / wndw.w); -- center after perspective divide context.eyeToNdc.scaleY _ (2.0 / wndw.h) * (1.0 / viewSize) / 2.0; context.eyeToNdc.addY _ -(wndw.y / wndw.h); -- center after perspective divide context.eyeToNdc.scaleZ _ context.yonLimit / (context.yonLimit - context.hitherLimit); context.eyeToNdc.addZ _ -(context.hitherLimit*context.yonLimit) / (context.yonLimit - context.hitherLimit); }; WindowFromViewPort: PUBLIC PROC[context: Context] RETURNS[REF Rectangle] ~ { window: REF Rectangle _ NEW[Rectangle]; vp: Rectangle; IF context.viewPort = NIL OR context.viewPort.h <= 0.0 OR context.viewPort.w <= 0.0 THEN RETURN[ NIL ]; vp _ [ context.viewPort.x * context.pixelAspectRatio, context.viewPort.y, context.viewPort.w * context.pixelAspectRatio, context.viewPort.h ]; IF vp.w > vp.h THEN { window.x _ -1.0; window.w _ 2.0; window.y _ -vp.h / vp.w; window.h _ 2.0 * vp.h / vp.w; } ELSE { window.y _ -1.0; window.h _ 2.0; window.x _ -vp.w / vp.h; window.w _ 2.0 * vp.w / vp.h; }; RETURN[ window ]; }; LoadDepthSequence: PUBLIC PROC[ context: Context, sortOrder: LIST OF REF ANY _ NIL ] RETURNS[LIST OF REF ANY, CARDINAL] ~ { sortKey: NatSequence; -- 1st element of 'sortOrder', gives sort bucket index from depth buckets: SortSequence; -- 2nd element of 'sortOrder', sort buckets with linked lists of patches shape: ShapeSequence _ context.visibleShapes; patchCount, displayedPatchCount: CARDINAL _ 0; bucketListPtr: INT _ 1; -- start bucket count at 1 to allow use of zero as null minDepth: REAL _ context.yonLimit; maxDepth: REAL _ context.hitherLimit; zScale: REAL _ 1.; NewSortOrder: PROC[] ~ { buckets _ NEW[SortSequenceRep[patchCount+1]]; sortKey _ NEW[NatSequenceRep[context.depthResolution]]; sortKey.length _ context.depthResolution; }; FOR i: NAT IN [0.. shape.length) DO -- get minimum and maximum depth and patch count render: REF RenderData _ G3dRender.RenderDataFrom[shape[i]]; xfm: Matrix _ G3dMatrix.Mul[shape[i].matrix, context.eyeSpaceXfm]; radius: REAL _ shape[i].sphereExtent.radius; center: Triple _ G3dMatrix.Transform[ shape[i].sphereExtent.center, xfm]; IF center.z - radius < minDepth THEN minDepth _ center.z - radius; IF center.z + radius > maxDepth THEN maxDepth _ center.z + radius; patchCount _ patchCount + shape[i].surfaces.length; ENDLOOP; minDepth _ MAX[minDepth, 0]; -- nothing allowed behind the eyepoint IF (maxDepth - minDepth) > 0. THEN zScale _ (context.depthResolution - 1) / (maxDepth - minDepth); IF sortOrder # NIL THEN { sortKey _ NARROW[sortOrder.first]; buckets _ NARROW[sortOrder.rest.first]; } ELSE NewSortOrder[]; IF buckets.length < patchCount OR sortKey.length < context.depthResolution THEN NewSortOrder[]; -- get new storage if sizes have expanded FOR i: NAT IN [0..sortKey.length) DO sortKey[i] _ nullPtr; ENDLOOP; -- clear sort structure FOR s: NAT IN [0.. shape.length) DO -- Enter patches (by nearest z) in bucket lists patch: PatchSequence _ G3dRender.PatchesFrom[shape[s]]; FOR i: NAT IN [0..patch.length) DO IF context.stopMe^ THEN RETURN[NIL, 0]; -- shut down if stop signal received IF patch[i] # NIL AND patch[i].clipState # out THEN { -- can't be proven not visible neg, pos: BOOL _ FALSE; zNear: REAL _ maxDepth; iz: INT; FOR j: NAT IN [0..patch[i].nVtces) DO -- get minimum z-coordinate zNear _ MIN[zNear, patch[i][j].coord.ez]; ENDLOOP; iz _ INTEGER[Real.Fix[zScale *(zNear - minDepth)]]; -- get matching bucket addr. IF iz < 0 THEN iz _ 0; -- clip at zero IF NOT (patch[i].oneSided AND patch[i].dir = back) THEN { -- will be displayed bckPtr, nxtPtr: INT _ nullPtr; nxtPtr _ sortKey[iz]; WHILE nxtPtr # nullPtr AND buckets[nxtPtr].dir = front DO bckPtr _ nxtPtr; -- find right place in ordered chain, front-facing first nxtPtr _ buckets[nxtPtr].next; ENDLOOP; buckets[bucketListPtr] _ NEW[SortRecord]; buckets[bucketListPtr].patch _ patch[i]; buckets[bucketListPtr].next _ buckets[bucketListPtr].prev _ nullPtr; -- clear IF nxtPtr # nullPtr THEN { -- not at tail of list buckets[bucketListPtr].prev _ buckets[nxtPtr].prev; buckets[bucketListPtr].next _ nxtPtr; buckets[nxtPtr].prev _ bucketListPtr; }; IF bckPtr # nullPtr THEN { -- not at head of list buckets[bucketListPtr].next _ buckets[bckPtr].next; buckets[bucketListPtr].prev _ bckPtr; buckets[bckPtr].next _ bucketListPtr; }; IF bckPtr = nullPtr THEN { -- at head of list sortKey[iz] _ bucketListPtr; -- reset list header }; IF nxtPtr = nullPtr THEN -- at tail of list buckets[sortKey[iz]].prev _ bucketListPtr; -- reset tail ptr at head buckets[bucketListPtr].dir _ patch[i].dir; -- front or backfacing or unknown bucketListPtr _ bucketListPtr + 1; displayedPatchCount _ displayedPatchCount + 1; }; }; ENDLOOP; ENDLOOP; sortKey.length _ context.depthResolution; RETURN[ LIST[sortKey, buckets], displayedPatchCount ]; }; GetDepths: PROC[ context: Context] ~ { shape: ShapeSequence _ context.visibleShapes; minDepth: REAL _ context.yonLimit; maxDepth: REAL _ context.hitherLimit; FOR i: NAT IN [0.. shape.length) DO -- get minimum and maximum depth xfm: Matrix _ G3dMatrix.Mul[shape[i].matrix, context.eyeSpaceXfm]; radius: REAL _ shape[i].sphereExtent.radius; center: Triple _ shape[i].sphereExtent.center; ctrLessRadius: Triple _ [center.x, center.y - radius, center.z]; shapeClass: REF ShapeClass _ G3dRender.ShapeClassFrom[shape[i]]; center _ G3dMatrix.Transform[ center, xfm]; ctrLessRadius _ G3dMatrix.Transform[ctrLessRadius, xfm]; radius _ G3dVector.Length[G3dVector.Sub[center, ctrLessRadius]]; IF shapeClass.type # $ConvexPolygon AND shapeClass.type # $Poly THEN radius _ 1.1 * radius; -- fudge for patches IF center.z - radius < minDepth THEN minDepth _ center.z - radius; IF center.z + radius > maxDepth THEN maxDepth _ center.z + radius; ENDLOOP; minDepth _ MAX[minDepth, context.hitherLimit]; -- nothing allowed behind the eyepoint context.eyeToNdc.scaleZ _ maxDepth / (maxDepth - minDepth); context.eyeToNdc.addZ _ -minDepth*maxDepth / (maxDepth - minDepth); FOR s: NAT IN [0.. shape.length) DO render: REF RenderData _ G3dRender.RenderDataFrom[shape[s]]; FOR i: NAT IN [0.. render.patch.length) DO 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]] _ G3dClipXfmShade.XfmPtToDisplay[ context, [ex, ey, ez], shape[s] ]; ENDLOOP; ENDLOOP; ENDLOOP; }; DoBackToFront: PUBLIC PROC[ context: Context, sortInfo: LIST OF REF ANY, action: PROC[REF Patch] ] ~ { sortKey: NatSequence _ NARROW[sortInfo.first]; buckets: SortSequence _ NARROW[sortInfo.rest.first]; FOR i: NAT DECREASING IN [0..sortKey.length) DO j: NAT _ sortKey[i]; WHILE j # nullPtr DO j _ buckets[j].prev; -- jump to tail of list and work back action[buckets[j].patch]; -- call back with next polygon in order IF j = sortKey[i] THEN j _ nullPtr; -- exit if head of list reached IF context.stopMe^ THEN RETURN[]; -- shut down if stop signal received ENDLOOP; ENDLOOP; CombineBoxes[context]; -- get combined bounding box on scene }; DoFrontToBack: PUBLIC PROC[ context: Context, sortInfo: LIST OF REF ANY, action: PROC[REF Patch] ] ~ { sortKey: NatSequence _ NARROW[sortInfo.first]; buckets: SortSequence _ NARROW[sortInfo.rest.first]; FOR i: NAT IN [0..sortKey.length) DO j: NAT _ sortKey[i]; WHILE j # nullPtr DO action[buckets[j].patch]; -- call back with next polygon in order j _ buckets[j].next; IF context.stopMe^ THEN RETURN[]; -- shut down if stop signal received ENDLOOP; ENDLOOP; CombineBoxes[context]; -- get combined bounding box on scene }; DoForPatches: PUBLIC PROC[ context: Context, set: ShapeSequence, patchAction: PROC[REF Patch], shapeAction: PROC[Shape] _ NIL ] ~ { FOR s: NAT IN [0..set.length) DO IF set[s].clipState = in AND shapeAction # NIL THEN shapeAction[set[s]] ELSE { patch: PatchSequence _ G3dRender.PatchesFrom[set[s]]; FOR i: NAT IN [0..patch.length) DO IF patch[i] # NIL THEN { IF context.stopMe^ THEN RETURN[]; -- shut down if stop signal received IF patch[i].clipState # out THEN patchAction[ patch[i] ]; }; ENDLOOP; }; ENDLOOP; }; CombineBoxes: PUBLIC PROC[context: Context] ~ { -- get combined bounding box context.screenExtent _ [ [LAST[NAT], LAST[NAT]], [FIRST[NAT], FIRST[NAT]] ]; FOR i: NAT IN [0..context.visibleShapes.length) DO IF context.screenExtent.min.x > context.visibleShapes[i].screenExtent.min.x THEN context.screenExtent.min.x _ context.visibleShapes[i].screenExtent.min.x; IF context.screenExtent.max.x < context.visibleShapes[i].screenExtent.max.x THEN context.screenExtent.max.x _ context.visibleShapes[i].screenExtent.max.x; IF context.screenExtent.min.y > context.visibleShapes[i].screenExtent.min.y THEN context.screenExtent.min.y _ context.visibleShapes[i].screenExtent.min.y; IF context.screenExtent.max.y < context.visibleShapes[i].screenExtent.max.y THEN context.screenExtent.max.y _ context.visibleShapes[i].screenExtent.max.y; ENDLOOP; }; ShowOnePatch: PROC[ context: Context, shape: Shape, i: NAT] ~ { patch: PatchSequence _ G3dRender.PatchesFrom[shape]; IF patch[i].clipState # out THEN [] _ G3dRender.ShapeClassFrom[shape].displayPatch[ context, patch[i] ]; }; ShowOneVtx: PROC[ context: Context, shape: Shape, j: NAT] ~ { patch: PatchSequence _ G3dRender.PatchesFrom[shape]; FOR i: NAT IN [0..patch.length) DO containsVtx: BOOL _ FALSE; FOR k: NAT IN [0..patch[i].nVtces) DO IF patch[i][k].vtxPtr = j THEN { containsVtx _ TRUE; EXIT; }; ENDLOOP; IF containsVtx THEN IF patch[i].clipState # out THEN [] _ G3dRender.ShapeClassFrom[shape].displayPatch[ context, patch[i] ]; ENDLOOP; }; ShowObjects: PUBLIC PROC[ context: Context, frontToBack: BOOL _ FALSE ] ~ { ShowPatch: PROC[p: REF Patch] ~{ [] _ p.renderData.class.displayPatch[ context, p ]; patchCount _ patchCount + 1; IF log # NIL AND context.antiAliasing AND progressReportsPerFrame > 0 AND patchCount >= nextReport THEN { report: IO.STREAM _ OpenLogCarefully["FrameProgress.log"]; message: ROPE _ Rope.Cat[ Rope.Cat[ "\n ", Convert.RopeFromInt[patchCount], " of " ], Rope.Cat[ Convert.RopeFromInt[patchTotal], " done in ", ElapsedTime[time] ], Rope.Cat[ " at ", Convert.RopeFromTime[ from: BasicTime.Now[], start: hours, includeZone: FALSE ] ], Rope.Cat[ " est. done at ", Convert.RopeFromTime[ BasicTime.Update[ BasicTime.Now[], Real.Fix[(CurrentTime[] - time) * (patchTotal - patchCount) / patchCount] ] ] ] ]; log.PutRope[ message ]; IO.Flush[log]; report.PutRope[ message ]; IO.Close[ report]; -- make readable from elsewhere nextReport _ MIN[patchCount + patchTotal/progressReportsPerFrame, patchTotal-1]; }; }; patchCount, patchTotal, nextReport: INT _ 0; time: REAL _ CurrentTime[]; log: IO.STREAM _ NARROW[ Atom.GetPropFromList[context.props, $Log] ]; timing: ROPE; justOne: REF IntegerPair _ NARROW[ Atom.GetPropFromList[context.props, $SinglePatch] ]; oneVtx: REF IntegerPair _ NARROW[ Atom.GetPropFromList[context.props, $SingleVtx] ]; shape: ShapeSequence _ context.visibleShapes; IF shape = NIL THEN RETURN[]; IF context.depthBuffering THEN GetDepths[context]; IF justOne # NIL THEN { ShowOnePatch[context, shape[justOne.x], justOne.y]; RETURN[]; }; IF oneVtx # NIL THEN { ShowOneVtx[context, shape[oneVtx.x], oneVtx.y]; RETURN[]; }; IF context.depthBuffering AND NOT context.antiAliasing THEN DoForPatches[context, shape, ShowPatch] ELSE { IF GetProp[context.props, $SortToPriority] # NIL THEN SIGNAL G3dRender.Error[$Unimplemented, "Priority Sort under repair"] ELSE [context.sortSequence, patchTotal] _ LoadDepthSequence[context, NARROW[context.sortSequence] ]; timing _ Rope.Cat[ timing, " Sort: ", ElapsedTime[time] ]; time _ CurrentTime[]; IF context.delayClear THEN context.class.loadBackground[context]; -- load background IF context.stopMe^ THEN RETURN[]; -- shut down if stop signal received IF frontToBack THEN DoFrontToBack[context, NARROW[context.sortSequence], ShowPatch] ELSE DoBackToFront[context, NARROW[context.sortSequence], ShowPatch]; }; IF log # NIL THEN log.PutRope[ Rope.Cat[ timing, " Scan: ", ElapsedTime[time] ] ]; }; ShowWireFrameObjects: PUBLIC PROC[context: Context ] ~ { ShowPatch: PROC[p: REF Patch] ~{ [] _ p.renderData.class.displayPatch[ context, p ]; }; ShowShape: PROC[s: Shape] ~{ render: REF RenderData _ G3dRender.RenderDataFrom[s]; IF (render.class.display # NIL AND NOT context.antiAliasing) THEN [] _ render.class.display[ context, s ] ELSE OutputShape[context, s]; }; shape: G3dRender.ShapeSequence _ context.visibleShapes; justOne: REF IntegerPair _ NARROW[ Atom.GetPropFromList[context.props, $SinglePatch] ]; oneVtx: REF IntegerPair _ NARROW[ Atom.GetPropFromList[context.props, $SingleVtx] ]; IF shape = NIL THEN RETURN[]; IF context.delayClear THEN context.class.loadBackground[context]; -- load background IF justOne # NIL THEN { ShowOnePatch[context, shape[justOne.x], justOne.y]; RETURN[]; }; IF oneVtx # NIL THEN { ShowOneVtx[context, shape[oneVtx.x], oneVtx.y]; RETURN[]; }; DoForPatches[context, shape, ShowPatch, ShowShape]; CombineBoxes[context]; -- get combined bounding box on scene }; OutputShape: PROC[context: Context, shape: Shape] ~ { patch: PatchSequence _ G3dRender.PatchesFrom[shape]; IF context.stopMe^ THEN RETURN[]; -- shut down if stop signal received FOR i: NAT IN [0..patch.length) DO [] _ G3dRender.ShapeClassFrom[shape].displayPatch[ context, patch[i] ]; ENDLOOP; }; EdgesToPolygons: PatchProc ~ { MakeEdge: PROC[pt1, pt2: CtlPtInfo, edge: REF Patch] ~ { dirX, dirY, mag, offSetX1, offSetY1, offSetX2, offSetY2: REAL; dirX _ pt2.coord.ex - pt1.coord.ex; -- get unit vector in edge direction dirY _ pt2.coord.ey - pt1.coord.ey; mag _ RealFns.SqRt[ Sqr[dirX] + Sqr[dirY] ]; IF mag < G3dScanConvert.justNoticeable THEN RETURN[]; -- quit if too short to be seen dirX _ dirX / mag; dirY _ dirY / mag; offSetX1 _ dirX * baseRadius; offSetY1 _ dirY * baseRadius; offSetX2 _ dirX * baseRadius; offSetY2 _ dirY * baseRadius; FOR i: NAT IN [0..3) DO edge[i] _ pt1; ENDLOOP; FOR i: NAT IN [3..6) DO edge[i] _ pt2; ENDLOOP; edge[0].coord.ex _ edge[0].coord.ex+offSetY1; edge[0].coord.ey _ edge[0].coord.ey-offSetX1; edge[1].coord.ex _ edge[1].coord.ex-offSetX1; edge[1].coord.ey _ edge[1].coord.ey-offSetY1; edge[2].coord.ex _ edge[2].coord.ex-offSetY1; edge[2].coord.ey _ edge[2].coord.ey+offSetX1; edge[3].coord.ex _ edge[3].coord.ex-offSetY2; edge[3].coord.ey _ edge[3].coord.ey+offSetX2; edge[4].coord.ex _ edge[4].coord.ex+offSetX2; edge[4].coord.ey _ edge[4].coord.ey+offSetY2; edge[5].coord.ex _ edge[5].coord.ex+offSetY2; edge[5].coord.ey _ edge[5].coord.ey-offSetX2; IF patch.dir = back THEN FOR i: NAT IN [0..3) DO -- reverse order to make backfacing temp: CtlPtInfo _ edge[i]; edge[i] _ edge[5-i]; edge[5-i] _ temp; ENDLOOP; IF edge.clipState = in THEN FOR i: NAT IN [0..6) DO OPEN edge[i].coord; [[sx, sy, sz]] _ G3dClipXfmShade.XfmPtToDisplay[ context, [ex, ey, ez] ]; ENDLOOP; [] _ DoOutputPolygon[context, edge]; }; baseRadius: REAL _ .003; limit: NAT _ IF patch.type = $PolyLine THEN patch.nVtces - 1 ELSE patch.nVtces; { IF patch.dir = unknown THEN patch.dir _ G3dClipXfmShade.BackFacing[ context, patch ! G3dRender.Error => IF code = $Condition THEN GO TO GiveUp ]; IF patch.dir = front OR NOT patch.oneSided THEN FOR i: NAT IN [0..limit) DO edge: REF Patch _ G3dClipXfmShade.GetPatch[6]; edge.type _ $ConvexPolygon; edge.nVtces _ 6; edge.oneSided _ patch.oneSided; edge.dir _ patch.dir; edge.clipState _ patch.clipState; edge.renderData _ patch.renderData; MakeEdge[ patch[i], patch[(i+1) MOD patch.nVtces], edge ]; G3dClipXfmShade.ReleasePatch[edge]; -- end of the line for this patch ENDLOOP; EXITS GiveUp => NULL }; RETURN[patch]; }; OutputPolygon: PUBLIC PatchProc ~ { shadingClass: REF ShadingClass _ patch.renderData.shadingClass; renderStyle: RenderStyle _ NARROW[shadingClass.renderMethod, REF RenderStyle]^; inputPatch: REF Patch _ patch; IF patch = NIL OR patch.clipState = out THEN RETURN[patch]; -- reject if outside frame IF patch.type # $ConvexPolygon AND patch.type # $Poly AND patch.type # $PolyLine THEN { SIGNAL G3dRender.Error[$MisMatch, "Not a Polygon or PolyLine"]; RETURN[patch]; -- catches unexpanded patches, etc. }; IF context.antiAliasing AND -- antialiased lines ( patch.type = $PolyLine OR renderStyle = lines OR renderStyle = shadedLines ) THEN patch _ EdgesToPolygons[context, patch] ELSE patch _ DoOutputPolygon[ context, patch ]; RETURN[patch]; }; DoOutputPolygon: PROC[ context: Context, patch: REF Patch ] RETURNS[REF Patch] ~ { CopyPatch: PROC[inPatch: REF Patch] RETURNS[outPatch: REF Patch] ~ { outPatch _ G3dClipXfmShade.GetPatch[inPatch.nVtces]; outPatch.type _ inPatch.type; outPatch.oneSided _ inPatch.oneSided; outPatch.nVtces _ inPatch.nVtces; outPatch.clipState _ inPatch.clipState; outPatch.dir _ inPatch.dir; outPatch.renderData _ inPatch.renderData; outPatch.props _ inPatch.props; FOR i: NAT IN [0..inPatch.nVtces) DO outPatch[i] _ inPatch[i]; ENDLOOP; }; shadingClass: REF ShadingClass _ patch.renderData.shadingClass; renderStyle: RenderStyle _ NARROW[shadingClass.renderMethod, REF RenderStyle]^; inputPatch: REF Patch _ patch; IF patch.clipState = clipped THEN { patch _ CopyPatch[patch]; patch _ G3dClipXfmShade.ClipPoly[context, patch]; IF patch.nVtces > 2 OR ( patch.type = $PolyLine AND patch.nVtces > 1 ) THEN FOR i: NAT IN [0..patch.nVtces) DO OPEN patch[i].coord; [[sx, sy, sz]] _ G3dClipXfmShade.XfmPtToDisplay[context, [ex, ey, ez]]; ENDLOOP; }; IF patch.nVtces > 2 OR ( patch.type = $PolyLine AND patch.nVtces > 1 ) THEN { IF patch.type = $PolyLine OR renderStyle = lines THEN { [] _ context.class.displayPolygon[context, patch]; GO TO GiveUp; }; IF patch.dir = unknown THEN patch.dir _ G3dClipXfmShade.BackFacing[ context, patch ! G3dRender.Error => IF code = $Condition THEN GO TO GiveUp ]; IF patch.dir = front THEN [] _ context.class.displayPolygon[context, patch] ELSE IF patch.oneSided -- backfacing!! THEN GO TO GiveUp -- Reject if closed surface ELSE { -- make reversed copy of patch for display IF inputPatch = patch THEN patch _ CopyPatch[patch]; FOR i: NAT IN [0..patch.nVtces/2) DO -- reorder to keep clockwise on display tempVtx: CtlPtInfo _ patch[i]; patch[i] _ patch[patch.nVtces-1 - i]; patch[patch.nVtces-1 - i] _ tempVtx; ENDLOOP; IF renderStyle # hiddenLines AND renderStyle # linesWnormals THEN FOR i: NAT IN [0..patch.nVtces) DO -- recalculate shading for back side patch[i].shade.exn _ -patch[i].shade.exn; -- reverse normals patch[i].shade.eyn _ -patch[i].shade.eyn; patch[i].shade.ezn _ -patch[i].shade.ezn; patch[i] _ shadingClass.shadeVtx[context, patch[i], shadingClass]; ENDLOOP; [] _ context.class.displayPolygon[context, patch]; }; EXITS GiveUp => NULL }; IF inputPatch # patch THEN G3dClipXfmShade.ReleasePatch[patch]; RETURN[inputPatch]; }; RopeDisplay: PUBLIC PatchProc ~ { renderData: REF RenderData _ patch.renderData; shadingClass: REF ShadingClass _ patch.renderData.shadingClass; msg: ROPE _ NARROW[ GetProp[renderData.props, $RopeMessage ] ]; font: ROPE _ NARROW[ GetProp[renderData.props, $RopeFont ] ]; color: Pixel _ [ Real.Fix[shadingClass.color.R*255.0], Real.Fix[shadingClass.color.G*255.0], Real.Fix[shadingClass.color.B*255.0], 0, 0 ]; position: Triple _ [patch[0].coord.sx, patch[0].coord.sy, 0.0]; position2: Triple _ [patch[1].coord.sx, patch[1].coord.sy, 0.0]; size: REAL _ G3dVector.Length[ G3dVector.Sub[position2, position] ]; context.class.draw2DRope[ context, msg, [ position.x/context.ndcToPixels.scaleX, 1.0 - position.y/ABS[context.ndcToPixels.scaleY] ], color, size, font ]; RETURN[patch]; }; MakeFrame: PUBLIC ContextProc ~ { IF Atom.GetPropFromList[context.props, $DisableClear] # NIL THEN ShowShapes[context] ELSE MakeTheFrame[context]; }; ShowShapes: PUBLIC ContextProc ~ { delay: BOOL _ context.delayClear; context.delayClear _ FALSE; -- avoids frame clear before making new frame MakeTheFrame[context: context, clear: FALSE]; context.delayClear _ delay; }; MakeTheFrame: PROC[context: Context, clear: BOOL _ TRUE] ~ { Action: PROC ~ { allLines: BOOL _ TRUE; time: REAL _ CurrentTime[]; log: IO.STREAM _ NARROW[ Atom.GetPropFromList[context.props, $Log] ]; context.stopMe^ _ FALSE; -- get stop flag unstuck, if necessary context.imageReady _ FALSE; -- discourage use of bits by other processes context.screenExtent _ [[0,0], [0,0]]; -- clear bounding box of coverage IF log # NIL THEN { report: IO.STREAM _ OpenLogCarefully["FrameProgress.log"]; message: ROPE _ Rope.Cat[ Rope.Cat["\n Making frame ", Convert.RopeFromReal[ context.viewPort.w ]], Rope.Cat[" by ", Convert.RopeFromReal[ context.viewPort.h ]], Rope.Cat[" - Frame # ", Convert.RopeFromInt[ context.frameNumber ], "\n"] ]; log.PutRope[ message ]; IO.Flush[log]; report.PutRope[ message ]; IO.Close[ report]; -- make readable from elsewhere }; ValidateContext[context]; -- update anything changed since last frame FOR i: NAT IN [0..context.shapes.length) DO render: REF RenderData _ G3dRender.RenderDataFrom[context.shapes[i]]; IF render.class.doBeforeFrame # NIL THEN -- execute animation updates FOR doList: LIST OF ShapeProc _ render.class.doBeforeFrame, doList.rest UNTIL doList = NIL DO context.shapes[i] _ doList.first[context, context.shapes[i]]; ENDLOOP; ENDLOOP; IF log # NIL THEN log.PutRope[ Rope.Cat[ " VtxOps: ", ElapsedTime[time] ] ]; IF clear AND NOT context.delayClear THEN context.class.loadBackground[context]; -- load background IF context.visibleShapes # NIL AND context.visibleShapes.length > 0 THEN { FOR i: NAT IN [0 .. context.visibleShapes.length) DO -- all line drawing? render: REF RenderData _ G3dRender.RenderDataFrom[context.visibleShapes[i]]; IF NARROW[render.shadingClass.renderMethod, REF RenderStyle]^ # lines THEN allLines _ FALSE; ENDLOOP; IF allLines AND NOT context.antiAliasing THEN ShowWireFrameObjects[ context ] ELSE ShowObjects[ context: context, frontToBack: context.antiAliasing ]; context.imageReady _ TRUE; -- encourage use of bits by other processes IF log # NIL THEN { report: IO.STREAM _ OpenLogCarefully["FrameProgress.log"]; message: ROPE _ Rope.Cat[ Rope.Cat["\n Frame: ", ElapsedTime[time], "\n"], Rope.Cat[" Completed at: ", Convert.RopeFromTime[BasicTime.Now[]], "\n"] ]; report.PutRope[ message ]; IO.Close[ report]; log.PutRope[ Rope.Cat[" Frame: ", ElapsedTime[time], "\n"] ]; IO.Flush[log]; }; } ELSE { IF log # NIL THEN { log.PutRope[" No visible objects in frame "]; IO.Flush[log]; }; }; }; CedarProcess.DoWithPriority[background, Action]; }; END. ξG3dSortandDisplayImpl.mesa Copyright c 1984, 1986 by Xerox Corporation. All rights reserved. Last Edited by: Crow, November 6, 1989 6:19:46 pm PST Glassner, July 5, 1989 6:35:20 pm PDT Bloomenthal, June 27, 1989 12:35:17 pm PDT Internal Declarations Renamed Procedures Global Variables Utility Procedures Prevent hangups on FS.LockConflict by catching error, pausing and retrying Procedures for Updating Context Validate all shapes and get visible shapes This eventually assumes that context.viewPort # NIL PROC[context: Context, shape: Shape, data: REF] RETURNS[Shape]; Update shading and transform matrices, return visibility (based on clipping and "hidden") Load shape and shading classes Update clip state based on bounding sphere Get shading, do transforms, etc. based on shape type PROC[context: Context, shape: Shape, data: REF] RETURNS[Shape]; Update patch structure under renderData to ready everything for display, etc. Shape class, shading class, matrix, bounding Sphere, and gross clip state are expected valid Get vertex, face normals if not yet present Create or update patch-by-patch description Build patch sequence for shape Store REF to parent shape and polygon number in object Patches built, update shading, transformed vertices, clip state Catch unclipped line drawings and expedite Transform and shade vertices, get clip state for each polygon If vertex normals used for shading, transform and get shading Cache shininess to get vertex shades without highlights Plain lines take object color Collect clipping data for vertices to tag patches, patches to tag shapes Check for backfacing Update polygons for faceted shading Get screen coordinates for on-screen polygons Refine shape clipstate based on polygon state PROC[context: Context, shape: Shape, data: REF] RETURNS[Shape]; Do what always must be done (precious little, it turns out) transform from world to eye unit not really needed (to avoid roundoff problems) bound window by -1.0 < x, y < 1.0 generate clipping planes unit plane equation for true distances compute the transformation from the eye coord sys to unit display coordinates (0.1.) after perspective divide Fits window to shape of viewport Simple Depth Sort Builds front-to-back sorted array of buckets, bidirectional linked lists within buckets Maintain doubly-linked list with back pointer to tail at list head Procedures for Display Calculate depths scaled over max and min depth for depth-buffering Warning!! Will not work with anisotropic scaling replace effective hither and yon stretching (min-max) over (0.0-1.0) [context.sortSequence, patchTotal] _ LoadPrioritySequence[context, NARROW[context.sortSequence] ] Unclipped shape output PROC[ context: Context, patch: REF Patch, data: REF ANY _ NIL ] RETURNS[REF Patch] Make a polygon for each edge and tile it, this causes shading as seen from outside PROC[ context: Context, patch: REF Patch, data: REF ANY _ NIL ] RETURNS[REF Patch] PROC[ context: Context, patch: REF Patch, data: REF ANY _ NIL ] RETURNS[REF Patch] IF context.antiAliasing -- ensure entire rope represented THEN shape.screenExtent.max.x _ Real.Round[context.viewPort.w]; Frame Generation and Animation Makes image without clearing frame, for compositing contexts, etc. Makes image, clears frame, etc. Κ)˜™Jšœ Οmœ7™BJšœ5™5Icode™%Kšœ*™*J˜JšΟk œΉžœ˜γJ˜—head2šΟnœžœž˜$Iašžœ–žœ˜ΎMšžœ˜J˜Jšœžœ˜ —headšΟl™Jšžœžœžœ˜Jšœ žœ˜%Jšœžœ˜,Jšžœžœ žœΟc˜:Jšœ žœ˜"Jšœžœ˜,Jšœ žœ˜ Jšœ žœ˜Jšœ žœ˜(Jšœ žœ˜'Jšœ žœ˜%Jšœ žœ˜#Jšœžœ˜,Jšœžœ˜1Jšœ žœ˜ Jšœ žœ˜%Jšœžœ˜.Jšœžœ˜3Jšœ žœ˜$Jšœžœ˜1Jšœ žœ˜(Jšœ žœ˜"Jšœ žœ˜(Jšœžœ˜/Jšœžœ˜4Jšœžœ ˜2Jšœžœ"˜5Jšœžœ%˜:Jšœ žœ˜'Jšœžœ˜4Jšœžœ˜)Jšœžœ˜/Jšœžœ˜6Jšœ žœ˜%Jšœžœ˜2Jšœ žœ˜$Jšœ žœ-˜J™JJšœ žœžœ˜Jšœžœž˜šžœ ‘(˜7šœ žœ ˜Jšœ˜šœžœ žœ‘˜DJšžœ žœžœ˜!—Jšœ˜—Jšžœžœ žœžœ˜Jšœ‘&˜CJšžœ˜—Jšžœ ˜J˜——š ™šŸœžœžœ˜4Mšœžœ˜š žœžœžœžœžœžœ˜LMšžœžœ1˜<—Mšžœžœžœ‘$˜YMšžœžœžœ1˜Lšžœžœžœžœ˜3Mšžœžœžœ%˜AMšžœ%˜)—šžœžœžœžœ˜3Mšžœ>˜BMšžœ@˜D—Jšžœžœžœ,˜Ošžœžœ˜Jšœ‘˜4šžœžœžœ"ž‘$˜Ušœ:˜:Jšœ5˜5Jšœ˜—Jšžœ˜—J˜—J™Jšœ,™,šžœžœžœ˜J˜šžœžœžœ6˜WJšžœžœ,˜L—šžœžœžœžœ˜,Jšœ9˜9Jšžœ žœžœ7˜JJšžœ˜—O˜!O˜—Ošœžœ˜Ošœ˜—šŸœžœžœ˜4Mšœ0ž™3šžœžœ˜ J˜'Jšœžœ˜šœ‘1˜HJ˜J˜J˜J˜!J˜J˜!J˜!J˜J˜—šžœžœ˜Ošžœ0‘˜E—J˜—Ošœ'‘7˜^Mšœžœ˜O˜—šŸ œžœžœ˜1š žœžœžœžœžœ˜8Oš œžœžœžœžœ˜5šœžœ˜'O˜@O˜—šžœ žœ˜Ošžœ˜šžœžœžœ˜Ošžœžœ/˜:——šžœžœžœžœ˜7O˜GO˜—˜2Jšœ-‘%˜RJ˜—šœ‘0˜IJšœ3žœžœžœ˜CJšœ(‘#˜KJ˜—š žœžœžœ%žœ‘˜LO˜[Ošžœ˜—šžœ$˜&OšžœI˜M—O˜—šžœžœžœ˜O˜-Ošœžœ˜O˜—šžœ˜OšžœΔ˜Θ—Ošœ ‘2˜>šžœžœžœ$ž˜Cšœžœ/˜2Ošžœ/˜1OšžœžœB˜M——O˜—šŸ œžœ˜#Jšžœ'žœžœ™?O™YOšœžœ.˜9Jšœ žœ˜*Jšœžœ$˜5Jšœ ˜ Ošžœžœžœ˜&šžœžœžœžœ˜2šœ7˜7OšΟb™—šžœžœžœ ‘ ˜.OšœM˜M—šžœžœžœ‘˜9OšœK˜K—š žœžœžœžœžœ‘ ˜3Oš€*™*—šžœ(‘˜EOšžœ7˜;—šœK‘ ˜XJš€4™4—Jšžœžœ0˜MJ˜—šžœžœžœ‘'˜[Jš žœžœžœžœžœ˜$—J˜—šŸœžœ˜(Jšžœ'žœžœ™?O™MJ™\šŸœžœ˜ šžœžœžœž˜+JšœB˜BJšœ7˜7JšœL˜LJšžœ˜—Jšœžœ˜J˜—Jšœžœ.˜9Jšœ žœ˜*Jšœžœ$˜5Jšœ˜Jšœ?˜?Jšœžœžœ˜(J˜šžœžœž˜*Jšœžœ%˜/Jšžœ‘/˜R—˜Jš€+™+—šžœžœ‘˜:Jšžœžœ‘žœ,˜SJ˜—šžœžœžœ‘˜;Jšœžœ)˜:Jšœ+˜+Jš žœžœžœžœžœ žœ˜PJšœ˜—Jšžœžœžœ.˜RJš žœžœžœžœ ‘˜E˜Jš€+™+—Jšžœžœžœ˜š€™Jšœžœ*˜˜J™"—Jš žœžœžœžœžœ˜IJ˜QJšœžœžœžœ ˜7Jšœžœžœžœ˜=Jšœ žœžœ˜4Jšœ žœ"˜.Jšœ žœžœ˜4Jšœ žœ#˜0šœ4‘!œ˜VJ™—J˜B˜>Jšœ&™&—J˜8J˜J˜AJ˜;J˜J™nJ˜BJšœ-‘"˜OJ˜BJšœ-‘"˜OJ˜VJ˜yJ˜—š Ÿœžœžœžœžœ˜UO™ Ošœžœ žœ ˜'O˜šžœžœžœžœ˜TOšžœžœžœ˜—O˜“šžœ ˜šžœ˜O˜%O˜;O˜—šžœ˜ O˜%O˜;O˜——Ošžœ ˜O˜——š ™šŸœžœžœžœžœžœžœžœ žœžœžœžœžœžœ˜‚M™WMšœ‘A˜WMšœ‘H˜_Mšœ-˜-Mšœ!žœ˜.Mšœžœ‘7˜QMšœ žœ˜"Mšœ žœ˜%Mšœžœ˜šŸ œžœ˜Mšœ žœ ˜-Mšœ žœ*˜7M˜)M˜—š žœžœžœžœ‘0˜TJšœžœ1˜M˜M˜—šŸ œžœžœ)žœžœžœžœžœžœ ˜{Mšœžœ˜.Mšœžœ˜4šžœžœžœžœ˜%Mšœžœ˜šžœ ž˜Mšœ‘'˜FM˜Mšžœžœžœ‘$˜FMšžœ˜—Mšžœ˜—Jšœ‘%˜>M˜M™—šŸ œžœžœ@žœžœ!žœ žœ˜™šžœžœžœžœ˜#šžœžœžœ˜/Mšžœ˜šžœ˜Jšœ5˜5šžœžœžœžœžœ žœžœ˜˜IJšœRžœ™pJšžœQžœ˜t—JšœS˜SMšžœžœ(‘˜TJšžœžœžœ‘$˜Gšžœ ˜Jšžœžœ"˜DJšžœžœ#˜E—J˜——JšžœžœžœA˜RJ˜J˜—šŸœžœžœ˜8JšŸ œžœžœC˜YšŸ œžœ ˜Jšœžœ*˜5šžœžœžœžœ˜=Jšžœ)˜-Jšžœ˜—J˜—Mšœ7˜7Mšœ žœžœ6˜WMšœžœžœ4˜TMšžœ žœžœžœ˜Mšžœžœ)‘˜Ušžœ žœ˜Jšžœ;žœ˜L—šžœ žœ˜Jšžœ7žœ˜H—J˜3Jšœ‘%˜>J˜J˜—šŸ œžœ%˜6Jšœ™Jšœ4˜4Jšžœžœžœ‘$˜Gšžœžœžœžœ˜$JšœI˜IJšžœ˜—J˜—O˜šŸœ˜Ošžœžœžœžœžœžœžœ™RJ™RšŸœžœžœ ˜8Jšœ9žœ˜>Jšœ(‘(˜PJ˜#J˜,Jšžœ%žœžœ‘˜WJ˜.J˜š žœžœžœžœžœ‘$˜LOšœ+‘˜>Ošœ)˜)Ošœ)˜)OšœB˜BOšžœ˜——Ošœ2˜2Ošœ˜———Ošžœ ž˜O˜—Ošžœžœ%˜?Ošžœ ˜O˜—šŸ œžœ˜!Jšœ žœ˜.Jšœžœ.˜?Jšœžœžœ-˜?Jšœžœžœ*˜=˜Jšœ&˜&Jšœ&˜&Jšœ&˜&J˜—Jšœ?˜?JšœA˜AJšœžœ:˜Dšžœ‘!™BJšžœ;™?—šœ˜J˜Jšœ:žœ ˜]J˜J˜—Ošžœ˜J˜—O˜—š ™šŸ œžœ˜!šžœ6žœ˜˜>JšœI˜IJšœ˜—Jšœžœ ˜(Jšœžœ‘˜OJšœ˜—Ošœ‘+˜Išžœžœžœžœ˜,Jšœžœ:˜Ešžœžœžœ‘˜GJšžœ žœžœ5˜Hšžœ žœžœ˜J˜=Jšžœ˜——Jšžœ˜—Jšžœžœžœ;˜Lšžœžœžœ˜$Mšžœ-‘˜C— šžœžœžœ"˜D šžœ˜ š žœžœžœ%žœ‘˜NJšœžœA˜L šžœžœ#žœ˜FQšžœ žœ˜—Qšžœ˜—šžœ žœžœ˜(Jšžœ ˜$JšžœF˜J—Jšœžœ‘+˜Išžœžœžœ˜Jšœžœžœ)˜:šœ žœ ˜Jšœ0˜0JšœI˜IJšœ˜—Jšœžœ˜/Jšœ=ž œ ˜SJ˜—J˜—šžœ˜Jšžœžœžœ1žœ˜VJ˜——J˜—J˜0J˜—J˜—Jšžœ˜K™—…—€lΪv