DIRECTORY Atom USING [GetPropFromList, PutPropOnList], Real USING [RoundC, Fix, FixI, FixC], RealFns USING [SqRt], Basics USING [BITOR, BITAND], Rope USING [ROPE], IO USING [STREAM, GetInt], Imager USING [Context, MaskVectorI], ImagerColor USING [RGB], Pixels USING [GetSampleSet, SampleSet], ScanConvert USING [PutLine, justNoticeable], Vector3d USING [Dot, Triple, Quad, Normalize, Cross, Add], Plane3d USING [DistanceToPt], ThreeDMisc USING [GetMappedColor, GetImagerContext, GetBackground, SetNamedColor], ThreeDScenes USING [AllOut, ClipState, Context, Error, GetShading, GetVtxShades, NoneOut, OutCode, PutShading, ReadColors, ReadVertexCoords, SetUpStandardFile, ShadePt, ShadingSequence, ShadingValue, ShapeInstance, ShapeSequence, SixSides, Vertex, VertexInfo, VertexInfoSequence, VertexSequence, XfmPtToDisplay, XfmToDisplay, XfmToEyeSpace], Tilers USING [PolygonTiler], ThreeDSurfaces USING [Patch, PatchProcs, PtrPatchSequence, PtrPatch, PatchSequence, SortSequence, ShapePatch]; ThreeDSurfacesImpl: CEDAR PROGRAM IMPORTS Atom, Real, RealFns, IO, Basics, ThreeDScenes, Tilers, Vector3d, Plane3d, ScanConvert, ThreeDMisc, Pixels, Imager EXPORTS ThreeDSurfaces = BEGIN Context: TYPE ~ ThreeDScenes.Context; RGB: TYPE ~ ImagerColor.RGB; -- [ r, g, b: REAL]; SixSides: TYPE ~ ThreeDScenes.SixSides; Triple: TYPE ~ Vector3d.Triple; SampleSet: TYPE ~ Pixels.SampleSet; ShapeInstance: TYPE ~ ThreeDScenes.ShapeInstance; Patch: TYPE ~ ThreeDSurfaces.Patch; PatchSequence: TYPE ~ ThreeDSurfaces.PatchSequence; PtrPatch: TYPE ~ ThreeDSurfaces.PtrPatch; PtrPatchSequence: TYPE ~ ThreeDSurfaces.PtrPatchSequence; ShapePatch: TYPE ~ ThreeDSurfaces.ShapePatch; Vertex: TYPE ~ ThreeDScenes.Vertex; VertexSequence: TYPE ~ ThreeDScenes.VertexSequence; ShadingValue: TYPE ~ ThreeDScenes.ShadingValue; ShadingSequence: TYPE ~ ThreeDScenes.ShadingSequence; tempPatch, tempPatch2: REF Patch _ NIL; -- temps for clipping, etc. pixelBytes: SampleSet _ Pixels.GetSampleSet[1]; -- cache for pixel size stopMe: BOOLEAN _ FALSE; Sqr: PROCEDURE [number: REAL] RETURNS [REAL] ~ INLINE { RETURN[number * number]; }; Sgn: PROCEDURE [number: REAL] RETURNS [REAL] ~ INLINE { IF number < 0. THEN RETURN[-1.] ELSE RETURN[1.]; }; DiffPosns: PROC[vtx1, vtx2: REF Vertex] RETURNS[Triple] ~ { RETURN[[vtx1.x - vtx2.x, vtx1.y - vtx2.y, vtx1.z - vtx2.z]] }; HoldEverything: PROCEDURE [] ~ { stopMe _ FALSE; ERROR ABORTED; }; EnableDisplay: PUBLIC PROC [] ~ { stopMe _ FALSE; }; StopDisplay: PUBLIC PROC [] ~ { stopMe _ TRUE; }; ShapetoColorBytes: PROC [context: REF Context, s: REF ShapeInstance] RETURNS[SampleSet] ~ { ref: REF ANY _ ThreeDScenes.GetShading[ s, $Color]; r, g, b: REAL; ir, ig, ib: INTEGER; addOn: NAT _ 0; IF context.alphaBuffer THEN addOn _ addOn + 1; IF context.depthBuffer THEN addOn _ addOn + 1; IF ref # NIL THEN { [r, g, b] _ NARROW[ ref, REF RGB]^; -- shape color ir _ Real.RoundC[r*255.0]; ig _ Real.RoundC[g*255.0]; ib _ Real.RoundC[b*255.0]; } ELSE ir _ ig _ ib _ 255; SELECT context.renderMode FROM -- convert color to byte sequence $LF => { clr: RGB _ ThreeDMisc.GetBackground[context]; IF (255.0 * (clr.R + clr.G + clr.B) / 3) > 128 THEN pixelBytes[0] _ 255 ELSE pixelBytes[0] _ 0; -- ensure contrast with backgrnd }; $Dithered, $PseudoClr => { IF pixelBytes.length < 1+addOn THEN pixelBytes _ Pixels.GetSampleSet[1+addOn]; pixelBytes[0] _ ThreeDMisc.GetMappedColor[context, [ir, ig, ib] ]; IF addOn > 0 THEN pixelBytes[1] _ 0; IF addOn > 1 THEN pixelBytes[2] _ 0; }; $Grey => { IF pixelBytes.length < 1+addOn THEN pixelBytes _ Pixels.GetSampleSet[1+addOn]; pixelBytes[0] _ (ir + ig + ib)/3; IF addOn > 0 THEN pixelBytes[1] _ 0; IF addOn > 1 THEN pixelBytes[2] _ 0; }; $FullClr, $Dorado24 => { IF pixelBytes.length < 3+addOn THEN pixelBytes _ Pixels.GetSampleSet[3+addOn]; pixelBytes[0] _ ir; pixelBytes[1] _ ig; pixelBytes[2] _ ib; IF addOn > 0 THEN pixelBytes[3] _ 0; IF addOn > 1 THEN pixelBytes[4] _ 0; }; ENDCASE => SIGNAL ThreeDScenes.Error[[$Unimplemented, "Unknown renderMode"]]; RETURN[pixelBytes]; }; CombineBoxes: PROC[context: REF Context] ~ { -- get combined bounding box context.extentCovered _ [LAST[NAT], 0, LAST[NAT], 0]; FOR i: NAT IN [0..context.shapes.length) DO IF context.shapes[i].clipState # out THEN { IF context.extentCovered.left > context.shapes[i].screenExtent.left THEN context.extentCovered.left _ context.shapes[i].screenExtent.left; IF context.extentCovered.right < context.shapes[i].screenExtent.right THEN context.extentCovered.right _ context.shapes[i].screenExtent.right; IF context.extentCovered.bottom > context.shapes[i].screenExtent.bottom THEN context.extentCovered.bottom _ context.shapes[i].screenExtent.bottom; IF context.extentCovered.top < context.shapes[i].screenExtent.top THEN context.extentCovered.top _ context.shapes[i].screenExtent.top; }; ENDLOOP; }; ShapePatchToPatch: PUBLIC PROC[context: REF Context, sPatch: REF ThreeDSurfaces.ShapePatch] RETURNS [patch: REF Patch] ~ { ptrPatch: REF PtrPatch _ NARROW[sPatch.shape.surface, REF PtrPatchSequence][sPatch.patch]; vertex: REF ThreeDScenes.VertexSequence _ sPatch.shape.vertex; shade: REF ThreeDScenes.ShadingSequence _ sPatch.shape.shade; faceted: BOOLEAN _ NARROW[ ThreeDScenes.GetShading[ sPatch.shape, $Type ], ATOM ] = $Faceted; lines: BOOLEAN _ NARROW[ ThreeDScenes.GetShading[ sPatch.shape, $Type ], ATOM ] = $Lines; patchInfo: REF ThreeDScenes.ShadingSequence _ NARROW[ ThreeDScenes.GetShading[ sPatch.shape, $PatchColors ] ]; coloredPatches: BOOLEAN _ IF patchInfo # NIL AND NOT faceted THEN ThreeDScenes.GetShading[ sPatch.shape, $PatchColorFile ] # NIL ELSE FALSE; IF ptrPatch.clipState = out THEN RETURN[NIL]; -- reject if outside frame patch _ tempPatch; -- get global temp storage for patch IF (patch = NIL) OR (patch.length < 2 * ptrPatch.nVtces) THEN patch _ tempPatch _ NEW[Patch[2 * ptrPatch.nVtces] ]; patch.nVtces _ ptrPatch.nVtces; patch.clipState _ ptrPatch.clipState; patch.type _ ptrPatch.type; patch.oneSided _ ptrPatch.oneSided; patch.props _ sPatch.shape.shadingProps; -- transmit shading info FOR i: NAT IN [0..ptrPatch.nVtces) DO j: NAT _ ptrPatch.vtxPtr[i]; patch[i].coord _ vertex[j]^; patch[i].shade _ shade[j]^; IF lines THEN { -- lines use unshaded values patch[i].shade.ir _ Real.FixC[patch[i].shade.r * 255.0]; patch[i].shade.ig _ Real.FixC[patch[i].shade.g * 255.0]; patch[i].shade.ib _ Real.FixC[patch[i].shade.b * 255.0]; } ELSE IF patchInfo # NIL THEN -- shades for individual patches IF faceted THEN patch[i].shade _ patchInfo[sPatch.patch]^ ELSE IF coloredPatches THEN { patch[i].shade.ir _ Real.FixC[ patch[i].shade.ir * patchInfo[sPatch.patch].r ]; patch[i].shade.ig _ Real.FixC[ patch[i].shade.ig * patchInfo[sPatch.patch].g ]; patch[i].shade.ib _ Real.FixC[ patch[i].shade.ib * patchInfo[sPatch.patch].b ]; patch[i].shade.it _ Real.FixC[ patch[i].shade.it * patchInfo[sPatch.patch].t ]; IF context.alphaBuffer THEN { -- possible pixel by pixel shading patch[i].shade.r _ shade[j].r * patchInfo[sPatch.patch].r; patch[i].shade.g _ shade[j].g * patchInfo[sPatch.patch].g; patch[i].shade.b _ shade[j].b * patchInfo[sPatch.patch].b; patch[i].shade.t _ shade[j].t * patchInfo[sPatch.patch].t; }; }; ENDLOOP; }; LoadShape: PUBLIC PROC[ shape: REF ShapeInstance, fileName: Rope.ROPE, type: ATOM _ $ConvexPolygon, insideVisible: BOOLEAN _ FALSE ] ~ { numVtces: NAT; min, max: Triple; radius: REAL; nullVertex: ThreeDScenes.Vertex; -- gets initialized vertex record nullShade: ThreeDScenes.ShadingValue; -- gets initialized ShadingValue record input: IO.STREAM; [input, numVtces] _ ThreeDScenes.SetUpStandardFile[fileName]; shape.numSurfaces _ IO.GetInt[input]; shape.type _ type; shape.insideVisible _ insideVisible; shape.vertex _ NEW[ ThreeDScenes.VertexSequence[numVtces] ]; shape.shade _ NEW[ ThreeDScenes.ShadingSequence[numVtces] ]; FOR i: NAT IN [0..numVtces) DO -- initialize sequences (compiler should do this (grump)) shape.vertex[i] _ NEW[ ThreeDScenes.Vertex _ nullVertex ]; shape.shade[i] _ NEW[ ThreeDScenes.ShadingValue _ nullShade]; ENDLOOP; ThreeDScenes.PutShading[shape, $Color, NEW[ RGB _ [0.7, 0.7, 0.7] ] ]; -- default grey ThreeDScenes.ReadVertexCoords[shape.vertex, input, numVtces]; -- read vertices ReadPatches[shape, input, shape.numSurfaces, shape.type, insideVisible]; min _ max _ [shape.vertex[0].x, shape.vertex[0].y, 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] # NIL THEN { IF shape.vertex[i].x < min.x THEN min.x _ shape.vertex[i].x ELSE IF shape.vertex[i].x > max.x THEN max.x _ shape.vertex[i].x; IF shape.vertex[i].y < min.y THEN min.y _ shape.vertex[i].y ELSE IF shape.vertex[i].y > max.y THEN max.y _ shape.vertex[i].y; IF shape.vertex[i].z < min.z THEN min.z _ shape.vertex[i].z ELSE IF shape.vertex[i].x > max.z THEN max.z _ shape.vertex[i].z; }; ENDLOOP; shape.centroid.x _ (min.x + max.x) / 2; -- get middle point in each coordinate shape.centroid.y _ (min.y + max.y) / 2; shape.centroid.z _ (min.z + max.z) / 2; shape.boundingRadius _ 0.; FOR i: NAT IN [0..numVtces) DO -- find radius radius _ RealFns.SqRt[ Sqr[shape.vertex[i].x - shape.centroid.x] + Sqr[shape.vertex[i].y - shape.centroid.y] + Sqr[shape.vertex[i].z - shape.centroid.z] ]; IF radius > shape.boundingRadius THEN shape.boundingRadius _ radius; ENDLOOP; }; ReadPatches: PUBLIC PROC[shape: REF ShapeInstance, in: IO.STREAM, nPatches: NAT, type: ATOM, insideVisible: BOOLEAN _ FALSE] ~ { surface: REF PtrPatchSequence; shape.surface _ NEW[ PtrPatchSequence[shape.numSurfaces] ]; surface _ NARROW[shape.surface, REF PtrPatchSequence]; FOR i: NAT IN [0..nPatches) DO -- Read Patchs nVtces: NAT _ IO.GetInt[in]; surface[i] _ NEW[PtrPatch[nVtces]]; surface[i].nVtces _ nVtces; surface[i].type _ type; surface[i].oneSided _ NOT insideVisible; FOR j: NAT IN [0..nVtces) DO surface[i].vtxPtr[j] _ IO.GetInt[in] - 1; -- switch to counting from zero ENDLOOP; ENDLOOP; }; GetPatchColors: PUBLIC PROC[shape: REF ShapeInstance, fileName: Rope.ROPE] ~ { in: IO.STREAM; nPolys: NAT; patchInfo: REF ThreeDScenes.ShadingSequence _ NARROW[ ThreeDScenes.GetShading[ shape, $PatchColors ] ]; [in, nPolys] _ ThreeDScenes.SetUpStandardFile[fileName]; IF patchInfo = NIL THEN patchInfo _ NEW[ThreeDScenes.ShadingSequence[nPolys] ]; ThreeDScenes.ReadColors[patchInfo, in, nPolys]; ThreeDScenes.PutShading[ shape, $PatchColors, patchInfo ]; ThreeDScenes.PutShading[ shape, $PatchColorFile, fileName ]; shape.shadingInValid _ TRUE; }; ClipPoly: PUBLIC PROC[ context: REF Context, poly: REF Patch] RETURNS [REF Patch] ~ { Clip: PROC[side: SixSides, pIn, pOut: REF Patch] RETURNS [REF Patch, REF Patch] = { Dist: PROC[side: SixSides, vtx: Vertex] RETURNS [REAL] = { -- + inside, - outside RETURN[ Plane3d.DistanceToPt[ [vtx.ex, vtx.ey, vtx.ez], context.clippingPlanes[side] ] ]; }; lastDist, dist: REAL; outCnt, last: NAT; IF pIn.nVtces < 3 THEN RETURN[ pIn, pOut ]; -- return if degenerate outCnt _ 0; IF pIn.type # $Path THEN { lastDist _ Dist[side, pIn.vtx[pIn.nVtces - 1].coord]; last _ pIn.nVtces - 1; }; IF pOut.length < 2 * pIn.nVtces THEN pOut _ NEW[Patch[2 * pIn.nVtces]]; FOR i: NAT IN [0..pIn.nVtces) DO a, b: REAL; dist _ Dist[side, pIn.vtx[i].coord]; IF (i # 0 OR pIn.type # $Path) AND lastDist * dist < 0. THEN { b _ dist / (dist - lastDist); a _ 1.0 - b; pOut.vtx[outCnt].coord.x _ pIn.vtx[i].coord.x * a + pIn.vtx[last].coord.x * b; pOut.vtx[outCnt].coord.y _ pIn.vtx[i].coord.y * a + pIn.vtx[last].coord.y * b; pOut.vtx[outCnt].coord.z _ pIn.vtx[i].coord.z * a + pIn.vtx[last].coord.z * b; pOut.vtx[outCnt].coord.ex _ pIn.vtx[i].coord.ex * a + pIn.vtx[last].coord.ex * b; pOut.vtx[outCnt].coord.ey _ pIn.vtx[i].coord.ey * a + pIn.vtx[last].coord.ey * b; pOut.vtx[outCnt].coord.ez _ pIn.vtx[i].coord.ez * a + pIn.vtx[last].coord.ez * b; pOut.vtx[outCnt].shade.r _ pIn.vtx[i].shade.r * a + pIn.vtx[last].shade.r * b; pOut.vtx[outCnt].shade.g _ pIn.vtx[i].shade.g * a + pIn.vtx[last].shade.g * b; pOut.vtx[outCnt].shade.b _ pIn.vtx[i].shade.b * a + pIn.vtx[last].shade.b * b; pOut.vtx[outCnt].shade.t _ pIn.vtx[i].shade.t * a + pIn.vtx[last].shade.t * b; pOut.vtx[outCnt].shade.ir _ Real.RoundC[ pIn.vtx[i].shade.ir * a + pIn.vtx[last].shade.ir * b ]; pOut.vtx[outCnt].shade.ig _ Real.RoundC[ pIn.vtx[i].shade.ig * a + pIn.vtx[last].shade.ig * b ]; pOut.vtx[outCnt].shade.ib _ Real.RoundC[ pIn.vtx[i].shade.ib * a + pIn.vtx[last].shade.ib * b ]; pOut.vtx[outCnt].shade.it _ Real.RoundC[ pIn.vtx[i].shade.it * a + pIn.vtx[last].shade.it * b ]; pOut.vtx[outCnt].shade.txtrX _ pIn.vtx[i].shade.txtrX * a + pIn.vtx[last].shade.txtrX * b; pOut.vtx[outCnt].shade.txtrY _ pIn.vtx[i].shade.txtrY * a + pIn.vtx[last].shade.txtrY * b; pOut.vtx[outCnt].shade.exn _ pIn.vtx[i].shade.exn*a + pIn.vtx[last].shade.exn*b; pOut.vtx[outCnt].shade.eyn _ pIn.vtx[i].shade.eyn*a + pIn.vtx[last].shade.eyn*b; pOut.vtx[outCnt].shade.ezn _ pIn.vtx[i].shade.ezn*a + pIn.vtx[last].shade.ezn*b; outCnt _ outCnt + 1; }; IF dist >= 0. THEN { -- put out point if inside pOut.vtx[outCnt].coord.x _ pIn.vtx[i].coord.x; pOut.vtx[outCnt].coord.y _ pIn.vtx[i].coord.y; pOut.vtx[outCnt].coord.z _ pIn.vtx[i].coord.z; pOut.vtx[outCnt].coord.ex _ pIn.vtx[i].coord.ex; pOut.vtx[outCnt].coord.ey _ pIn.vtx[i].coord.ey; pOut.vtx[outCnt].coord.ez _ pIn.vtx[i].coord.ez; pOut.vtx[outCnt].shade.r _ pIn.vtx[i].shade.r; pOut.vtx[outCnt].shade.g _ pIn.vtx[i].shade.g; pOut.vtx[outCnt].shade.b _ pIn.vtx[i].shade.b; pOut.vtx[outCnt].shade.t _ pIn.vtx[i].shade.t; pOut.vtx[outCnt].shade.ir _ pIn.vtx[i].shade.ir; pOut.vtx[outCnt].shade.ig _ pIn.vtx[i].shade.ig; pOut.vtx[outCnt].shade.ib _ pIn.vtx[i].shade.ib; pOut.vtx[outCnt].shade.it _ pIn.vtx[i].shade.it; pOut.vtx[outCnt].shade.txtrX _ pIn.vtx[i].shade.txtrX; pOut.vtx[outCnt].shade.txtrY _ pIn.vtx[i].shade.txtrY; pOut.vtx[outCnt].shade.exn _ pIn.vtx[i].shade.exn; pOut.vtx[outCnt].shade.eyn _ pIn.vtx[i].shade.eyn; pOut.vtx[outCnt].shade.ezn _ pIn.vtx[i].shade.ezn; outCnt _ outCnt + 1; }; lastDist _ dist; last _ i; ENDLOOP; pOut.type _ pIn.type; pOut.oneSided _ pIn.oneSided; pOut.nVtces _ outCnt; pOut.props _ pIn.props; RETURN [ pOut, pIn ]; }; -- end Clip Proc orOfCodes: ThreeDScenes.OutCode _ ThreeDScenes.NoneOut; poly2: REF Patch _ tempPatch2; -- get global temp storage for patch IF poly.type # $ConvexPolygon AND poly.type # $Path THEN SIGNAL ThreeDScenes.Error[[$Unimplemented, "Not clippable"]]; FOR i: NAT IN [0..poly.nVtces) DO orOfCodes _ LOOPHOLE[ Basics.BITOR[LOOPHOLE[orOfCodes], LOOPHOLE[poly.vtx[i].coord.clip]], ThreeDScenes.OutCode]; ENDLOOP; IF (poly2 = NIL) OR (poly2.length < 2 * poly.nVtces) THEN poly2 _ tempPatch2 _ NEW[Patch[2 * poly.nVtces] ]; IF orOfCodes.near THEN [poly, poly2] _ Clip[ Near, poly, poly2]; IF orOfCodes.far THEN [poly, poly2] _ Clip[ Far, poly, poly2]; IF orOfCodes.left THEN [poly, poly2] _ Clip[ Left, poly, poly2]; IF orOfCodes.right THEN [poly, poly2] _ Clip[ Right, poly, poly2]; IF orOfCodes.bottom THEN [poly, poly2] _ Clip[Bottom, poly, poly2]; IF orOfCodes.top THEN [poly, poly2] _ Clip[ Top, poly, poly2]; RETURN[ poly ]; }; GetPatchClipState: PUBLIC PROC[ patch: REF Patch] ~ { orOfCodes: ThreeDScenes.OutCode _ ThreeDScenes.NoneOut; andOfCodes: ThreeDScenes.OutCode _ ThreeDScenes.AllOut; FOR i: NAT IN [0..patch.nVtces) DO orOfCodes _ LOOPHOLE[ Basics.BITOR[ LOOPHOLE[orOfCodes], LOOPHOLE[patch[i].coord.clip]], ThreeDScenes.OutCode]; andOfCodes _ LOOPHOLE[ Basics.BITAND[LOOPHOLE[andOfCodes], LOOPHOLE[patch[i].coord.clip]], ThreeDScenes.OutCode]; ENDLOOP; IF andOfCodes # ThreeDScenes.NoneOut THEN patch.clipState _ out ELSE IF orOfCodes = ThreeDScenes.NoneOut THEN patch.clipState _ in ELSE patch.clipState _ clipped; }; GetPtrPatchClipState: PROC[ shape: REF ShapeInstance, patch: REF PtrPatch] ~ { orOfCodes: ThreeDScenes.OutCode _ ThreeDScenes.NoneOut; andOfCodes: ThreeDScenes.OutCode _ ThreeDScenes.AllOut; FOR j: NAT IN [0..patch.nVtces) DO k: NAT _ patch.vtxPtr[j]; orOfCodes _ LOOPHOLE[ Basics.BITOR[ LOOPHOLE[orOfCodes], LOOPHOLE[shape.vertex[k].clip]], ThreeDScenes.OutCode]; andOfCodes _ LOOPHOLE[ Basics.BITAND[LOOPHOLE[andOfCodes], LOOPHOLE[shape.vertex[k].clip]], ThreeDScenes.OutCode]; ENDLOOP; IF andOfCodes # ThreeDScenes.NoneOut THEN patch.clipState _ out ELSE IF orOfCodes = ThreeDScenes.NoneOut THEN patch.clipState _ in ELSE patch.clipState _ clipped; }; ShapeDo: PROC[ context: REF Context, shape: REF ShapeInstance, fullExpand: BOOLEAN, limitType: ATOM, limit: REAL] RETURNS [REF ShapeInstance] ~ { 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[ VertexSequence[newPts.length * shape.numSurfaces] ] ELSE IF vertex.length < newPts.length + ptIndex -- then we expand as needed THEN { vertex2: REF VertexSequence; IF vertex.length * 2 > LAST[NAT] THEN SIGNAL ThreeDScenes.Error[[$Unimplemented, "Sequence too long (Dragon needed)"]]; vertex2 _ NEW[ VertexSequence[vertex.length*2] ]; FOR i: NAT IN [0..vertex.length) DO -- copy old sequence IF vertex[i] # NIL THEN vertex2[i] _ NEW[ Vertex _ vertex[i]^ ] ELSE vertex2[i] _ NEW[ Vertex ]; ENDLOOP; vertex _ vertex2; }; 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: REF 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; }; 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 ThreeDScenes.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; }; }; patch: REF PtrPatchSequence _ NARROW[ shape.surface ]; vertex: REF ThreeDScenes.VertexSequence; shade: REF ThreeDScenes.ShadingSequence; surface: REF PtrPatchSequence; patchInfo: REF ThreeDScenes.ShadingSequence _ NARROW[ ThreeDScenes.GetShading[ shape, $PatchColors ] ]; newPts: REF ThreeDScenes.VertexInfoSequence; newPatches: REF PtrPatchSequence; ptIndex, patchIndex: INT _ 0; class: REF ThreeDSurfaces.PatchProcs _ NARROW[ Atom.GetPropFromList[context.props, patch[0].type] ]; IF class = NIL THEN ERROR ThreeDScenes.Error[[$MisMatch, "No class registered"]]; IF fullExpand AND class.expand = NIL THEN ERROR ThreeDScenes.Error[[$MisMatch, "No expand Proc"]]; IF NOT fullExpand AND class.subdivide = NIL THEN ERROR ThreeDScenes.Error[[$MisMatch, "No subdivide Proc"]]; FOR pNum: NAT IN [0..shape.numSurfaces) DO IF fullExpand -- expand patch THEN [newPts, newPatches] _ class.expand[shape, patch[pNum], limitType, limit] ELSE [newPts, newPatches] _ class.subdivide[shape, patch[pNum], limitType, limit]; 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[ ThreeDScenes.Vertex ]; vertex[i+ptIndex]^ _ newPts[i].coord; shade[i+ptIndex] _ NEW[ ThreeDScenes.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[newPatches[i].length] ]; FOR j: NAT IN [0..newPatches[i].length) DO surface[i+patchIndex][j] _ newPatches[i][j] + ptIndex; ENDLOOP; surface[i+patchIndex].type _ newPatches[i].type; surface[i+patchIndex].oneSided _ newPatches[i].oneSided; surface[i+patchIndex].nVtces _ newPatches[i].length; 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.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; ThreeDScenes.PutShading[ shape, $PatchColors, NIL ]; shape.shadingInValid _ TRUE; shape.vtcesInValid _ TRUE; RETURN[shape]; }; ShapeExpand: PUBLIC PROC[ context: REF Context, shape: REF ShapeInstance, limitType: ATOM _ NIL, limit: REAL _ 0.0] RETURNS [REF ShapeInstance] ~ { RETURN[ ShapeDo[ context, shape, TRUE, limitType, limit ] ]; }; ShapeSubdivide: PUBLIC PROC[ context: REF Context, shape: REF ShapeInstance, limitType: ATOM _ NIL, limit: REAL _ 0.0] RETURNS [REF ShapeInstance] ~ { RETURN[ ShapeDo[ context, shape, FALSE, limitType, limit ] ]; }; ExpandToLines: PROC[ context: REF Context, patch: REF Patch, action: PROC[context: REF Context, patch: REF Patch] ] ~ { class: REF ThreeDSurfaces.PatchProcs _ NARROW[ Atom.GetPropFromList[context.props, patch.type] ]; IF class = NIL THEN ERROR ThreeDScenes.Error[[$MisMatch, "No class registered"]]; class.displayLines[context, patch, NIL, 0.0, action]; -- expand to default limit and display }; limitInPixels: REAL _ 2.0; ExpandToConvexPolys: PROC[ context: REF Context, patch: REF Patch, action: PROC[context: REF Context, patch: REF Patch] ] ~ { class: REF ThreeDSurfaces.PatchProcs _ NARROW[ Atom.GetPropFromList[context.props, patch.type] ]; IF class = NIL THEN ERROR ThreeDScenes.Error[[$MisMatch, "No class registered"]]; IF context.alphaBuffer THEN class.display[context, patch, NIL, 0.0, action] -- expand to maximum and display ELSE class.display[context, patch, NIL, limitInPixels, action]; -- expand for quick display }; RegisterSurfaceType: PUBLIC PROC[ context: REF Context, type: ATOM, procs: REF ThreeDSurfaces.PatchProcs ] ~ { context.props _ Atom.PutPropOnList[context.props, type, procs]; }; BackFacing: PUBLIC PROC[ poly: REF Patch, useEyeSpace: BOOLEAN _ FALSE] RETURNS [BOOLEAN] ~ { SELECT poly.type FROM $ConvexPolygon => IF useEyeSpace THEN { this: ThreeDScenes.VertexInfo _ poly.vtx[0]; next: ThreeDScenes.VertexInfo _ poly.vtx[1]; last: ThreeDScenes.VertexInfo _ poly.vtx[2]; direction: Triple _ Vector3d.Normalize[Vector3d.Cross[ [next.coord.ex - this.coord.ex, next.coord.ey - this.coord.ey, next.coord.ez - this.coord.ez], [last.coord.ex - this.coord.ex, last.coord.ey - this.coord.ey, last.coord.ez - this.coord.ez] ] ]; RETURN[ Vector3d.Dot[[this.coord.ex, this.coord.ey, this.coord.ez] , direction] > 0. ]; } ELSE { -- do check in image space, rejecting eensy polygon edges s: REAL _ 1.0 / ScanConvert.justNoticeable; -- scales visible feature size to 1 FOR i: NAT IN [0..poly.nVtces) DO this: ThreeDScenes.VertexInfo _ poly.vtx[i]; next: ThreeDScenes.VertexInfo _ poly.vtx[(i+1) MOD poly.nVtces]; last: ThreeDScenes.VertexInfo _ poly.vtx[(i+poly.nVtces-1) MOD poly.nVtces]; zNorm: INT _ -- integer computation of z-coord of normal ( Real.Fix[s*next.coord.sx - s*this.coord.sx] ) * ( Real.Fix[s*last.coord.sy - s*this.coord.sy] ) - ( Real.Fix[s*next.coord.sy - s*this.coord.sy] ) * ( Real.Fix[s*last.coord.sx - s*this.coord.sx] ); IF zNorm # 0 THEN RETURN[ zNorm > 0 ] ENDLOOP; SIGNAL ThreeDScenes.Error[[$Condition, "Edges too small for stable arithmetic"]]; }; $Bezier => { SIGNAL ThreeDScenes.Error[[$Unimplemented, "Backfacing for patches not done"]]; }; ENDCASE => SIGNAL ThreeDScenes.Error[[$Unimplemented, "Unknown type"]]; RETURN[ FALSE ]; }; ShadePoly: PUBLIC PROC[ context: REF Context, poly: REF Patch] ~ { ref: REF _ Atom.GetPropFromList[poly.props, $Shininess]; shininess: REAL _ IF ref # NIL THEN NARROW[ref, REF REAL]^ ELSE 0.0; clr: RGB; transmittance: REAL; shading: ATOM _ NARROW[ Atom.GetPropFromList[ poly.props, $Type ] ]; FOR i: NAT IN [0..poly.nVtces) DO IF shading = $Faceted THEN { -- calculate normals from vertex coords lVtx: NAT _ (i + poly.nVtces - 1) MOD poly.nVtces; nVtx: NAT _ (i + 1) MOD poly.nVtces; [[ poly[i].shade.exn, poly[i].shade.eyn, poly[i].shade.ezn ]] _ Vector3d.Cross[ [ poly[nVtx].coord.ex - poly[i].coord.ex, -- in eyespace, so do left-handed poly[nVtx].coord.ey - poly[i].coord.ey, poly[nVtx].coord.ez - poly[i].coord.ez ], [ poly[lVtx].coord.ex - poly[i].coord.ex, poly[lVtx].coord.ey - poly[i].coord.ey, poly[lVtx].coord.ez - poly[i].coord.ez ] ]; }; [clr, transmittance] _ ThreeDScenes.ShadePt[context, poly[i], 0.0]; poly.vtx[i].shade.ir _ Real.FixC[clr.R * 255.0]; poly.vtx[i].shade.ig _ Real.FixC[clr.G * 255.0]; poly.vtx[i].shade.ib _ Real.FixC[clr.B * 255.0]; poly.vtx[i].shade.it _ Real.FixC[ transmittance * 255.0]; ENDLOOP; }; GetShades: PUBLIC PROC[context: REF Context, shape: REF ShapeInstance] ~ { AverageVertices: PROC[poly: REF PtrPatch] RETURNS[vtx: Vertex] ~ { FOR i: NAT IN [0..poly.nVtces) DO addVtx: Vertex _ shape.vertex[poly.vtxPtr[i]]^; vtx.x _ vtx.x + addVtx.x; vtx.y _ vtx.y + addVtx.y; vtx.z _ vtx.z + addVtx.z; vtx.ex _ vtx.ex + addVtx.ex; vtx.ey _ vtx.ey + addVtx.ey; vtx.ez _ vtx.ez + addVtx.ez; vtx.sx _ vtx.sx + addVtx.sx; vtx.sy _ vtx.sy + addVtx.sy; vtx.sz _ vtx.sz + addVtx.sz; vtx.clip _ LOOPHOLE[ Basics.BITOR[ LOOPHOLE[vtx.clip], LOOPHOLE[addVtx.clip] ], ThreeDScenes.OutCode]; ENDLOOP; vtx.x _ vtx.x/poly.nVtces; vtx.y _ vtx.y/poly.nVtces; vtx.z _ vtx.z/poly.nVtces; vtx.ex _ vtx.ex/poly.nVtces; vtx.ey _ vtx.ey/poly.nVtces; vtx.ez _ vtx.ez/poly.nVtces; vtx.sx _ vtx.sx/poly.nVtces; vtx.sy _ vtx.sy/poly.nVtces; vtx.sz _ vtx.sz/poly.nVtces; }; poly: REF PtrPatchSequence; polyShades: REF ThreeDScenes.ShadingSequence _ NARROW[ ThreeDScenes.GetShading[ shape, $PatchColors ] ]; ref: REF _ Atom.GetPropFromList[shape.shadingProps, $Shininess]; shininess: REAL _ IF ref # NIL THEN NARROW[ref, REF REAL]^ ELSE 0.0; clr: RGB; IF shape.surface = NIL OR polyShades = NIL OR ThreeDScenes.GetShading[shape, $Type] # $Faceted THEN { SIGNAL ThreeDScenes.Error[[$Condition, "Data missing for faceted shading"]]; RETURN; }; poly _ NARROW[ shape.surface, REF PtrPatchSequence ]; FOR i: NAT IN [0..poly.length) DO IF poly[i] # NIL THEN { trns: REAL _ 0.0; -- transmittance vtx: Vertex _ AverageVertices[poly[i]]; pt: ThreeDScenes.VertexInfo _ [ vtx, polyShades[i]^, NIL ]; [clr, trns] _ ThreeDScenes.ShadePt[context, pt, shininess]; -- calculate shade IF polyShades[i] = NIL THEN polyShades[i] _ NEW[ ThreeDScenes.ShadingValue ]; polyShades[i].ir _ Real.FixC[clr.R * 255.0]; polyShades[i].ig _ Real.FixC[clr.G * 255.0]; polyShades[i].ib _ Real.FixC[clr.B * 255.0]; polyShades[i].it _ Real.FixC[ trns * 255.0]; }; ENDLOOP; ThreeDScenes.PutShading[ shape, $PatchColors, polyShades ] }; GetNormal: PROC[vertex: REF ThreeDScenes.VertexSequence, poly: REF PtrPatch, cVtx: NAT] RETURNS[normal: Triple] ~ { lVtx: NAT _ (cVtx + poly.nVtces - 1) MOD poly.nVtces; nVtx: NAT _ (cVtx + 1) MOD poly.nVtces; normal _ Vector3d.Cross[ -- in object space so do right-handed DiffPosns[ vertex[poly.vtxPtr[lVtx]], vertex[poly.vtxPtr[cVtx]] ], DiffPosns[ vertex[poly.vtxPtr[nVtx]], vertex[poly.vtxPtr[cVtx]] ] ]; }; GetPolyNormals: PUBLIC PROC[shape: REF ShapeInstance] ~ { surface: REF PtrPatchSequence; patchInfo: REF ThreeDScenes.ShadingSequence _ NARROW[ ThreeDScenes.GetShading[ shape, $PatchColors ] ]; IF shape.type # $ConvexPolygon THEN SIGNAL ThreeDScenes.Error[[$MisMatch, "Operation only for polygons"]]; IF shape.surface = NIL THEN RETURN; -- not a shape (probably a light source) surface _ NARROW[shape.surface, REF PtrPatchSequence]; IF patchInfo = NIL THEN patchInfo _ NEW[ ThreeDScenes.ShadingSequence[shape.numSurfaces] ]; FOR i: NAT IN [0..patchInfo.length) DO IF surface[i] # NIL THEN { sumNmls: Triple _ [0., 0., 0.]; FOR cVtx: NAT IN [0..surface[i].nVtces) DO sumNmls _ Vector3d.Add[ sumNmls, GetNormal[shape.vertex, surface[i], cVtx] ]; ENDLOOP; sumNmls _ Vector3d.Normalize[sumNmls]; IF patchInfo[i] = NIL THEN patchInfo[i] _ NEW[ ThreeDScenes.ShadingValue ]; patchInfo[i].xn _ sumNmls.x; patchInfo[i].yn _ sumNmls.y; patchInfo[i].zn _ sumNmls.z; }; ENDLOOP; ThreeDScenes.PutShading[ shape, $PatchColors, patchInfo ]; shape.vtcesInValid _ TRUE; -- make sure eye-space normals correct }; GetVtxNormals: PUBLIC PROC[shape: REF ShapeInstance] ~ { surface: REF PtrPatchSequence _ NARROW[shape.surface]; IF shape.type # $ConvexPolygon THEN SIGNAL ThreeDScenes.Error[[$MisMatch, "Operation only for polygons"]]; IF shape.shade = NIL OR shape.shade.length # shape.vertex.length THEN { shape.shade _ NEW[ ThreeDScenes.ShadingSequence[shape.vertex.length] ]; IF ThreeDScenes.GetShading[ shape, $Type ] = NIL OR ThreeDScenes.GetShading[ shape, $Type ] = $Faceted THEN ThreeDScenes.PutShading[ shape, $Type, $Smooth]; }; FOR i: NAT IN [0..shape.vertex.length) DO IF shape.shade[i] = NIL THEN shape.shade[i] _ NEW[ ThreeDScenes.ShadingValue ]; shape.shade[i].xn _ shape.shade[i].yn _ shape.shade[i].zn _ 0.0; ENDLOOP; FOR i: NAT IN [0..shape.numSurfaces) DO -- get normals at vertices, add to earlier ones IF surface[i] # NIL THEN FOR cVtx: NAT IN [0..surface[i].nVtces) DO OPEN shape.shade[surface[i].vtxPtr[cVtx]]; cornerNormal: Triple _ GetNormal[ shape.vertex, surface[i], cVtx ]; [[xn, yn, zn]] _ Vector3d.Add[ cornerNormal, [xn, yn, zn]]; ENDLOOP; ENDLOOP; FOR i: NAT IN [0..shape.shade.length) DO IF shape.shade[i] # NIL THEN { OPEN shape.shade[i]; [[xn, yn, zn]] _ Vector3d.Normalize[ [xn, yn, zn] ]; }; ENDLOOP; shape.vtcesInValid _ TRUE; -- make sure eye-space normals correct }; LoadSortSequence: PUBLIC PROC[ context: REF Context, buckets: REF ThreeDSurfaces.SortSequence _ NIL ] RETURNS[REF ThreeDSurfaces.SortSequence] ~ { shape: REF ThreeDScenes.ShapeSequence _ context.shapes; patchCount: CARDINAL _ 0; bucketListPtr: NAT _ context.depthResolution; minDepth: REAL _ context.yonLimit; maxDepth: REAL _ context.hitherLimit; zScale: REAL _ 1.; FOR i: NAT IN [0.. shape.length) DO -- get minimum and maximum depth and patch count IF shape[i] # NIL AND Atom.GetPropFromList[shape[i].props, $Hidden] = NIL THEN IF shape[i].clipState # out AND shape[i].surface # NIL THEN { radius: REAL _ shape[i].boundingRadius; IF shape[i].centroid.ez - radius < minDepth THEN minDepth _ shape[i].centroid.ez - radius; IF shape[i].centroid.ez + radius > maxDepth THEN maxDepth _ shape[i].centroid.ez + radius; patchCount _ patchCount + shape[i].numSurfaces; }; ENDLOOP; minDepth _ MAX[minDepth, 0]; -- nothing allowed behind the eyepoint IF (maxDepth - minDepth) > 0. THEN zScale _ (context.depthResolution - 1) / (maxDepth - minDepth); IF buckets = NIL OR buckets.length < patchCount + context.depthResolution THEN { buckets _ NEW[ThreeDSurfaces.SortSequence[patchCount + context.depthResolution]]; FOR i: NAT IN [0..buckets.length) DO -- initialize elements of sort structure buckets[i] _ NEW[ ShapePatch _ [NIL, 0, 0] ]; ENDLOOP; }; FOR i: NAT IN [0..context.depthResolution) DO -- clear structure for new sort buckets[i].shape _ NIL; buckets[i].next _ 0; ENDLOOP; FOR s: NAT IN [0.. shape.length) DO -- Enter patches (by z-centroid) in bucket lists IF shape[s] # NIL AND Atom.GetPropFromList[shape[s].props, $Hidden] = NIL THEN IF shape[s].clipState # out AND shape[s].surface # NIL THEN { patch: REF PtrPatchSequence _ NARROW[shape[s].surface]; FOR i: NAT IN [0..shape[s].numSurfaces) DO IF patch[i] # NIL THEN { zSum: REAL _ 0.; iz: INTEGER; IF shape[s].clipState = in THEN { -- unclipped, inside FOR j: NAT IN [0..patch[i].nVtces) DO zSum _ zSum + shape[s].vertex[patch[i].vtxPtr[j]].ez; ENDLOOP; iz _ Real.FixI[zScale * ((zSum / patch[i].nVtces) - minDepth)]; patch[i].clipState _ in; } ELSE IF shape[s].clipState = clipped THEN { -- clipped, evaluate clipping tags GetPtrPatchClipState[ shape[s], patch[i]]; IF patch[i].clipState # out THEN { FOR j: NAT IN [0..patch[i].nVtces) DO k: NAT _ patch[i].vtxPtr[j]; zSum _ zSum + shape[s].vertex[k].ez; ENDLOOP; iz _ Real.FixI[zScale * ((zSum / patch[i].nVtces) - minDepth)]; IF iz < 0 THEN iz _ 0; }; }; IF patch[i].clipState # out THEN { IF buckets[iz].shape # NIL THEN { -- previous entry in this bucket buckets[bucketListPtr].next _ buckets[iz].next; buckets[iz].next _ bucketListPtr; iz _ bucketListPtr; bucketListPtr _ bucketListPtr + 1; }; buckets[iz].shape _ shape[s]; buckets[iz].patch _ i; }; }; ENDLOOP; }; ENDLOOP; RETURN[ buckets ]; }; GetDepths: PROC[ context: REF Context] ~ { shape: REF ThreeDScenes.ShapeSequence _ context.shapes; minDepth: REAL _ context.yonLimit; maxDepth: REAL _ context.hitherLimit; zScale: REAL _ 1.; FOR i: NAT IN [0.. shape.length) DO -- get minimum and maximum depth IF shape[i] # NIL AND Atom.GetPropFromList[shape[i].props, $Hidden] = NIL THEN IF shape[i].clipState # out AND shape[i].surface # NIL THEN { radius: REAL _ shape[i].boundingRadius; IF shape[i].centroid.ez - radius < minDepth THEN minDepth _ shape[i].centroid.ez - radius; IF shape[i].centroid.ez + radius > maxDepth THEN maxDepth _ shape[i].centroid.ez + radius; }; ENDLOOP; minDepth _ MAX[minDepth, context.hitherLimit]; -- nothing allowed behind the eyepoint IF (maxDepth - minDepth) > 0. THEN zScale _ (context.depthResolution - 1) / (maxDepth - minDepth); FOR s: NAT IN [0.. shape.length) DO IF shape[s] # NIL AND Atom.GetPropFromList[shape[s].props, $Hidden] = NIL THEN IF shape[s].clipState # out AND shape[s].surface # NIL THEN { patch: REF PtrPatchSequence _ NARROW[shape[s].surface]; FOR i: NAT IN [0..shape[s].numSurfaces) DO IF patch[i] # NIL THEN IF shape[s].clipState = in THEN { -- unclipped, inside FOR j: NAT IN [0..patch[i].nVtces) DO shape[s].vertex[patch[i].vtxPtr[j]].sz _ zScale * (shape[s].vertex[patch[i].vtxPtr[j]].ez - minDepth); ENDLOOP; patch[i].clipState _ in; } ELSE IF shape[s].clipState = clipped THEN -- clipped, ensure positive result FOR j: NAT IN [0..patch[i].nVtces) DO shape[s].vertex[patch[i].vtxPtr[j]].sz _ MAX[ 0.0, zScale * (shape[s].vertex[patch[i].vtxPtr[j]].ez - minDepth) ]; ENDLOOP; ENDLOOP; }; ENDLOOP; }; currentColor: SampleSet; -- global color for line drawings currentShape: REF ShapeInstance; UpdateShapeCache: PROC[context: REF Context, shape: REF ShapeInstance] ~ { currentShape _ shape; IF NARROW[ Atom.GetPropFromList[shape.shadingProps, $Type ], ATOM ] = $Lines THEN currentColor _ ShapetoColorBytes[context, shape]; }; DoBackToFront: PUBLIC PROC[ context: REF Context, sortSeq: REF ThreeDSurfaces.SortSequence, action: PROC[REF ThreeDSurfaces.ShapePatch] ] ~ { FOR i: NAT DECREASING IN [0..context.depthResolution) DO j: NAT _ i; WHILE sortSeq[j].shape # NIL DO IF sortSeq[j].shape # currentShape THEN UpdateShapeCache[context, sortSeq[j].shape]; action[sortSeq[j]]; -- call back with next polygon in order j _ sortSeq[j].next; IF j = 0 THEN EXIT; ENDLOOP; ENDLOOP; CombineBoxes[context]; -- get combined bounding box on scene }; DoFrontToBack: PUBLIC PROC[ context: REF Context, sortSeq: REF ThreeDSurfaces.SortSequence, action: PROC[REF ThreeDSurfaces.ShapePatch] ] ~ { FOR i: NAT IN [0..context.depthResolution) DO j: NAT _ i; WHILE sortSeq[j].shape # NIL DO IF sortSeq[j].shape # currentShape THEN UpdateShapeCache[context, sortSeq[j].shape]; action[sortSeq[j]]; -- call back with next polygon in order j _ sortSeq[j].next; IF j = 0 THEN EXIT; ENDLOOP; ENDLOOP; CombineBoxes[context]; -- get combined bounding box on scene }; DoForPatches: PUBLIC PROC[ context: REF Context, set: REF ThreeDScenes.ShapeSequence, patchAction: PROC[REF ThreeDSurfaces.ShapePatch], shapeAction: PROC[REF ThreeDScenes.ShapeInstance] _ NIL ] ~ { FOR s: NAT IN [0..set.length) DO IF Atom.GetPropFromList[set[s].props, $Hidden] = NIL THEN { IF set[s].clipState = in AND set[s].type = $ConvexPolygon AND shapeAction # NIL THEN shapeAction[set[s]] ELSE IF set[s].clipState # out AND set[s].surface # NIL THEN { patch: REF PtrPatchSequence _ NARROW[set[s].surface]; currentColor _ ShapetoColorBytes[context, set[s]]; -- set color for shape FOR i: NAT IN [0..set[s].numSurfaces) DO IF patch[i] # NIL THEN { IF set[s].clipState = in THEN patch[i].clipState _ in -- unclipped, inside ELSE IF set[s].clipState = clipped THEN GetPtrPatchClipState[ set[s], patch[i] ] ELSE patch[i].clipState _ out; IF patch[i].clipState # out THEN patchAction[NEW[ ShapePatch _ [set[s], i, 0] ]]; }; ENDLOOP; }; }; ENDLOOP; }; ShowObjects: PUBLIC PROC[ context: REF Context, frontToBack: BOOLEAN _ FALSE ] ~ { ShowPatch: PROC[p: REF ThreeDSurfaces.ShapePatch] ~{ patch: REF Patch _ ShapePatchToPatch[ context, p ]; OutputPatch[ context, patch ]; }; shape: REF ThreeDScenes.ShapeSequence _ context.shapes; FOR i: NAT IN [0.. shape.length) DO IF Atom.GetPropFromList[shape[i].props, $Hidden] = NIL OR shape[i].type = $Light THEN IF shape[i].vtcesInValid THEN { shape[i].clipState _ ThreeDScenes.XfmToEyeSpace[ context, shape[i] ]; IF shape[i].clipState # out THEN ThreeDScenes.XfmToDisplay[context, shape[i] ]; }; ENDLOOP; FOR i: NAT IN [0.. shape.length) DO -- now, get it shaded IF shape[i].shadingInValid AND (shape[i].clipState # out) THEN { IF ThreeDScenes.GetShading[ shape[i], $Type ] = $Faceted THEN GetShades[ context, shape[i] ]; ThreeDScenes.GetVtxShades[ context, shape[i] ]; }; ENDLOOP; IF context.depthBuffer THEN { GetDepths[context]; DoForPatches[context, context.shapes, ShowPatch]; } ELSE { context.sortSequence _ LoadSortSequence[context, NARROW[context.sortSequence] ]; IF frontToBack THEN DoFrontToBack[context, NARROW[context.sortSequence], ShowPatch] ELSE DoBackToFront[context, NARROW[context.sortSequence], ShowPatch]; }; }; ShowWireFrameObjects: PUBLIC PROC[context: REF Context ] ~ { ShowPatch: PROC[p: REF ThreeDSurfaces.ShapePatch] ~{ patch: REF Patch _ ShapePatchToPatch[ context, p ]; OutputPatchEdges[ context, patch ]; }; ShowShape: PROC[s: REF ThreeDScenes.ShapeInstance] ~{ OutputShapeLines[ context, s ]; }; shape: REF ThreeDScenes.ShapeSequence _ context.shapes; FOR i: NAT IN [0.. shape.length) DO IF Atom.GetPropFromList[shape[i].props, $Hidden] = NIL AND shape[i].vtcesInValid THEN { shape[i].clipState _ ThreeDScenes.XfmToEyeSpace[ context, shape[i] ]; IF shape[i].clipState # out THEN ThreeDScenes.XfmToDisplay[context, shape[i] ]; }; ENDLOOP; IF context.renderMode = $LF OR context.renderMode = $Dithered THEN ThreeDMisc.SetNamedColor[context, "White" ]; IF context.alphaBuffer THEN DoForPatches[context, context.shapes, ShowPatch] ELSE DoForPatches[context, context.shapes, ShowPatch, ShowShape]; CombineBoxes[context]; -- get combined bounding box on scene }; EdgesToPolygons: PROC[context: REF Context, patch: REF Patch] ~ { baseRadius: REAL _ .003; limit: NAT _ IF patch.type = $Path THEN patch.nVtces - 1 ELSE patch.nVtces; edge: REF Patch _ NEW[ Patch[6] ]; MakeEdge: PROC[pt1, pt2: ThreeDScenes.VertexInfo] ~ { 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 < ScanConvert.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 edge.clipState = in THEN FOR i: NAT IN [0..6) DO OPEN edge[i].coord; [[sx, sy, sz]] _ ThreeDScenes.XfmPtToDisplay[ context, [ex, ey, ez] ]; ENDLOOP; OutputPatch[context, edge]; }; edge.type _ $ConvexPolygon; edge.nVtces _ 6; edge.oneSided _ patch.oneSided; edge.clipState _ patch.clipState; edge.props _ Atom.PutPropOnList[ NIL, $Type, $Smooth ]; FOR i: NAT IN [0..limit) DO MakeEdge[ patch[i], patch[(i+1) MOD patch.nVtces] ]; ENDLOOP; }; OutputShapeLines: PUBLIC PROC[context: REF Context, s: REF ThreeDScenes.ShapeInstance] ~ { ax, ay, bx, by: NAT; color: SampleSet _ ShapetoColorBytes[context, s]; imagerCtx: Imager.Context; usingImager: BOOLEAN _ FALSE; patches: REF PtrPatchSequence; IF s.surface = NIL THEN RETURN; IF context.renderMode = $Dithered THEN { usingImager _ TRUE; imagerCtx _ ThreeDMisc.GetImagerContext[context]; }; patches _ NARROW[s.surface, REF PtrPatchSequence]; FOR i: NAT IN [0..s.numSurfaces) DO IF patches[i].type # $ConvexPolygon THEN SIGNAL ThreeDScenes.Error[[$MisMatch, "Operation only for convex polygons"]] ELSE FOR j: NAT IN [0..patches[i].nVtces] DO k: NAT _ IF j = patches[i].nVtces THEN 0 ELSE j; bx _ Real.RoundC[s.vertex[patches[i].vtxPtr[k]].sx]; by _ Real.RoundC[s.vertex[patches[i].vtxPtr[k]].sy]; IF j > 0 THEN IF NOT usingImager THEN ScanConvert.PutLine[ context.display, [ax,ay], [bx,by], color ] ELSE Imager.MaskVectorI[ imagerCtx, ax, ay, bx, by ]; ax _ bx; ay _ by; ENDLOOP; IF stopMe THEN HoldEverything[]; -- shut down if stop signal received ENDLOOP; }; OutputPatchEdges: PUBLIC PROC[context: REF Context, patch: REF Patch] ~ { imagerCtx: Imager.Context; usingImager: BOOLEAN _ FALSE; IF patch = NIL OR patch.clipState = out THEN RETURN[]; -- reject if outside frame IF patch.type # $ConvexPolygon AND patch.type # $Path -- expand to curves THEN { ExpandToLines[context, patch, OutputPatchEdges]; RETURN[]; }; IF context.alphaBuffer -- antialiased THEN { EdgesToPolygons[context, patch]; RETURN[]; }; IF context.renderMode = $Dithered THEN { usingImager _ TRUE; imagerCtx _ ThreeDMisc.GetImagerContext[context]; }; IF patch.clipState = in THEN { ax, ay, bx, by: NAT; limit: NAT _ IF patch.type = $Path THEN patch.nVtces - 1 ELSE patch.nVtces; FOR j: NAT IN [0..limit] DO i: NAT _ IF j = patch.nVtces THEN 0 ELSE j; bx _ Real.RoundC[patch[i].coord.sx]; by _ Real.RoundC[patch[i].coord.sy]; IF j > 0 THEN IF NOT usingImager THEN ScanConvert.PutLine[ context.display, [ax,ay], [bx,by], currentColor ] ELSE Imager.MaskVectorI[ imagerCtx, ax, ay, bx, by ]; ax _ bx; ay _ by; ENDLOOP; } ELSE IF patch.clipState = clipped THEN { patch _ ClipPoly[ context, patch ]; IF patch.nVtces > 2 OR ( patch.type = $Path AND patch.nVtces > 1 ) THEN { ax, ay, bx, by: NAT; limit: NAT _ IF patch.type = $Path THEN patch.nVtces - 1 ELSE patch.nVtces; FOR j: NAT IN [0..limit] DO i: NAT _ IF j = patch.nVtces THEN 0 ELSE j; x, y, z: REAL; [[x, y, z]] _ ThreeDScenes.XfmPtToDisplay[ context, [patch[i].coord.ex, patch[i].coord.ey, patch[i].coord.ez] ]; bx _ Real.RoundC[x]; by _ Real.RoundC[y]; IF j > 0 THEN IF NOT usingImager THEN ScanConvert.PutLine[ context.display, [ax,ay], [bx,by], currentColor ] ELSE Imager.MaskVectorI[ imagerCtx, ax, ay, bx, by ]; ax _ bx; ay _ by; ENDLOOP; }; }; IF stopMe THEN HoldEverything[]; -- shut down if stop signal received }; OutputPatch: PUBLIC PROC[context: REF Context, patch: REF Patch] ~ { IF patch = NIL OR patch.clipState = out THEN RETURN[]; -- reject if outside frame IF NARROW[ Atom.GetPropFromList[patch.props, $Type ], ATOM ] = $Lines THEN { OutputPatchEdges[context, patch]; -- to show sorted, anti-aliased line drawings RETURN[]; }; IF patch.type # $ConvexPolygon -- catch unexpanded patches, etc. THEN { ExpandToConvexPolys[context, patch, OutputPatch]; RETURN; }; IF NARROW[ Atom.GetPropFromList[patch.props, $Type ], ATOM ] = $Lines THEN { OutputPatchEdges[context, patch]; -- to show expanded curved patches, etc RETURN[]; }; IF patch.clipState = clipped THEN { patch _ ClipPoly[context, patch]; FOR i: NAT IN [0..patch.nVtces) DO OPEN patch[i].coord; [[sx, sy, sz]] _ ThreeDScenes.XfmPtToDisplay[context, [ex, ey, ez]]; ENDLOOP; }; IF patch.nVtces > 2 THEN { IF NOT BackFacing[ patch ! ThreeDScenes.Error => IF reason.code = $Condition THEN GO TO GiveUp ] THEN Tilers.PolygonTiler[context, patch] ELSE IF patch.oneSided -- backfacing!! THEN RETURN[] -- Reject if closed surface ELSE { FOR i: NAT IN [0..patch.nVtces) DO -- recalculate shading for back side r, g, b, t: REAL; 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; [[r,g,b], t] _ ThreeDScenes.ShadePt[ context, patch[i], 0.0 ]; patch[i].shade.ir _ Real.FixC[r * 255.0]; patch[i].shade.ig _ Real.FixC[g * 255.0]; patch[i].shade.ib _ Real.FixC[b * 255.0]; patch[i].shade.it _ Real.FixC[ t * 255.0]; ENDLOOP; FOR i: NAT IN [0..patch.nVtces/2) DO -- reorder to keep clockwise on display tempVtx: ThreeDScenes.VertexInfo _ patch[i]; patch[i] _ patch[patch.nVtces-1 - i]; patch[patch.nVtces-1 - i] _ tempVtx; ENDLOOP; Tilers.PolygonTiler[context, patch] }; EXITS GiveUp => NULL }; IF stopMe THEN HoldEverything[]; -- shut down if stop signal received }; END. pThreeDSurfacesImpl.mesa Copyright c 1984, 1986 by Xerox Corporation. All rights reserved. Last Edited by: Crow, May 29, 1986 5:42:31 pm PDT Internal Declarations Utility Procedures Procedures for Reading in Shape Descriptions Get File, Read number of points and number of polygons Find approximation to bounding sphere Procedures for Transformations and Clipping Put out point if clip plane crossed Procedures for expansion 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 expand a patch into curved lines, pass them as "$Paths" PROC[ context: REF Context, patch: REF Patch, limitType: ATOM, limit: REAL, action: PROC]; expand a patch into displayable polygons PROC[ context: REF Context, patch: REF Patch, limitType: ATOM, limit: REAL, action: PROC]; Procedures for Shading Assumes left-handed space (eye space or screen space) Backfacing test based on first vertex and adjacent vertices last: ThreeDScenes.VertexInfo _ poly.vtx[poly.nVtces - 1]; Backfacing test based on convex hull being hidden behind its base Calculate shades for Faceted shading (Used for quick display) Compute Normals, Sum normals at polygon corners to get polygon normal Sum normals for vertices given by adjacent polygon corners, only for polygons! Procedures for Sorting and Display Get Everything (including light centroids) into eyespace Make a polygon for each edge and tile it Unclipped shape output IF context.renderMode = $LF OR context.renderMode = $Dithered THEN { IF context.renderMode = $LF OR context.renderMode = $Dithered THEN { Κ0³˜Iheadšœ™šœ Οmœ7™BJšœ1™1J˜šΟk ˜ Jšœ žœ"˜0Jšœ žœ˜)Jšœ žœ˜Jšœ žœžœžœ˜!Jšœ žœžœ˜Jšœžœžœ ˜Idefaultšœ žœ˜'Lšœžœžœ˜Jšœ žœ˜+Jšœžœ˜.Jšœ žœ,˜=Jšœ žœ˜ Jšœ žœJ˜\Jšœžœσ˜†Jšœ žœ˜ Jšœžœb˜w—J˜—head2šœžœž˜!Iašžœq˜yNšžœ˜J˜Jšœž˜J˜—head3šΟb™Jšœ žœ˜%Jšžœžœžœ Οc˜;Jšœ žœ˜'Jšœžœ˜Jšœ žœ˜#Jšœžœ˜1Jšœžœ˜#Jšœžœ ˜3Jšœ žœ˜)Jšœžœ#˜9Jšœ žœ˜-Jšœžœ˜#Jšœžœ˜3Jšœžœ˜/Jšœžœ!˜6Jšœžœ žœ ˜DJšœ4 ˜KJšœžœžœ˜—šŸ™šΟnœž œ žœžœžœžœžœ˜SL˜—š ‘œž œ žœžœžœžœ˜8Nšžœ ž œž œ˜3Nšœ˜N˜—š‘ œžœ žœ žœ ˜;Jšžœ8˜>J˜—š‘œž œ˜!Jšœ žœ˜Nšžœžœ˜J˜J˜—Jš‘ œžœžœžœ˜7Jš‘ œžœžœžœ˜4š ‘œžœ žœ žœžœ˜dNšœžœžœ'˜3Lšœ žœ˜Lšœ žœ˜L˜Lšžœžœ˜.Lšžœžœ˜.šžœžœ˜ šžœ˜Lšœ žœžœžœ ˜9LšœV˜VL˜—Lšžœ˜—šžœžœ !˜Gšœ˜Jšœžœ%˜-šžœ-˜/Jšžœžœ  ˜Q—Jšœ˜—šœ˜Jšžœžœ+˜NJšœB˜BJšžœ žœžœ žœ˜LJšœ˜—šœ ˜ Jšžœžœ+˜NJšœ!˜!Jšžœ žœžœ žœ˜LJ˜—šœ˜Jšžœžœ+˜NJšœ˜Jšœ˜Jšœ˜Jšžœ žœžœ žœ˜LJšœ˜—Jšžœžœ<˜N—Jšžœ ˜Jšœ˜—š‘ œžœ žœ  ˜IIfdefaultš œžœžœžœžœ˜5šžœžœžœžœ˜,šžœ#žœ˜+šžœB˜DJšžœB˜F—šžœD˜FJšžœD˜H—šžœF˜HJšžœF˜J—šžœ@˜BJšžœ@˜D—Jšœ˜—Jšžœ˜—J˜—š ‘œž œ žœžœžœ žœ ˜ŽJšœ žœ ž œžœ!˜]Nšœžœ3˜>Jšœžœ3˜=Jšœ žœžœ2žœ˜aJšœžœžœ2žœ ˜]šœ žœ žœ˜6Jšœ6˜6Jšœ˜—š œžœžœ žœžœžœ ˜=Jšžœ<ž˜CJšžœžœ˜ —J˜Jš žœžœžœžœ ˜NJšœ $˜Ašžœ žœžœ&˜9Jšžœžœ˜;—Jšœ ˜ Jšœ&˜&Jšœ˜Jšœ#˜#Jšœ- ˜Fšžœžœžœž˜%Jšœžœ˜Jšœ˜Jšœ˜šžœžœ ˜8Jšœ9˜9Lšœ8˜8Lšœ8˜8Lšœ˜—šžœ žœ   ˜Ešžœ˜ Jšžœ*˜.šžœžœžœ˜JšœQ˜QLšœO˜OLšœO˜OLšœP˜Pšžœžœ "˜BLšœ:˜:Jšœ:˜:Jšœ:˜:Jšœ:˜:J˜—J˜———Jšžœ˜—Jšœ˜——šŸ,™,š‘Πbnœžœžœ žœžœžœ"žœžœ˜“Jšœ žœ˜Jšœ˜Jšœžœ˜ Jšœ% !˜FJšœ( (˜Pšœžœžœ˜Jšœ6™6—Jšœ>˜>Jšœ&˜&J˜Jšœ˜Jšœ$˜$Iunitšœžœ*˜ ˜N šœH˜HQ™%—LšœF˜Fš žœžœžœžœ !˜Yšžœžœžœ˜šžœ˜Jšžœ˜Jšžœžœžœ˜A—šžœ˜Jšžœ˜Jšžœžœžœ˜A—šžœ˜Jšžœ˜Jšžœžœžœ˜A—Jšœ˜—Jšžœ˜—Jšœ- &˜SJšœ)˜)Jšœ(˜(Jšœ˜š žœžœžœžœ ˜BJšœ˜šœ-˜-Jšœ+˜+Jšœ.˜.—Jšžœ˜!Jšžœ˜#Jšžœ˜ —J˜J˜—š‘ œžœžœžœžœžœ žœžœžœžœ˜‰Lšœ žœ˜Lšœžœžœ)˜šžœžœžœž˜*Nšœ6˜6Nšžœ˜—Nšœ0˜0Nšœ8˜8Nšœ4˜4Nšœ:˜:Nšœ2˜2Nšžœ˜—Nšœ) ˜>Nšœ,˜,Nšžœ˜—Nšžœ žœ )˜ZNšœ  "˜BNšœ˜Nšœ˜Nšœ#˜#Jšœ.žœ˜4Nšœž˜Nšœž˜Nšžœ˜N˜—š‘ œžœžœ žœžœ&žœžœ žœžœžœ˜€N™FNšžœžœ˜Jšœžœžœ˜7šžœžœžœžœ˜+šžœ žœžœ˜Jšœžœ ˜Jšœžœ˜ šžœžœ  ˜=šžœžœžœžœ˜&Jšœ6˜6Jšžœ˜ —Jšœ@˜@Jšœ˜J˜—šžœžœžœ "˜NLšœ*˜*šžœžœ˜"šžœžœžœž˜%Jšœžœ˜Jšœ$˜$Jšžœ˜—Jšœ?˜?Jšžœžœ˜J˜—J˜—šžœžœ˜"šžœžœžœ  ˜DJ˜3J˜!J˜9J˜—Jšœ!˜!J˜J˜—Jšœ˜—Jšžœ˜—Jšœ˜——Nšžœ˜—Nšžœ ˜Nšœ˜N˜—š‘ œžœ žœ ˜*Nšœžœ-˜7Nšœ žœ˜"Nšœ žœ˜%Nšœžœ˜ š žœžœžœžœ  ˜Dš žœ žœžœ1žœž˜Nšžœžœžœžœ˜=Nšœžœ˜'Nšžœ*˜,Nšœžœ*˜/šžœ)˜+Nšžœ*˜.—N˜——Nšžœ˜—Nšœ žœ! &˜Ušžœ˜Nšžœ@˜D— šžœžœžœž˜#š žœ žœžœ1žœž˜Nšžœžœžœžœ˜>Jšœžœžœ˜7šžœžœžœžœ˜+šžœ žœž˜šžœ˜šžœ  ˜"šžœžœžœžœ˜&Jšœi˜iJšžœ˜ —Jšœ˜J˜—šžœžœžœ "˜Lšžœžœžœžœ˜&šœ)žœ˜-Jšœ˜Jšœ<˜Nšœ˜N˜—š‘ œžœžœ žœžœ0žœžœ!˜’šžœžœžœžœ˜.Nšœžœ˜ šžœžœž˜Nšžœ!žœ-˜TNšœ ,˜BNšœ˜Nšžœžœžœ˜Nšžœ˜—Nšžœ˜—Jšœ %˜>Nšœ˜N™—š‘ œžœžœ žœžœ5žœžœ5žœžœžœ˜Ϋš žœžœžœžœ/ž˜Ušžœ˜šžœžœžœžœ˜PNšžœ˜šžœžœžœžœ˜?Jšœžœžœ˜5Jšœ6 ˜Lšžœžœžœžœžœ žœžœ˜Bšžœ˜Jšžœ ˜7šžœžœ˜#Jšžœ)˜-Jšžœ˜——Jšžœžœ žœ!˜QJšœ˜Jšžœ˜ —J˜——J˜—Nšžœ˜—Nšœ˜J˜—š ‘ œžœžœ žœžœžœ˜Rš‘ œžœžœ˜4Jšœžœ)˜3Jšœ˜J˜—šœžœ-˜7Q™8—šžœžœžœžœ˜$šžœ1žœžœ˜Pšžœžœžœ˜%JšœE˜EJšžœžœ/˜OJšœ˜——Jšžœ˜—š žœžœžœžœ  ˜Ašžœžœžœ˜Bšžœ6˜8Jšžœ ˜$—Jšœ/˜/J˜—Jšžœ˜—šžœ˜JšžœO˜Sšžœ˜Jšœ1žœ˜Pšžœ ˜Jšžœžœ"˜DJšžœžœ#˜E—J˜——Jšœ˜J˜—š‘œžœžœ žœ˜<š‘ œžœžœ˜4Jšœžœ)˜3Jšœ#˜#J˜—š‘ œžœžœ˜5Jšœ˜J˜—Nšœžœ-˜7 šžœžœžœž˜$šžœ1žœžœžœ˜WJšœE˜EJšžœžœ/˜OJšœ˜—Jšžœ˜—šžœžœ˜=Jšžœ-˜1—šžœ˜Jšžœ1˜5Jšžœ>˜B—Jšœ %˜>Jšœ˜J˜—š‘œžœ žœžœ ˜AJ™(Jšœ žœ˜Jš œžœžœžœžœ˜KJšœžœ žœ ˜"š‘œžœ'˜5Jšœ9žœ˜>Jšœ( $œ˜PJšœ#˜#Jšœ,˜,Jšžœ"žœžœ ˜TJšœ.˜.Jšœ<˜Jšœ)˜)Jšœ)˜)Jšœ>˜>Jšœ*˜*Lšœ)˜)Lšœ)˜)Jšœ*˜*Jšžœ˜—š žœžœžœžœ '˜MJšœ,˜,Jšœ%˜%Jšœ$˜$Jšžœ˜—Jšœ#˜#Jšœ˜———Jšžœ ž˜J˜—Jšžœžœ $˜FJ˜J˜J˜—J˜—Jšžœ˜—…—Άν7