DIRECTORY Atom USING [ GetPropFromList, PropList, PutPropOnList, RemPropFromList ], List USING [ Nconc1 ], Rope USING [ ROPE, Equal ], Real USING [ Fix, Float, LargestNumber ], RealFns USING [ ArcTanDeg, Sin, Cos, Power, SqRt ], Checksum USING [ ComputeChecksum ], IO USING [ GetAtom, GetInt, GetReal, STREAM ], ImagerPixel USING [ GetPixels, ObtainScratchPixels, PixelBuffer, PixelMap, ReleaseScratchPixels ], ImagerSample USING [ SampleBuffer ], Vector2 USING [ Dot, Length, Mul, Unit ], G3dVector USING [ Cross, Normalize ], ScanConvert USING [ justNoticeable ], ThreeDBasics USING [ Box, Context, Error, IntSequence, LoadShadingClass, Pair, PairSequence, Patch, PtrPatchSequence, RealSequence, Rectangle, RegisterShadingClass, RGB, ShadingClass, ShapeClass, ShapeInstance, ShapeProc, Spot, SpotProc, SummedTexture, SumSequence, TextureFunction, TextureMap, Triple, TripleSequence, VertexInfo, VertexInfoProc, VertexInfoSequence, VtxToRealSeqProc ], RenderWithPixels USING [ AntiAliasing, GetContext, AllocatePixelMemory, ShadeSpot ], SurfaceRender USING [ ValidateContext ], ShapeUtilities USING [ ShadeVtx ], SceneUtilities USING [ FindShape, GetRope ], AISAnimation USING [ GetAIS ], MappedAndSolidTexture USING [ ]; MappedAndSolidTextureImpl: CEDAR PROGRAM IMPORTS AISAnimation, Atom, Checksum, G3dVector, ImagerPixel, IO, List, Real, RealFns, RenderWithPixels, Rope, SceneUtilities, ShapeUtilities, SurfaceRender, ThreeDBasics, Vector2 EXPORTS MappedAndSolidTexture = BEGIN Context: TYPE ~ ThreeDBasics.Context; RGB: TYPE ~ ThreeDBasics.RGB; Box: TYPE ~ ThreeDBasics.Box; Rectangle: TYPE ~ ThreeDBasics.Rectangle; Pair: TYPE ~ ThreeDBasics.Pair; -- [ x, y: REAL]; PairSequence: TYPE ~ ThreeDBasics.PairSequence; Triple: TYPE ~ ThreeDBasics.Triple; -- [ x, y, z: REAL] TripleSequence: TYPE ~ ThreeDBasics.TripleSequence; VertexInfo: TYPE ~ ThreeDBasics.VertexInfo; VertexInfoSequence: TYPE ~ ThreeDBasics.VertexInfoSequence; IntSequence: TYPE ~ ThreeDBasics.IntSequence; RealSequence: TYPE ~ ThreeDBasics.RealSequence; TextureFunction: TYPE ~ ThreeDBasics.TextureFunction; TextureMap: TYPE ~ ThreeDBasics.TextureMap; SumSequence: TYPE ~ ThreeDBasics.SumSequence; SummedTexture: TYPE ~ ThreeDBasics.SummedTexture; Patch: TYPE ~ ThreeDBasics.Patch; Spot: TYPE ~ ThreeDBasics.Spot; SpotProc: TYPE ~ ThreeDBasics.SpotProc; ShadingClass: TYPE ~ ThreeDBasics.ShadingClass; ShapeInstance: TYPE ~ ThreeDBasics.ShapeInstance; ShapeProc: TYPE ~ ThreeDBasics.ShapeProc; LORA: TYPE = LIST OF REF ANY; Swap: PROCEDURE [p: Pair] RETURNS [Pair] ~ INLINE { RETURN[ [p.y, p.x] ]; }; 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.]; }; 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; justNoticeable: REAL ~ ScanConvert.justNoticeable; -- 0.02 registeredTextureFunctions: Atom.PropList _ NIL; -- keeps active solid texture functions defaultAuxiliaryData: REF Pair _ NEW[Pair _ [0.0, 0.0]]; maxTxtrRange: REAL _ 32.0; -- texture coordinate range, small numbers expected Init: PROC[] ~ { txtrShadingClass: ShadingClass _ [ type: $MappedAndSolidTexture, cnvrtVtx: GetLerpedVals, getColor: RecoverColor, loadShapeAux: LoadShapeAux, loadVtxAux: LoadVtxAux, lerpVtxAux: LerpVtxAux, shadeVtx: ShapeUtilities.ShadeVtx ]; ThreeDBasics.RegisterShadingClass[txtrShadingClass, $MappedAndSolidTexture]; RegisterTextureFunction[ $Spots, Spots ]; RegisterTextureFunction[ $Wurlitzer, Wurlitzer ]; RegisterTextureFunction[ $TwistedStripes, TwistedStripes ]; RegisterTextureFunction[ $BurlWood, BurlWood ]; RegisterTextureFunction[ $ZebraBurl, ZebraBurl ]; RegisterTextureFunction[ $Marble, Marble ]; }; CheckAndAddProcs: PUBLIC PROC[shape: REF ShapeInstance ] ~ { IF shape.shadingClass = NIL THEN ThreeDBasics.LoadShadingClass[shape, $MappedAndSolidTexture] ELSE IF shape.shadingClass.type # $MappedAndSolidTexture THEN { shape.shadingClass.type _ $MappedAndSolidTexture; shape.shadingClass.cnvrtVtx _ GetLerpedVals; shape.shadingClass.getColor _ RecoverColor; shape.shadingClass.loadShapeAux _ LoadShapeAux; shape.shadingClass.loadVtxAux _ LoadVtxAux; shape.shadingClass.lerpVtxAux _ LerpVtxAux; shape.shadingClass.shadeVtx _ ShapeUtilities.ShadeVtx; }; }; AddSolidTexture: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE, name: ATOM ] ~{ shape: REF ShapeInstance _ SceneUtilities.FindShape[ context, shapeName ]; txtrFn: TextureFunction _ GetRegisteredTextureFunction[name]; txtrFn.props _ PutProp[txtrFn.props, $Shape, shape]; CheckAndAddProcs[shape]; shape.shadingClass.texture _ List.Nconc1[ -- append to existing list shape.shadingClass.texture, NEW[TextureFunction _ txtrFn] ]; }; AddMappedTexture: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE, texture: REF TextureMap] ~ { shape: REF ShapeInstance _ SceneUtilities.FindShape[ context, shapeName ]; CheckAndAddProcs[shape]; shape.shadingClass.texture _ List.Nconc1[ shape.shadingClass.texture, texture ]; -- append }; SumAllMappedTextures: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE] ~ { shape: REF ShapeInstance _ SceneUtilities.FindShape[ context, shapeName ]; texture: LIST OF REF ANY _ shape.shadingClass.texture; FOR txtrList: LIST OF REF ANY _ texture, txtrList.rest UNTIL txtrList = NIL DO WITH txtrList.first SELECT FROM textureMap: REF TextureMap => SumMappedTexture[textureMap]; ENDCASE; ENDLOOP; }; RemoveAllTexture: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE] ~ { shape: REF ShapeInstance _ SceneUtilities.FindShape[ context, shapeName ]; shape.shadingClass.texture _ NIL; }; GetLerpedVals: ThreeDBasics.VtxToRealSeqProc ~ { maxLength: NAT _ IF data = $PixelShading THEN 16 ELSE 10; sourceAux: REF Pair _ NARROW[ source.aux ]; IF sourceAux = NIL THEN sourceAux _ defaultAuxiliaryData; IF dest = NIL OR dest.maxLength < maxLength THEN dest _ NEW[RealSequence[maxLength]]; IF data = $PixelShading THEN { -- shade will be computed anew at each pixel dest[0] _ source.shade.r; dest[1] _ source.shade.g; dest[2] _ source.shade.b; dest[3] _ source.shade.t; dest[4] _ source.shade.exn; dest[5] _ source.shade.eyn; dest[6] _ source.shade.ezn; dest[7] _ source.coord.ex; dest[8] _ source.coord.ey; dest[9] _ source.coord.ez; dest[10] _ source.coord.sz; -- for depth buffering dest[11] _ sourceAux.x; dest[12] _ sourceAux.y; dest[13] _ source.coord.x; dest[14] _ source.coord.y; dest[15] _ source.coord.z; } ELSE { -- Shade will only be multiplied or interpolated dest[0] _ source.shade.er; dest[1] _ source.shade.eg; dest[2] _ source.shade.eb; dest[3] _ source.shade.et; dest[4] _ source.coord.sz; -- for depth buffering dest[5] _ sourceAux.x; dest[6] _ sourceAux.y; dest[7] _ source.coord.x; dest[8] _ source.coord.y; dest[9] _ source.coord.z; }; dest.length _ maxLength; RETURN [dest]; }; RecoverColor: SpotProc ~ { IF shading.texture # NIL THEN GetTxtrAt[context, shading, spot]; IF shading.texture # NIL OR spot.val.length > 15 THEN RenderWithPixels.ShadeSpot[context, shading, spot]; }; LoadShapeAux: PUBLIC ShapeProc ~ { -- load aux field in vtx xMin, yMin, xRng, yRng: REAL _ 0.5; auxInfo: REF PairSequence _ NEW[ PairSequence[shape.shade.length] ]; WITH data SELECT FROM vtces: REF VertexInfoSequence => { FOR i: NAT IN [0..shape.shade.length) DO auxInfo[i] _ NARROW[vtces[i].aux, REF Pair]^; ENDLOOP; auxInfo.length _ shape.shade.length; }; pairs: REF PairSequence => auxInfo _ pairs; ENDCASE => SIGNAL ThreeDBasics.Error[[$Unimplemented, "Unrecognized type"]]; FOR i: NAT IN [0..shape.shade.length) DO IF i = 0 THEN { xRng _ xMin _ auxInfo[i].x; yRng _ yMin _ auxInfo[i].y; } ELSE { IF xRng < auxInfo[i].x THEN xRng _ auxInfo[i].x; IF yRng < auxInfo[i].y THEN yRng _ auxInfo[i].y; IF xMin > auxInfo[i].x THEN xMin _ auxInfo[i].x; IF yMin > auxInfo[i].y THEN yMin _ auxInfo[i].y; }; ENDLOOP; xRng _ xRng - xMin; yRng _ yRng - yMin; shape.shadingProps _ PutProp[ shape.shadingProps, $TxtrCoordRange, NEW[ Pair _ [xRng, yRng ] ] ]; IF xRng > maxTxtrRange OR yRng > maxTxtrRange THEN SIGNAL ThreeDBasics.Error[[$MisMatch, "Suspiciously big texture coords"]]; shape.shadingProps _ PutProp[ shape.shadingProps, $AuxiliaryVtxData, auxInfo]; RETURN[ shape ]; }; LoadVtxAux: PUBLIC ThreeDBasics.VertexInfoProc ~ { -- load aux field in vtx vtxAux: REF Pair _ NEW[Pair]; WITH data SELECT FROM input: LORA => { auxInfo: REF PairSequence _ NARROW[ input.first ]; index: INTEGER _ NARROW[ input.rest.first, REF INTEGER ]^; vtxAux^ _ auxInfo[index]; -- texture coords }; txtr: REF Pair => { vtxAux.x _ txtr.x; vtxAux.y _ txtr.y; }; ENDCASE => SIGNAL ThreeDBasics.Error[[$Unimplemented, "Unrecognized type"]]; vtx.aux _ vtxAux; RETURN[ vtx ]; }; LerpVtxAux: PUBLIC ThreeDBasics.VertexInfoProc ~ { -- linear interpolation for texture coords input: LORA _ NARROW[data]; vtxAux: REF Pair _ NEW[Pair]; vtxa: REF Pair _ NARROW[ input.first ]; vtxb: REF Pair _ NARROW[ input.rest.first ]; a: REAL _ NARROW[input.rest.rest.first, REF REAL]^; b: REAL _ NARROW[input.rest.rest.rest.first, REF REAL]^; vtxAux.x _ vtxa.x*a + vtxb.x*b; vtxAux.y _ vtxa.y*a + vtxb.y*b; vtx.aux _ vtxAux; RETURN[ vtx ]; }; AdjustTexture: PUBLIC PROC[poly: REF Patch, texture: LORA, halfXRange, halfYRange: REAL _ .5] ~ { mappedTexture: BOOLEAN _ FALSE; FOR txtrList: LORA _ NARROW[texture], txtrList.rest UNTIL txtrList = NIL DO WITH txtrList.first SELECT FROM -- look for mapped texture texture: REF ThreeDBasics.TextureMap => mappedTexture _ TRUE; ENDCASE; ENDLOOP; IF mappedTexture THEN { maxXtxtr, maxYtxtr, minXtxtr, minYtxtr: REAL; FOR i: CARDINAL IN [0..poly.nVtces) DO -- find maxima and minima txtr: REF Pair _ NARROW[poly[i].aux]; IF i = 0 THEN { maxXtxtr _ minXtxtr _ txtr.x; maxYtxtr _ minYtxtr _ txtr.y; } ELSE { IF maxXtxtr < txtr.x THEN maxXtxtr _ txtr.x; IF maxYtxtr < txtr.y THEN maxYtxtr _ txtr.y; IF minXtxtr > txtr.x THEN minXtxtr _ txtr.x; IF minYtxtr > txtr.y THEN minYtxtr _ txtr.y; }; ENDLOOP; IF maxXtxtr - minXtxtr > halfXRange THEN { -- seam in x FOR i: CARDINAL IN [0..poly.nVtces) DO -- push small ones beyond maximum txtr: REF Pair _ NARROW[poly[i].aux]; WHILE maxXtxtr - txtr.x > halfXRange DO txtr.x _ txtr.x + 1.0; ENDLOOP; ENDLOOP; minXtxtr _ maxXtxtr; FOR i: CARDINAL IN [0..poly.nVtces) DO -- find minimum txtr: REF Pair _ NARROW[poly[i].aux]; IF minXtxtr > txtr.x THEN minXtxtr _ txtr.x; ENDLOOP; }; IF maxYtxtr - minYtxtr > halfYRange THEN { -- seam in y FOR i: CARDINAL IN [0..poly.nVtces) DO -- push small ones beyond maximum txtr: REF Pair _ NARROW[poly[i].aux]; WHILE maxYtxtr - txtr.y > halfYRange DO txtr.y _ txtr.y + 1.0; ENDLOOP; ENDLOOP; minYtxtr _ maxYtxtr; FOR i: CARDINAL IN [0..poly.nVtces) DO -- find minimum txtr: REF Pair _ NARROW[poly[i].aux]; IF minYtxtr > txtr.y THEN minYtxtr _ txtr.y; ENDLOOP; }; minXtxtr _ Real.Float[Real.Fix[minXtxtr]]; minYtxtr _ Real.Float[Real.Fix[minYtxtr]]; FOR i: CARDINAL IN [0..poly.nVtces) DO -- adjust everything to minima under 1.0 txtr: REF Pair _ NARROW[poly[i].aux]; txtr.x _ txtr.x - minXtxtr; txtr.y _ txtr.y - minYtxtr; ENDLOOP; }; }; GetTxtrAt: PUBLIC SpotProc ~ { texture: LORA _ shading.texture; FOR txtrList: LORA _ texture, txtrList.rest UNTIL txtrList = NIL DO WITH txtrList.first SELECT FROM -- textures evaluated top to bottom texture: REF TextureMap => -- modify with mapped texture WITH texture.pixels SELECT FROM buf: ImagerPixel.PixelMap => IF texture.type = $Bump THEN SIGNAL ThreeDBasics.Error[[$MisMatch, "Need summed area table"]] ELSE SimpleTexture[spot, buf, texture.type]; sumMap: REF SummedTexture => IF texture.type = $Bump THEN { ref: REF ANY _ Atom.GetPropFromList[texture.props, $BumpScale]; bumpScale: REAL _ IF ref # NIL THEN NARROW[ref, REF REAL]^ ELSE 1.0; BumpTexture[spot, sumMap, bumpScale]; } ELSE SumMapTexture[spot, sumMap, texture.type]; ENDCASE => ERROR; txtrFn: REF TextureFunction => -- solid or other function-based texture txtrFn.proc[context, shading, spot, txtrFn.props]; ENDCASE => SIGNAL ThreeDBasics.Error[[$Unimplemented, "Unknown texture type"]]; ENDLOOP; }; SimpleTexture: PROC[spot: REF Spot, map: ImagerPixel.PixelMap, type: ATOM] ~ { GetTxtrAddress: PROC[buf: ImagerPixel.PixelMap, x, y: REAL] RETURNS[ txtrX, txtrY: INTEGER ] ~ { bufWidth: NAT _ buf.box.max.f - buf.box.min.f; bufHeight: NAT _ buf.box.max.s - buf.box.min.s; txtrX _ Real.Fix[x * bufWidth] MOD bufWidth; IF txtrX < 0 THEN txtrX _ txtrX + bufWidth; txtrY _ Real.Fix[y * bufHeight] MOD bufHeight; IF txtrY < 0 THEN txtrY _ txtrY + bufHeight; }; pixel: ImagerPixel.PixelBuffer _ ImagerPixel.ObtainScratchPixels[map.samplesPerPixel, 1]; x, y: INTEGER; txtrX: NAT ~ spot.val.length - 5; txtrY: NAT ~ txtrX+1; [x, y] _ GetTxtrAddress[map, spot.val[txtrX], spot.val[txtrY]]; ImagerPixel.GetPixels[self: map, pixels: pixel, initIndex: [f: x, s: y+map.box.min.s], count: 1]; SELECT type FROM $Color => FOR i: NAT IN [0..3) DO txtrValue: REAL _ pixel[i][0] / 256.0 ; spot.val[i] _ spot.val[i] * txtrValue; ENDLOOP; $ColorAndTransmittance => { surfTrans: REAL _ 1.0 - spot.val[3]; txtrTrans: REAL _ 1.0 - pixel[3][0] /256.0; FOR i: NAT IN [0..3) DO txtrVal: REAL _ pixel[i][0] / 256.0 ; spot.val[i] _ txtrTrans * surfTrans * spot.val[i] -- lerp spot reduced by transmittance + (1.0 - txtrTrans) * spot.val[i] * txtrVal; -- with spot times texture ENDLOOP; spot.val[3] _ spot.val[3] * txtrTrans; -- transmittances multiply }; $IntensityandTransmittance => { -- alpha channels provide coverage = 1.0 - transmtnce surfTrans: REAL _ 1.0 - spot.val[3]; txtrVal: REAL _ 1.0 - pixel[0][0] / 256.0 ; txtrTrans: REAL _ 1.0 - pixel[1][0] / 256.0 ; FOR i: NAT IN [0..3) DO spot.val[i] _ txtrTrans * surfTrans * spot.val[i] -- lerp spot reduced by transmittance + (1.0 - txtrTrans) * spot.val[i] * txtrVal; -- with spot times texture ENDLOOP; spot.val[3] _ spot.val[3] * txtrTrans; -- transmittances multiply }; $Intensity => { txtrValue: REAL _ pixel[0][0] / 256.0 ; FOR i: NAT IN [0..3) DO spot.val[i] _ spot.val[i] * txtrValue; ENDLOOP; }; ENDCASE => SIGNAL ThreeDBasics.Error[[$MisMatch, "Unknown texture type"]]; ImagerPixel.ReleaseScratchPixels[pixel]; }; SumMapTexture: PROC[spot: REF Spot, txtrSum: REF SummedTexture, type: ATOM] ~ { maxTxtrX: REAL _ txtrSum[0][0].length; -- width (thus max x address) of texture maxTxtrY: REAL _ txtrSum[0].length; -- height (thus max y address) of texture txtrX: NAT ~ spot.val.length - 5; txtrY: NAT ~ txtrX+1; txtrXIncr: REAL _ 0.5 * MAX[ ABS[spot.yIncr[txtrX]], ABS[spot.xIncr[txtrX]] ]; txtrYIncr: REAL _ 0.5 * MAX[ ABS[spot.yIncr[txtrY]], ABS[spot.xIncr[txtrY]] ]; area: REAL _ 4 * txtrXIncr * maxTxtrX * txtrYIncr * maxTxtrY; WHILE spot.val[txtrX] - txtrXIncr < 0.0 DO spot.val[txtrX] _ spot.val[txtrX] + 1.0; ENDLOOP; WHILE spot.val[txtrY] - txtrYIncr < 0.0 DO spot.val[txtrY] _ spot.val[txtrY] + 1.0; ENDLOOP; SELECT type FROM $Color => FOR i: NAT IN [0..3) DO txtrValue: REAL _ SumValues[spot, txtrSum[i], txtrX, txtrY, txtrXIncr, txtrYIncr, area] / 256.0; spot.val[i] _ MAX[ 0.0, spot.val[i] * txtrValue ]; ENDLOOP; $ColorAndTransmittance => { surfTrans: REAL _ 1.0 - spot.val[3]; txtrTrans: REAL _ SumValues[ spot, txtrSum[3], txtrX, txtrY, txtrXIncr, txtrYIncr, area ] /256.0; txtrTrans _ 1.0 - MAX[ 0.0, txtrTrans]; -- from alpha to transmittance FOR i: NAT IN [0..3) DO txtrVal: REAL _ SumValues[ spot, txtrSum[i], txtrX, txtrY, txtrXIncr, txtrYIncr, area ] /256.0; spot.val[i] _ txtrTrans * surfTrans * spot.val[i] -- lerp spot reduced by trans. + (1.0 - txtrTrans) * spot.val[i] * txtrVal; -- with spot times texture spot.val[i] _ MAX[ 0.0, spot.val[i]]; ENDLOOP; spot.val[3] _ spot.val[3] * txtrTrans; -- transmittances multiply }; $IntensityandTransmittance => { txtrVal: REAL _ SumValues[spot, txtrSum[0], txtrX, txtrY, txtrXIncr, txtrYIncr, area] / 256.0; surfTrans: REAL _ 1.0 - spot.val[3]; txtrTrans: REAL _ SumValues[spot, txtrSum[1], txtrX, txtrY, txtrXIncr, txtrYIncr, area] / 256.0; txtrVal _ MAX[ 0.0, txtrVal]; txtrTrans _ 1.0 - MAX[ 0.0, txtrTrans]; -- from alpha to transmittance FOR i: NAT IN [0..3) DO spot.val[i] _ txtrTrans * surfTrans * spot.val[i] -- lerp spot reduced by trans. + (1.0 - txtrTrans) * spot.val[i] * txtrVal; -- with spot times texture spot.val[i] _ MAX[ 0.0, spot.val[i]]; ENDLOOP; spot.val[3] _ spot.val[3] * txtrTrans; -- transmittances multiply }; $Intensity => { txtrValue: REAL _ SumValues[spot, txtrSum[0], txtrX, txtrY, txtrXIncr, txtrYIncr, area] / 256.0; IF txtrValue < 0.0 THEN txtrValue _ 0.0; IF spot.xySwapped THEN txtrValue _ 0.5 * txtrValue; FOR i: NAT IN [0..3) DO spot.val[i] _ MAX[ 0.0, spot.val[i] * txtrValue ]; ENDLOOP; }; ENDCASE => SIGNAL ThreeDBasics.Error[[$MisMatch, "Unknown texture type"]]; }; BumpTexture: PROC[spot: REF Spot, txtrSum: REF SummedTexture, bumpScale: REAL] ~ { txtrX: NAT ~ spot.val.length - 5; txtrY: NAT ~ txtrX+1; Positivize: PROC[] ~ { WHILE spot.val[txtrX] - maxXIncr < 0.0 DO spot.val[txtrX] _ spot.val[txtrX] + 1.0; ENDLOOP; WHILE spot.val[txtrY] - maxYIncr < 0.0 DO spot.val[txtrY] _ spot.val[txtrY] + 1.0; ENDLOOP; }; nmlX: NAT ~ 4; nmlY: NAT ~ nmlX + 1; nmlZ: NAT ~ nmlX + 2; maxTxtrX: REAL _ txtrSum[0][0].length; -- width (thus max x address) of texture maxTxtrY: REAL _ txtrSum[0].length; -- height (thus max y address) of texture maxXIncr, maxYIncr, area, length: REAL; txtrXIncr: Pair _ [spot.xIncr[txtrX], spot.xIncr[txtrY]]; txtrYIncr: Pair _ [-txtrXIncr.y, txtrXIncr.x]; -- rotate txtrXIncr 90 deg. cosA: REAL _ Vector2.Dot[ Vector2.Unit[txtrYIncr], Vector2.Unit[[spot.yIncr[txtrX], spot.yIncr[txtrY]]] ]; txtrYIncr _ Vector2.Mul[txtrYIncr, ABS[cosA]]; IF spot.xySwapped THEN { -- compensate for sideways scanning in tiler txtrYIncr.x _ -txtrYIncr.x; txtrYIncr.y _ -txtrYIncr.y; [[txtrXIncr.x, txtrYIncr.x]] _ Swap[[txtrXIncr.x, txtrYIncr.x]]; [[txtrXIncr.y, txtrYIncr.y]] _ Swap[[txtrXIncr.y, txtrYIncr.y]]; }; maxXIncr _ 0.5 * MAX[ ABS[txtrXIncr.x], ABS[txtrYIncr.x] ]; maxYIncr _ 0.5 * MAX[ ABS[txtrXIncr.y], ABS[txtrYIncr.y] ]; area _ 4 * maxXIncr * maxTxtrX * maxYIncr * maxTxtrY; length _ Vector2.Length[ txtrXIncr ]; IF length < 2.0/maxTxtrX THEN txtrXIncr _ Vector2.Mul[ txtrXIncr, 2.0 / (length*maxTxtrX) ]; length _ Vector2.Length[ txtrYIncr ]; IF length < 2.0/maxTxtrX THEN txtrYIncr _ Vector2.Mul[ txtrYIncr, 2.0 / (length*maxTxtrX) ]; SELECT txtrSum.length FROM 1 => { txtrValue, txtrValueX, txtrValueY, cosA, sinA, length: REAL; perturbDir, tnml, nml: Triple; Positivize[]; -- ensure positive texture coordinate values txtrValue _ SumValues[spot, txtrSum[0], txtrX, txtrY, maxXIncr, maxYIncr, area] / 256.0; spot.val[txtrX] _ spot.val[txtrX] + txtrXIncr.x; -- get txtr offset one pixel to right spot.val[txtrY] _ spot.val[txtrY] + txtrXIncr.y; Positivize[]; txtrValueX _ SumValues[spot, txtrSum[0], txtrX, txtrY, maxXIncr, maxYIncr, area] /256.0; spot.val[txtrX] _ spot.val[txtrX] - txtrXIncr.x; -- get original texture value back spot.val[txtrY] _ spot.val[txtrY] - txtrXIncr.y; spot.val[txtrX] _ spot.val[txtrX] + txtrYIncr.x; -- get txtr offset one pixel above spot.val[txtrY] _ spot.val[txtrY] + txtrYIncr.y; Positivize[]; txtrValueY _ SumValues[spot, txtrSum[0], txtrX, txtrY, maxXIncr, maxYIncr, area] /256.0; perturbDir _ G3dVector.Cross[ [maxTxtrX*txtrXIncr.x, maxTxtrY*txtrXIncr.y, bumpScale*(txtrValueX - txtrValue)], [maxTxtrX*txtrYIncr.x, maxTxtrY*txtrYIncr.y, bumpScale*(txtrValueY - txtrValue)] ]; perturbDir.z _ ABS[perturbDir.z]; perturbDir _ G3dVector.Normalize[perturbDir]; length _ RealFns.SqRt[ Sqr[perturbDir.x] + Sqr[perturbDir.z] ]; nml _ [spot.val[nmlX], spot.val[nmlY], spot.val[nmlZ]]; cosA _ perturbDir.z / length; sinA _ perturbDir.x / length; tnml.x _ nml.x * cosA + nml.z * sinA; tnml.y _ nml.y; tnml.z _ - nml.x * sinA + nml.z * cosA; nml _ G3dVector.Normalize[tnml]; length _ RealFns.SqRt[ Sqr[perturbDir.z] + Sqr[perturbDir.y] ]; cosA _ perturbDir.z / length; sinA _ perturbDir.y / length; tnml.x _ nml.x; tnml.y _ nml.y * cosA + nml.z * sinA; tnml.z _ - nml.y * sinA + nml.z * cosA; nml _ G3dVector.Normalize[tnml]; spot.val[nmlX] _ nml.x; spot.val[nmlY] _ nml.y; spot.val[nmlZ] _ nml.z; }; ENDCASE => SIGNAL ThreeDBasics.Error[[$MisMatch, "Wrong no. of samples in texture"]]; }; GetLerpedValue: PROC[ llVal, ulVal, urVal, lrVal: INT, xPos, yPos: REAL ] RETURNS[ intPart: INT, fracPart: REAL ] ~ { lowerFrac, upperFrac: REAL; lowerInt, upperInt: INT; lowerFrac _ xPos * (lrVal - llVal); -- lerp upper values lowerInt _ Real.Fix[ lowerFrac ]; lowerFrac _ lowerFrac - lowerInt; lowerInt _ llVal + lowerInt; upperFrac _ xPos * (urVal - ulVal); -- lerp lower values upperInt _ Real.Fix[ upperFrac ]; upperFrac _ upperFrac - upperInt; upperInt _ ulVal + upperInt; fracPart _ yPos * (upperInt - lowerInt); -- lerp upper and lower lerps intPart _ Real.Fix[ fracPart ]; fracPart _ fracPart - intPart; intPart _ intPart + lowerInt; fracPart _ fracPart + lowerFrac + yPos * (upperFrac - lowerFrac); }; CorrectSum: PROC[txtrSum: REF SumSequence, x, y: NAT] RETURNS[INT] ~ { maxTxtrX: NAT _ txtrSum[0].length-1; maxTxtrY: NAT _ txtrSum.length-1; IF x < txtrSum[0].length AND y < txtrSum.length THEN { RETURN[ txtrSum[y][x] ] } ELSE IF x >= txtrSum[0].length AND y >= txtrSum.length THEN { x _ x - txtrSum[0].length; y _ y - txtrSum.length; RETURN[ CorrectSum[txtrSum, x, y] + txtrSum[maxTxtrY][maxTxtrX] + CorrectSum[txtrSum, x, maxTxtrY] + CorrectSum[txtrSum, maxTxtrX, y] ]; } ELSE IF x >= txtrSum[0].length THEN { x _ x - txtrSum[0].length; RETURN[ CorrectSum[txtrSum, x, y] + txtrSum[y][maxTxtrX] ]; } ELSE { -- IF y >= txtrSum.length y _ y - txtrSum.length; RETURN[ CorrectSum[txtrSum, x, y] + txtrSum[maxTxtrY][x] ]; }; }; GetValueAt: PROC[txtrSum: REF SumSequence, x, y: REAL] RETURNS[ intPart: INT, fracPart: REAL ] ~ { xPos, yPos: REAL; lX, lY, rX, uY: NAT; xPos _ x * txtrSum[0].length; lX _ Real.Fix[xPos]; rX _ lX + 1; yPos _ y * txtrSum.length; lY _ Real.Fix[yPos]; uY _ lY + 1; xPos _ xPos - Real.Fix[xPos]; -- get fractional part yPos _ yPos - Real.Fix[yPos]; [ intPart, fracPart ] _ GetLerpedValue[ CorrectSum[txtrSum, lX, lY], CorrectSum[txtrSum, lX, uY], CorrectSum[txtrSum, rX, uY], CorrectSum[txtrSum, rX, lY], xPos, yPos ]; }; SumValues: PROC[ spot: REF Spot, txtrSum: REF SumSequence, txtrX, txtrY: NAT, txtrXIncr, txtrYIncr, area: REAL ] RETURNS[ txtrValue: REAL ] ~ { i1, i2, i3, i4: INT; f1, f2, f3, f4: REAL; IF area < 4.0 THEN { -- summed area tables don't work if all samples lie in the same pixel area _ 4.0; -- filtering works best if done over two pixel widths txtrXIncr _ 1.0 / txtrSum[0].length; txtrYIncr _ 1.0 / txtrSum.length; }; [ i1, f1 ] _ GetValueAt[ txtrSum, spot.val[txtrX] + txtrXIncr, spot.val[txtrY] + txtrYIncr ]; [ i2, f2 ] _ GetValueAt[ txtrSum, spot.val[txtrX] - txtrXIncr, spot.val[txtrY] - txtrYIncr ]; [ i3, f3 ] _ GetValueAt[ txtrSum, spot.val[txtrX] + txtrXIncr, spot.val[txtrY] - txtrYIncr ]; [ i4, f4 ] _ GetValueAt[ txtrSum, spot.val[txtrX] - txtrXIncr, spot.val[txtrY] + txtrYIncr ]; txtrValue _ (i1 + i2 - i3 - i4 ) / area; txtrValue _ txtrValue + (f1 + f2 - f3 - f4 ) / area; }; RegisterTextureFunction: PUBLIC PROC[ name: ATOM, proc: SpotProc, props: Atom.PropList _ NIL ] ~ { registeredTextureFunctions _ Atom.PutPropOnList[ registeredTextureFunctions, name, NEW[ TextureFunction _ [name, proc, props] ] ]; }; GetRegisteredTextureFunction: PUBLIC PROC[name: ATOM] RETURNS[ txtrFn: TextureFunction ] ~ { ref: REF TextureFunction _ NARROW[ Atom.GetPropFromList[registeredTextureFunctions, name] ]; IF ref # NIL THEN txtrFn _ ref^ ELSE SIGNAL ThreeDBasics.Error[[$MisMatch, "Unregistered procedure"]] }; Spots: SpotProc ~ { x: NAT _ spot.val.length-3; y: NAT _ x+1; z: NAT _ x+2; -- object space coordinate r: NAT ~ 0; g: NAT ~ 1; b: NAT ~ 2; t: NAT ~ 3; intensity: REAL _ RealFns.Sin[10.0 * spot.val[x] ] * RealFns.Sin[14.0 * spot.val[y] ] * RealFns.Sin[20.0 * spot.val[z] ]; intensity _ (intensity + 1.0) / 2.0; spot.val[r] _ spot.val[r] * intensity; spot.val[g] _ spot.val[g] * intensity; spot.val[b] _ spot.val[b] * intensity; spot.val[t] _ spot.val[t] * intensity; }; Wurlitzer: SpotProc ~ { x: NAT _ spot.val.length-3; y: NAT _ x+1; z: NAT _ x+2; -- object space coordinate r: NAT ~ 0; g: NAT ~ 1; b: NAT ~ 2; t: NAT ~ 3; intensity: REAL _ RealFns.Sin[10.0 * spot.val[x] ] * RealFns.Sin[14.0 * spot.val[y] ] * RealFns.Sin[20.0 * spot.val[z] ]; intensity _ (intensity + 1.0) / 2.0; spot.val[r] _ spot.val[r] * (RealFns.Sin[10.0*spot.val[x]] +1.0) / 2.0; spot.val[g] _ spot.val[g] * (RealFns.Sin[14.0*spot.val[y]] +1.0) / 2.0; spot.val[b] _ spot.val[b] * (RealFns.Sin[20.0*spot.val[z]] +1.0) / 2.0; spot.val[t] _ spot.val[t] * intensity; }; TwistedStripes: SpotProc ~ { x: NAT _ spot.val.length-3; y: NAT _ x+1; z: NAT _ x+2; -- object space coordinate r: NAT ~ 0; g: NAT ~ 1; b: NAT ~ 2; t: NAT ~ 3; angle: REAL _ 3.1416 * spot.val[z]; -- rotation varies with z cosAngle: REAL _ RealFns.Cos[angle]; sinAngle: REAL _ RealFns.Sin[angle]; intensity: REAL _ RealFns.Sin[40.0 * -- x component of rotated x-y vector (cosAngle * spot.val[x] + sinAngle * spot.val[y]) ]; transmittance: REAL; intensity _ (intensity + 1.0) / 2.0; transmittance _ (1.0 - intensity); spot.val[t] _ spot.val[t] * transmittance; spot.val[r] _ spot.val[r] * intensity; spot.val[g] _ spot.val[g] * (1.0 - intensity); spot.val[b] _ spot.val[b] * intensity; }; BurlWood: SpotProc ~ { x: NAT _ spot.val.length-3; y: NAT _ x+1; z: NAT _ x+2; -- object space coordinate r: NAT ~ 0; g: NAT ~ 1; b: NAT ~ 2; t: NAT ~ 3; red, grn, blu: REAL; chaos: REAL _ Chaos[ spot.val[x], spot.val[y], spot.val[z] ]; midBrown: REAL _ RealFns.Sin[ chaos*8 + 7*spot.val[x] + 3* spot.val[y] ]; brownLayer: REAL _ ABS[ RealFns.Sin[midBrown] ]; greenLayer: REAL _ - brownLayer; perturb: REAL _ IF brownLayer > 0.0 THEN ABS[RealFns.Sin[40 * chaos + 50*spot.val[z] ]] ELSE ABS[RealFns.Sin[30 * chaos + 30*spot.val[x] ]]; brownPerturb: REAL _ perturb * .6 + .3; -- perturb up to .6 greenPerturb: REAL _ perturb * .2 + .8; -- perturb up to .2 grnPerturb: REAL _ perturb * .15 + .85; -- perturb up to .15 grn _ .5 * RealFns.Power[ABS[brownLayer], 0.3]; -- makes seams brownLayer _ RealFns.Power[(brownLayer + 1.0) / 2.0, 0.6] * brownPerturb; greenLayer _ RealFns.Power[(greenLayer + 1.0) / 2.0, 0.6] * greenPerturb; red _ (.6 * brownLayer + .35 * greenLayer) * 2 * grn; blu _ (.25 * brownLayer + .35 * greenLayer) * 2 * grn; grn _ grn * MAX[brownLayer, greenLayer] * grnPerturb; spot.val[r] _ spot.val[r] * red; spot.val[g] _ spot.val[g] * grn; spot.val[b] _ spot.val[b] * blu; }; ZebraBurl: SpotProc ~ { x: NAT _ spot.val.length-3; y: NAT _ x+1; z: NAT _ x+2; -- object space coordinate r: NAT ~ 0; g: NAT ~ 1; b: NAT ~ 2; t: NAT ~ 3; red, grn, blu: REAL; chaos: REAL _ Chaos[ spot.val[x], spot.val[y], spot.val[z] ]; midBrown: REAL _ RealFns.Sin[ chaos*8 + 7*spot.val[x] + 3* spot.val[y] ]; brownLayer: REAL _ RealFns.Sin[midBrown]; greenLayer: REAL _ - brownLayer; perturb: REAL _ IF brownLayer > 0.0 THEN ABS[RealFns.Sin[40 * chaos + 50*spot.val[z] ]] ELSE ABS[RealFns.Sin[24 * chaos + 30*spot.val[x] ]]; brownPerturb: REAL _ perturb * .6 + .3; -- perturb up to .6 greenPerturb: REAL _ perturb * .2 + .8; -- perturb up to .2 grnPerturb: REAL _ perturb * .15 + .85; -- perturb up to .15 grn _ .5 * RealFns.Power[ABS[brownLayer], 0.3]; -- makes seams brownLayer _ RealFns.Power[(brownLayer + 1.0) / 2.0, 0.6] * brownPerturb; greenLayer _ RealFns.Power[(greenLayer + 1.0) / 2.0, 0.6] * greenPerturb; red _ (.6 * brownLayer + .35 * greenLayer) * 2 * grn; blu _ (.25 * brownLayer + .35 * greenLayer) * 2 * grn; grn _ grn * MAX[brownLayer, greenLayer] * grnPerturb; spot.val[r] _ spot.val[r] * red; spot.val[g] _ spot.val[g] * grn; spot.val[b] _ spot.val[b] * blu; }; Marble: SpotProc ~ { x: NAT _ spot.val.length-3; y: NAT _ x+1; z: NAT _ x+2; -- object space coordinate r: NAT ~ 0; g: NAT ~ 1; b: NAT ~ 2; t: NAT ~ 3; intensity: REAL _ RealFns.Sin[Chaos[ spot.val[x], spot.val[y], spot.val[z] ]*8 + 7*spot.val[z]]; intensity _ (intensity + 1.0) / 2.0; intensity _ RealFns.Power[intensity, 0.77]; spot.val[r] _ spot.val[r] * intensity; spot.val[g] _ spot.val[g] * intensity; spot.val[b] _ spot.val[b] * intensity; }; Chaos: PUBLIC PROC[x, y, z: REAL] RETURNS [REAL] ~ { f: REAL _ 1.; s, t: REAL _ 0.; FOR n: INT IN [0..7) DO s _ Noise[x * f, y * f, z * f]; t _ t + ABS[s] / f; f _ 2 * f; ENDLOOP; RETURN [t]; }; realScale: REAL _ 2.0 / LAST[CARDINAL]; RTable: TYPE ~ RECORD[SEQUENCE length: NAT OF REAL]; rTable: REF RTable _ NIL; Noise: PUBLIC PROC[vx, vy, vz: REAL] RETURNS [REAL] ~ { R: PROC[i, j, k: REAL] RETURNS [CARDINAL] ~ TRUSTED { A: TYPE ~ ARRAY [0..3) OF REAL; a: A _ [i * .12345 , j * .12345 , k * .12345 ]; aPointer: LONG POINTER TO A ~ @a; h: CARDINAL _ Checksum.ComputeChecksum[nWords: SIZE[A], p: LOOPHOLE[aPointer]]; RETURN [h]; }; SCurve: PROC[x: REAL] RETURNS [REAL] ~ { RETURN [x * x * (3 - 2 * x)]; }; m: NAT; ix, iy, iz: INT; x, y, z, jx, jy, jz, sx, sy, sz, tx, ty, tz, s, f: REAL; IF rTable = NIL THEN { rTable _ NEW[RTable[259]]; FOR n:INT IN [0..259) DO r:REAL _ n; rTable[n] _ R[r, r, r] * realScale - 1.; ENDLOOP; }; x _ vx + 1000.; y _ vy + 1000.; z _ vz + 1000.; ix _ Real.Fix[x]; iy _ Real.Fix[y]; iz _ Real.Fix[z]; sx _ SCurve[x - ix]; sy _ SCurve[y - iy]; sz _ SCurve[z - iz]; tx _ 1. - sx; ty _ 1. - sy; tz _ 1. - sz; f _ 0.; -- initialize sum to zero. FOR n: INT IN [0..8) DO -- sum together 8 local fields from neighboring lattice pts. SELECT n FROM -- each of 8 corners of the surrounding unit cube. 0 => {jx _ ix ; jy _ iy ; jz _ iz ; s _ tx * ty * tz }; 1 => {jx _ ix+1 ; s _ sx * ty * tz }; 2 => {jx _ ix ; jy _ iy+1 ; s _ tx * sy * tz }; 3 => {jx _ ix+1 ; s _ sx * sy * tz }; 4 => {jx _ ix ; jy _ iy ; jz _ iz+1 ; s _ tx * ty * sz }; 5 => {jx _ ix+1 ; s _ sx * ty * sz }; 6 => {jx _ ix ; jy _ iy+1 ; s _ tx * sy * sz }; 7 => {jx _ ix+1 ; s _ sx * sy * sz }; ENDCASE; m _ R[jx, jy, jz] MOD 256; f _ f + s * ( rTable[m]/2 + rTable[m+1]*(x-jx) + rTable[m+2]*(y-jy) + rTable[m+3]*(z-jz) ); ENDLOOP; RETURN [f]; }; EnableStreamProcs: PUBLIC PROC[ context: REF Context ] ~ { context.props _ Atom.PutPropOnList[ context.props, $TextureMapFromStream, NEW[ShapeProc _ TextureMapFromStream] ]; context.props _ Atom.PutPropOnList[ context.props, $TextureFunctionFromStream, NEW[ShapeProc _ TextureFunctionFromStream] ]; }; TextureMapFromStream: ShapeProc ~ { input: IO.STREAM _ NARROW[data]; txtrMap: REF TextureMap _ TextureFromAIS[ context: context, fileName: SceneUtilities.GetRope[input], type: IO.GetAtom[input] ]; AddMappedTexture[ context, shape.name, txtrMap ]; SELECT IO.GetAtom[input] FROM $FromVtxNos => MakeTxtrCoordsFromVtxNos[ context: context, shapeName: shape.name, vtcesInRow: IO.GetInt[input], numberOfRows: IO.GetInt[input], row0col0: [IO.GetInt[input], IO.GetInt[input]], rowNcol0: [IO.GetInt[input], IO.GetInt[input]], rowNcolM: [IO.GetInt[input], IO.GetInt[input]], row0ColM: [IO.GetInt[input], IO.GetInt[input]] ]; $FromNormals => MakeTxtrCoordsFromNormals[ context: context, shapeName: shape.name, botLeft: [IO.GetInt[input], IO.GetInt[input]], topLeft: [IO.GetInt[input], IO.GetInt[input]], topRight: [IO.GetInt[input], IO.GetInt[input]], botRight: [IO.GetInt[input], IO.GetInt[input]], sw: [IO.GetInt[input], IO.GetInt[input]], nw: [IO.GetInt[input], IO.GetInt[input]], ne: [IO.GetInt[input], IO.GetInt[input]], se: [IO.GetInt[input], IO.GetInt[input]] ]; $NoCoords => {}; ENDCASE => SIGNAL ThreeDBasics.Error[ [$Unimplemented, "Unknown texture coordType"] ]; [] _ SceneUtilities.GetRope[input]; -- ignore word "Scale" ScaleTxtrCoords[context, shape.name, IO.GetReal[input], IO.GetReal[input], IO.GetReal[input]]; RETURN[ shape ]; }; TextureFunctionFromStream: ShapeProc ~ { input: IO.STREAM _ NARROW[data]; AddSolidTexture[ context, shape.name, IO.GetAtom[input] ]; RETURN[ shape ]; }; ScaleTxtrCoords: PUBLIC PROC [ context: REF Context, shapeName: Rope.ROPE, scale: REAL, xRatio, yRatio: REAL _ 1.0 ] ~ { xScale, yScale: REAL; shape: REF ShapeInstance _ SceneUtilities.FindShape[ context, shapeName ]; auxInfo: REF PairSequence _ NARROW[GetProp[ shape.shadingProps, $AuxiliaryVtxData]]; refTriple: REF Triple _ NARROW[GetProp[shape.shadingProps, $TextureScale]]; IF refTriple = NIL THEN refTriple _ NEW[Triple _ [1.0, 1.0, 1.0]]; IF refTriple.x # 0.0 AND refTriple.y # 0.0 AND refTriple.z # 0.0 THEN { xScale _ scale*xRatio/(refTriple.x*refTriple.y); -- multiply by new, divide by old values yScale _ scale*yRatio/(refTriple.x*refTriple.z); }; refTriple^ _ [scale, xRatio, yRatio]; FOR i: NAT IN [0..auxInfo.length) DO auxInfo[i].x _ auxInfo[i].x * xScale; auxInfo[i].y _ auxInfo[i].y * yScale; ENDLOOP; shape.shadingProps _ PutProp[shape.shadingProps, $TextureScale, refTriple]; }; MakeTxtrCoordsFromVtxNos: PUBLIC PROC[ context: REF Context, shapeName: Rope.ROPE, vtcesInRow, numberOfRows: NAT, row0col0, rowNcol0, rowNcolM, row0ColM: Pair] ~ { shape: REF ShapeInstance _ SceneUtilities.FindShape[ context, shapeName ]; auxInfo: REF PairSequence _ NARROW[GetProp[shape.shadingProps, $AuxiliaryVtxData]]; args: LIST OF REAL; IF GetProp[ shape.fixedProps, $VertexTextureInFile] # NIL THEN { SIGNAL ThreeDBasics.Error[[$MisMatch, "Overwriting original texture coords, OK?"]]; shape.shadingProps _ Atom.RemPropFromList[shape.shadingProps, $VertexTextureInFile]; }; IF auxInfo = NIL THEN auxInfo _ NEW[ PairSequence[shape.shade.length] ]; IF row0ColM.x - row0col0.x > .3 * vtcesInRow OR rowNcolM.x - rowNcol0.x > .3 * vtcesInRow OR rowNcolM.y - row0ColM.y > .3 * numberOfRows OR rowNcol0.y - row0col0.y > .3 * numberOfRows THEN SIGNAL ThreeDBasics.Error[[$MisMatch, "Texture mapping dangerously dense"]]; FOR i: NAT IN [0..shape.shade.length) DO lPosX, lPosY, rPosX, rPosY: REAL; auxInfo[i].x _ Real.Float[i MOD vtcesInRow] / vtcesInRow; -- pct along row auxInfo[i].y _ Real.Float[i / vtcesInRow] / numberOfRows; -- pct across rows lPosX _ row0col0.x + auxInfo[i].y * (rowNcol0.x - row0col0.x);-- interp across rows lPosY _ row0col0.y + auxInfo[i].y * (rowNcol0.y - row0col0.y); rPosX _ row0ColM.x + auxInfo[i].y * (rowNcolM.x - row0ColM.x); rPosY _ row0ColM.y + auxInfo[i].y * (rowNcolM.y - row0ColM.y); auxInfo[i].x _ lPosX + auxInfo[i].x * (rPosX - lPosX); -- interp along row auxInfo[i].y _ lPosY + auxInfo[i].x * (rPosY - lPosY); ENDLOOP; auxInfo.length _ shape.shade.length; shape.shadingProps _ PutProp[ shape.shadingProps, $AuxiliaryVtxData, auxInfo]; shape.shadingProps _ PutProp[ shape.shadingProps, $TxtrCoordType, $FromVtxNos]; args _ CONS[row0ColM.y, NIL]; args _ CONS[row0ColM.x, args]; args _ CONS[rowNcolM.y, args]; args _ CONS[rowNcolM.x, args]; args _ CONS[rowNcol0.y, args]; args _ CONS[rowNcol0.x, args]; args _ CONS[row0col0.y, args]; args _ CONS[row0col0.x, args]; args _ CONS[Real.Float[numberOfRows], args]; args _ CONS[Real.Float[vtcesInRow], args]; shape.shadingProps _ PutProp[ shape.shadingProps, $TxtrCoordParams, args]; }; GetTxtrCoordsFromNormal: PUBLIC PROC[ context: REF Context, vtx: VertexInfo ] ~ { }; MakeTxtrCoordsFromNormals: PUBLIC PROC[ context: REF Context, shapeName: Rope.ROPE, botLeft: Pair _ [0.0, 0.0], topLeft: Pair _ [0.0, 1.0], topRight: Pair _ [1.0, 1.0], botRight: Pair _ [1.0, 0.0], sw: Pair _ [-180.0, -90.0], nw: Pair _ [-180.0, 90.0], ne: Pair _ [180.0, 90.0], se: Pair _ [180.0, -90.0] ] ~ { shape: REF ShapeInstance _ SceneUtilities.FindShape[ context, shapeName ]; auxInfo: REF PairSequence _ NARROW[GetProp[shape.shadingProps, $AuxiliaryVtxData]]; args: LIST OF REAL; poly: REF ThreeDBasics.PtrPatchSequence _ NARROW[shape.surface]; polyTags: ARRAY [0..16) OF BOOLEAN; lngtShift: REAL _ 0.0; -- latitude shift to allow < -180.0 and > 180.0 IF GetProp[shape.fixedProps, $VertexTextureInFile] # NIL THEN { SIGNAL ThreeDBasics.Error[[$MisMatch, "Overwriting original texture coords, OK?"]]; shape.shadingProps _ Atom.RemPropFromList[shape.shadingProps, $VertexTextureInFile]; }; IF auxInfo = NIL THEN auxInfo _ NEW[ PairSequence[shape.shade.length] ]; IF GetProp[shape.shadingProps, $VtxInfoComputed] = NIL THEN { shape.shadingClass.shadingType _ $Smooth; -- smooth shading forces computed normals SurfaceRender.ValidateContext[context]; -- make sure viewport, etc. is kosher }; IF MAX[sw.x, nw.x, se.x, ne.x] - MIN[sw.x, nw.x, se.x, ne.x] > 360.0 THEN SIGNAL ThreeDBasics.Error[[$MisMatch, "Longitude range exceeds 360 degrees"]]; IF MIN[sw.x, nw.x, se.x, ne.x] < -180.0 THEN lngtShift _ -180.0 - MIN[sw.x, nw.x, se.x, ne.x] -- positive shift if past -180 ELSE IF MAX[sw.x, nw.x, se.x, ne.x] > 180.0 THEN lngtShift _ 180.0 - MAX[sw.x, nw.x, se.x, ne.x]; -- negative shift if past 180 IF ABS[nw.y - sw.y] < 0.001 THEN nw.y _ sw.y + Sgn[nw.y - sw.y] * 0.001; -- stop div errs IF ABS[ne.y - se.y] < 0.001 THEN ne.y _ se.y + Sgn[ne.y - se.y] * 0.001; IF ABS[sw.x - se.x] < 0.001 THEN sw.x _ se.x + Sgn[sw.x - se.x] * 0.001; IF ABS[nw.x - ne.x] < 0.001 THEN nw.x _ ne.x + Sgn[nw.x - ne.x] * 0.001; FOR polyNumber: NAT IN [0..poly.length) DO minTxtrX, minTxtrY: REAL _ Real.LargestNumber; maxTxtrX: REAL _ 0.; FOR i: NAT IN [0..poly[polyNumber].nVtces) DO vtx: NAT _ poly[polyNumber].vtxPtr[i]; hypotenuse: REAL _ RealFns.SqRt[Sqr[shape.shade[vtx].yn] + Sqr[shape.shade[vtx].xn]]; longitude: REAL _ RealFns.ArcTanDeg[shape.shade[vtx].yn, shape.shade[vtx].xn]; latitude: REAL _ RealFns.ArcTanDeg[shape.shade[vtx].zn, hypotenuse]; lPosY: REAL _ (latitude - sw.y) / (nw.y - sw.y); -- percentage of distance on left edge rPosY: REAL _ (latitude - se.y) / (ne.y - se.y); lPosX: REAL _ sw.x + lPosY * (nw.x - sw.x); -- weighted average of positions rPosX: REAL _ se.x + rPosY * (ne.x - se.x); IF longitude > 180.0 - lngtShift THEN longitude _ -180.0 - (180.0 - longitude) ELSE IF longitude < -180.0 - lngtShift THEN longitude _ 180.0 + (longitude + 180.0); auxInfo[vtx].x _ (longitude - lPosX) / (rPosX - lPosX); -- percentage across auxInfo[vtx].y _ lPosY + auxInfo[vtx].x * (rPosY - lPosY); -- wtd av. % auxInfo[vtx].x _ MIN[ 1.0, MAX[0.0, auxInfo[vtx].x]]; auxInfo[vtx].y _ MIN[ 1.0, MAX[0.0, auxInfo[vtx].y]]; IF hypotenuse < 0.00001 THEN polyTags[i] _ TRUE -- catch unstable arithmetic ELSE { polyTags[i] _ FALSE; IF auxInfo[vtx].x < minTxtrX THEN minTxtrX _ auxInfo[vtx].x; IF auxInfo[vtx].x > maxTxtrX THEN maxTxtrX _ auxInfo[vtx].x; }; ENDLOOP; auxInfo.length _ shape.shade.length; shape.shadingProps _ PutProp[ shape.shadingProps, $AuxiliaryVtxData, auxInfo]; IF maxTxtrX - minTxtrX > .5 -- wrapping around seam, fix up coords THEN FOR i: NAT IN [0..poly[polyNumber].nVtces) DO vtx: NAT _ poly[polyNumber].vtxPtr[i]; IF maxTxtrX - auxInfo[vtx].x > .5 THEN auxInfo[vtx].x _ auxInfo[vtx].x + 1.; ENDLOOP; minTxtrX _ Real.LargestNumber; maxTxtrX _ 0.; FOR i: NAT IN [0..poly[polyNumber].nVtces) DO -- get corrected max and min IF polyTags[i] = FALSE THEN { vtx: NAT _ poly[polyNumber].vtxPtr[i]; IF auxInfo[vtx].x < minTxtrX THEN minTxtrX _ auxInfo[vtx].x; IF auxInfo[vtx].x > maxTxtrX THEN maxTxtrX _ auxInfo[vtx].x; }; ENDLOOP; FOR i: NAT IN [0..poly[polyNumber].nVtces) DO -- fix up unstable vertical normals vtx: NAT _ poly[polyNumber].vtxPtr[i]; IF polyTags[i] = TRUE THEN auxInfo[vtx].x _ (maxTxtrX + minTxtrX) / 2.; ENDLOOP; minTxtrX _ minTxtrY _ Real.LargestNumber; FOR i: NAT IN [0..poly[polyNumber].nVtces) DO -- slew according to corner coords vtx: NAT _ poly[polyNumber].vtxPtr[i]; lPosX: REAL _ botLeft.x + auxInfo[vtx].y * (topLeft.x - botLeft.x); lPosY: REAL _ botLeft.y + auxInfo[vtx].y * (topLeft.y - botLeft.y); rPosX: REAL _ botRight.x + auxInfo[vtx].y * (topRight.x - botRight.x); rPosY: REAL _ botRight.y + auxInfo[vtx].y * (topRight.y - botRight.y); auxInfo[vtx].x _ lPosX + auxInfo[vtx].x * (rPosX - lPosX); auxInfo[vtx].y _ lPosY + auxInfo[vtx].x * (rPosY - lPosY); IF auxInfo[vtx].x < minTxtrX THEN minTxtrX _ auxInfo[vtx].x; IF auxInfo[vtx].y < minTxtrY THEN minTxtrY _ auxInfo[vtx].y; ENDLOOP; minTxtrX _ Real.Float[Real.Fix[minTxtrX]]; minTxtrY _ Real.Float[Real.Fix[minTxtrY]]; FOR i: NAT IN [0..poly[polyNumber].nVtces) DO -- translate to origin vtx: NAT _ poly[polyNumber].vtxPtr[i]; auxInfo[vtx].x _ auxInfo[vtx].x - minTxtrX; auxInfo[vtx].y _ auxInfo[vtx].y - minTxtrY; ENDLOOP; ENDLOOP; shape.shadingProps _ PutProp[ shape.shadingProps, $TxtrCoordType, $FromNormals]; args _ CONS[botRight.y, NIL]; args _ CONS[botRight.x, args]; args _ CONS[topRight.y, args]; args _ CONS[topRight.x, args]; args _ CONS[topLeft.y, args]; args _ CONS[topLeft.x, args]; args _ CONS[botLeft.y, args]; args _ CONS[botLeft.x, args]; args _ CONS[se.y, args]; args _ CONS[se.x, args]; args _ CONS[ne.y, args]; args _ CONS[ne.x, args]; args _ CONS[nw.y, args]; args _ CONS[nw.x, args]; args _ CONS[sw.y, args]; args _ CONS[sw.x, args]; shape.shadingProps _ PutProp[ shape.shadingProps, $TxtrCoordParams, args]; }; TextureFromAIS: PUBLIC PROC[context: REF Context, fileName: Rope.ROPE, type: ATOM _ $Intensity, factor: REAL _ 1.0] RETURNS[texture: REF TextureMap] ~ { width, height: INTEGER _ 1024; renderMode: ATOM; bufContext: REF Context; FOR i: NAT IN [0..context.shapes.length) DO IF context.shapes[i].shadingClass.texture # NIL THEN FOR txtrList: LORA _ context.shapes[i].shadingClass.texture, txtrList.rest UNTIL txtrList = NIL DO WITH txtrList.first SELECT FROM texture: REF TextureMap => IF texture.type = type AND Rope.Equal[ fileName, NARROW[Atom.GetPropFromList[texture.props, $FileName], Rope.ROPE], FALSE ] THEN RETURN[texture]; -- if matched return texture map (may be summed) ENDCASE; ENDLOOP; ENDLOOP; renderMode _ IF type = $Color OR type = $ColorAndTransmittance THEN $FullColor ELSE $Gray; bufContext _ RenderWithPixels.GetContext[renderMode, width, height]; IF type = $ColorAndTransmittance THEN RenderWithPixels.AntiAliasing[bufContext]; bufContext.props _ context.props; -- bring along working directory, etc. [width, height] _ AISAnimation.GetAIS[ -- load pixelbuffer with AIS bits context: bufContext, fileRoot: fileName, center: FALSE]; IF width > bufContext.viewPort.w OR height > bufContext.viewPort.h THEN { bufContext.viewPort^ _ [0.0, 0.0, Real.Fix[width], Real.Fix[height]]; RenderWithPixels.AllocatePixelMemory[bufContext]; -- get bigger pixel buffer [width, height] _ AISAnimation.GetAIS[ -- load it up context: bufContext, fileRoot: fileName, center: FALSE]; }; context.props _ Atom.RemPropFromList[context.props, $TempPixels]; -- GetAIS sideffect bufContext.pixels.box.max.f _ width; bufContext.pixels.box.max.s _ MAX[height, 1024]; bufContext.pixels.box.min.s _ MAX[0, 1024 - height]; -- getAIS pushes image up against max texture _ NEW[TextureMap]; -- now, gin up TextureMap, using context.pixels texture.pixels _ bufContext.pixels; texture.type _ type; texture.props _ Atom.PutPropOnList[texture.props, $FileName, fileName]; IF type = $Bump THEN texture.props _ Atom.PutPropOnList[texture.props, $BumpScale, NEW[REAL _ factor]]; }; SumMappedTexture: PUBLIC PROC[texture: REF TextureMap] ~ { SumOneLine: PROC[ line: ImagerSample.SampleBuffer, sumMap: REF SumSequence, y: NAT ] RETURNS[REF SumSequence] ~ { IF sumMap = NIL THEN sumMap _ NEW[ SumSequence[box.max.s - box.min.s] ]; IF sumMap[y] = NIL THEN sumMap[y] _ NEW[ IntSequence[box.max.f] ]; sumMap[y].length _ box.max.f; FOR x: NAT IN [0..line.length) DO sumMap[y][x] _ INT32[line[x]]; IF x > 0 THEN { sumMap[y][x] _ sumMap[y][x] + sumMap[y][x - 1]; --add area to left IF y > 0 THEN -- x > 0 and y > 0: add area below minus area below and to left sumMap[y][x] _ sumMap[y][x] + (sumMap[y - 1][x] - sumMap[y - 1][x - 1]); } ELSE IF y > 0 THEN sumMap[y][x] _ sumMap[y][x] + sumMap[y - 1][x]; --add area below ENDLOOP; RETURN[sumMap]; }; pixels: ImagerPixel.PixelMap; box: Box; scanLine: ImagerPixel.PixelBuffer; summedTexture: REF SummedTexture; IF texture = NIL THEN SIGNAL ThreeDBasics.Error[[$MisMatch, "No texture to Sum"]]; WITH texture.pixels SELECT FROM pxMap: ImagerPixel.PixelMap => pixels _ pxMap; summed: REF SummedTexture => RETURN; -- previously summed ENDCASE => SIGNAL ThreeDBasics.Error[[$MisMatch, "Unknown texture pixel type"]]; box _ pixels.box; scanLine _ ImagerPixel.ObtainScratchPixels[ pixels.samplesPerPixel, box.max.f ]; summedTexture _ NEW[ SummedTexture[pixels.samplesPerPixel] ]; FOR y: NAT IN [box.min.s..box.max.s) DO ImagerPixel.GetPixels[self: pixels, pixels: scanLine, initIndex: [f: 0, s: y], count: box.max.f]; FOR i: NAT IN [0..pixels.samplesPerPixel) DO summedTexture[i] _ SumOneLine[ scanLine[i], summedTexture[i], y-box.min.s ]; ENDLOOP; ENDLOOP; texture.pixels _ summedTexture; ImagerPixel.ReleaseScratchPixels[scanLine]; }; Init[]; END. ���2��MappedAndSolidTextureImpl.mesa Copyright c 1984, 1986 by Xerox Corporation. All rights reserved. Last Edited by: Crow, March 16, 1989 9:50:21 am PST Internal Declarations Renamed Procedures Global Variables Initialization Register solid texture procs Procedures for Controlling Texture Procedures for VtxToRealSeq and computing color at a spot PROC[dest: REF RealSequence, source: VertexInfo, data: REF ANY] RTRNS[REF RealSequence]; PROC[context: REF Context, shading: REF ShadingClass, spot: REF Spot] Procedures for Auxiliary Clipping and Shading PROC[context: REF Context, shape: REF ShapeInstance, data: REF ANY _ NIL] RETURNS[REF ShapeInstance]; PROC[ context: REF Context, vtx: REF VertexInfo, data: REF ANY _ NIL ] RETURNS[REF VertexInfo]; PROC[ context: REF Context, vtx: REF VertexInfo, data: REF ANY _ NIL ] RETURNS[REF VertexInfo]; Support Procedures for setting up texture for tiler Procedures for Evaluating Texture at a Spot PROC[context: REF Context, shading: REF ShadingClass, spot: REF Spot] Increments in x and y This section could be executed once per scan segment txtrXIncr is vector for texture change to next pixel on scanline txtrYIncr is vector for texture change to corresponding pixel on scanline above scale txtrYIncr to projected length of texture increment along leading polygon edge Find max size of texture offsets to adjacent pixels to estimate texture spot spread Make sure area considered for surface orientation is large enough to avoid artifacts (This is independent of texture orientation, extreme aspect ratios in texture images should be avoided) perturb in x (rotate about y) perturb in y (rotate about x) Support Procedures for Summed Textures lowerValue: REAL _ llVal + xPos * (lrVal - llVal); -- if we only had double precision!! upperValue: REAL _ ulVal + xPos * (urVal - ulVal); RETURN [ lowerValue + yPos * (upperValue - lowerValue) ]; Procedures for solid texture calculation Regular array of dark spots Wurlitzer colors, stripes in 3-d Rotating stripes (barber pole) Perlin's marble texture returns band limited noise over R3. map the unit interval into an "S shaped" cubic f[x] | f[0]=0, f'[0]=0, f[1]=1, f'[1]=0. declare local variables. initialize random gradient table Force everything to be positive ixyz _ the integer lattice point "just below" v (identifies the surrounding unit cube). sxyz _ the vector difference v - ixyz biased with an S-Curve in each dimension. txyz _ the complementary set of S-Curves in each dimension. Add in each weighted component Procedures for Recovering Texture Descriptions from streams PROC[context: REF Context, shape: REF ShapeInstance, data: REF ANY _ NIL] RETURNS[REF ShapeInstance]; PROC[context: REF Context, shape: REF ShapeInstance, data: REF ANY _ NIL] RETURNS[REF ShapeInstance]; Procedures for Computing Texture Coordinates Stretch as indicated by corner coordinates Map from sphere to Cartesian coordinates 1st quadrant 0 - 1 range. Map polar coordinates into quadrilateral given by sw, nw, ne, se Procedures for Reading and Preparing Texture Files Previously used texture? Create context and load texture into it Ê*~��˜�Ihead™šœ Ïmœ7™BJ™3J˜�šÏk ˜ Jšœ žœ?˜MJšœ žœ˜Jšœ žœžœ ˜Jšœ žœ˜-Jšœžœ&˜6Jšœžœ˜%Jšžœžœžœ˜3JšœžœZ˜mJšœžœ˜%Jšœžœ˜,Jšœ žœ˜'Jšœžœ˜'Jšœžœªžœ˜ÏJšœžœ>˜TJšœžœ˜)Jšœžœ˜$Jšœžœ˜.Jšœžœ˜Jšœžœ˜ —J˜�—head2šœžœž˜(Iašžœ5žœt˜´Mšžœ˜J˜�Jšœž˜J˜�—head3šÏb™Mšœ žœ˜%Jšžœžœžœ˜Mšœžœ˜Mšœžœ˜)MšœžœÏc˜;Jšœžœ˜0Mšœžœ! ˜@Jšœžœ ˜4Jšœžœ˜,Jšœžœ$˜<Jšœ žœ˜.Idefaultšœžœ˜/Mšœžœ ˜5Mšœžœ˜+Mšœ žœ˜-Mšœžœ˜1Mšœžœ˜!Mšœžœ˜Mšœ žœ˜'Mšœžœ˜/Mšœžœ˜1Mšœžœ˜)M˜�Mšžœžœžœžœžœžœ˜M˜�Oš Ïnœž œžœ žœžœ˜MOš¡œž œ žœžœžœžœžœ˜Sunitš¡œž œ žœžœžœžœ˜8Mšžœ žœžœ˜3M˜——šŸ™Jš¡œžœ!žœžœžœžœžœ.˜uJš¡œžœ!žœžœžœžœž œ&˜x—šŸ™Jšœžœ' ˜BOšœ,žœ '˜XMšœžœžœ˜8Mšœžœ 3˜Q—šŸ™š¡œžœ˜˜"J˜J˜J˜J˜J˜J˜J˜!J˜—˜LJš ™—J˜)J˜1J˜;J˜/J˜1J˜+J˜——šŸ"™"š¡œžœžœ˜<šžœžœ˜Jšžœ=˜Ašžœžœ2˜9šžœ˜Jšœžœ˜2Jšœžœ˜-Jšœžœ˜+Jšœžœ˜/Jšœžœ˜+Jšœžœ˜+Jšœžœ#˜6J˜———J˜—š ¡œžœ žœžœžœ˜XJšœžœ@˜JJ˜=J˜4J˜šœ+ ˜EJšœžœ˜:J˜—J˜—š¡œžœžœ žœžœžœ˜sJšœžœ@˜JJ˜JšœQ ˜ZJ˜J˜�—š ¡œžœžœ žœžœ˜QJšœžœ@˜JJš œ žœžœžœžœ˜7šžœžœžœžœžœžœžœž˜Ošžœžœž˜Jšœžœ,˜;Jšžœ˜—Jšžœ˜—J˜—š ¡œžœžœ žœžœ˜MJšœžœ@˜JJšœžœ˜!J˜——šŸ9™9šŸ œ#˜0Jšžœžœ)žœžœžœžœ™XJš œžœžœžœžœ˜9Mšœžœžœ˜+Jšžœ žœžœ"˜9Jš žœžœžœžœžœ˜Ušžœ˜šžœ ,˜:J˜J˜J˜J˜J˜J˜J˜J˜J˜J˜Jšœ# ˜9J˜J˜J˜J˜J˜J˜—šžœ 0˜<J˜J˜J˜J˜Jšœ" ˜8J˜J˜J˜J˜J˜J˜——Jšœ ˜Jšžœ˜J˜J˜�—šŸœ˜Mšžœ žœžœžœ™EJšžœžœžœ#˜@šžœžœžœ˜1Jšžœ4˜8—J˜——šŸ-™-š¡œžœ ˜<Jšžœ žœžœžœžœžœžœžœ™eJšœžœ˜#Jšœ žœžœ%˜Dšžœžœž˜šœžœ˜"šžœžœžœžœ˜*Jšœ žœžœ˜-Jšžœ˜—J˜$M˜—Mšœžœ!˜+Mšžœžœ;˜L—šžœžœžœžœ˜*šžœ˜ JšžœA˜Ešžœ˜Jšžœžœ˜0Jšžœžœ˜0Jšžœžœ˜0Jšžœžœ˜0J˜——Jšžœ˜—M˜*JšœCžœ˜nšžœžœ˜.JšžœžœD˜O—J˜NOšžœ ˜O˜—š¡ œžœ" ˜LJšžœžœžœžœžœžœžœžœ ™_Mšœžœžœ˜šžœžœž˜šœžœ˜Jšœ žœžœ˜2Mš œžœžœžœžœ˜:Mšœ! ˜2M˜—Mšœžœ9˜BMšžœžœ;˜L—O˜Ošžœ˜O˜—š¡ œžœ! *˜]Jšžœžœžœžœžœžœžœžœ ™_Jšœžœžœ˜Mšœžœžœ˜Mšœžœžœžœ˜'Mšœžœžœžœ˜,Mš œžœžœžœžœ˜4Mš œžœžœžœžœ˜9M˜M˜O˜Ošžœ˜O˜——šŸ3™3š¡ œžœžœžœžœ%žœ ˜lJšœžœžœ˜šžœžœžœžœžœž˜Lšžœžœžœ ˜BJšœ žœ,žœ˜=Jšžœ˜ —Jšžœ˜—šžœžœ˜Jšœ(žœ˜-š žœžœžœžœ ˜AJšœžœžœ˜%šžœ˜ JšžœE˜Išžœ˜Jšžœžœ˜,Jšžœžœ˜,Jšžœžœ˜,Jšžœžœ˜,J˜——Jšžœ˜ —šžœ"žœ ˜9š žœžœžœžœ !˜HJšœžœžœ˜%Jšžœ žœžœ˜IJšžœ˜ —J˜š žœžœžœžœ ˜7Jšœžœžœ˜%Jšžœžœ˜,Jšžœ˜ —J˜—šžœ"žœ ˜9š žœžœžœžœ !˜HJšœžœžœ˜%Jšžœ žœžœ˜IJšžœ˜ —J˜š žœžœžœžœ ˜7Jšœžœžœ˜%Jšžœžœ˜,Jšžœ˜—J˜J™�—J˜Xš žœžœžœžœ (˜PJšœžœžœ˜%J˜J˜Jšžœ˜ —J˜—J˜——šŸ+™+š¡ œžœ˜Mšžœ žœžœžœ™EJšœ žœ˜!š žœžœžœžœž˜Dšžœžœžœ #˜Dšœ žœ ˜:šžœžœž˜šœžœ˜4Jšžœžœ:˜EJšžœ(˜,—šœžœžœ˜6šžœ˜Jšœžœžœ3˜?Jšœžœžœžœžœžœžœžœžœ˜DJ˜%J˜—Jšžœ+˜/—Jšžœžœ˜——šœžœ (˜KJ˜2—Jšžœžœ?˜P—Jšžœ˜—J˜—š¡ œžœžœ(žœ˜Nš ¡œžœ"žœžœžœ˜cOšœ žœ!˜.Ošœžœ!˜/Ošœžœ˜-Jšžœžœ˜+Jšœ žœ˜/Jšžœžœ˜,J˜—O˜YOšœžœ˜Ošœžœ˜!Ošœžœ ˜O˜?O˜bšžœžœ˜š œ žœžœžœžœ˜#Ošœžœ˜'O˜(Ošžœ˜—˜Jšœžœ˜$Jšœžœ˜+šžœžœžœžœ˜Ošœ žœ˜%Ošœ2 %œ3 ˜¤Ošžœ˜—Ošœ+ ˜EO˜—šœ 5˜UJšœžœ˜$Ošœ žœ˜+Ošœžœ˜-šžœžœžœžœ˜Ošœ3 %œ3 ˜¥Ošžœ˜—Ošœ+ ˜EO˜—˜Ošœžœ˜'šžœžœžœžœ˜O˜(Ošžœ˜—O˜—Jšžœžœ9˜K—O˜(J˜—š ¡ œžœžœžœžœ˜OJšœ žœ (˜RJšœ žœ )˜QOšœžœ˜!šœžœ˜J™—Jš œžœ žœžœžœ˜NJš œžœ žœžœžœ˜OJšœžœ4˜>Jšžœ#žœ+žœ˜]Jšžœ#žœ+žœ˜^šžœžœ˜š œ žœžœžœžœ˜#OšœžœU˜dOšœžœ#˜4Ošžœ˜—˜Jšœžœ˜$šœžœ ˜J˜:J˜ —Ošœžœ ˜Gšžœžœžœžœ˜šœ žœ ˜O˜:O˜ —Ošœ5 "œ4 ˜¥Ošœžœ˜%Ošžœ˜—Ošœ+ ˜EO˜—˜Ošœ žœU˜bJšœžœ˜$OšœžœU˜dOšœ žœ˜Ošœžœ ˜Fšžœžœžœžœ˜Ošœ6 "œ4 œ˜§Ošœžœ˜%Ošžœ˜—Ošœ+ ˜EO˜—˜OšœžœU˜dOšžœžœ˜(Ošž˜Ošœžœ˜0šžœžœžœžœ˜Ošœžœ#˜4Ošžœ˜—O˜—Jšžœžœ9˜K—J˜—š ¡œžœžœžœžœ˜ROšœžœ˜!Ošœžœ˜š¡ œžœ˜Jšžœ"žœ+žœ˜\Jšžœ"žœ+žœ˜\O˜—Jšœžœžœžœ˜@Jšœ žœ (˜RJšœ žœ *˜RJšœ4™4šœ"žœ˜'Jšœ@™@—šœ9˜9JšœO™O—šœN˜NJšœS™S—šœžœ˜JšœN˜NJšœ˜—Jšœ#žœ˜.šžœžœ ,˜IJšœ:˜:JšœD˜DJšœB˜BJšœ˜JšœS™S—Jšœžœžœžœ˜;Jšœžœžœžœ˜;šœ6˜6J™TJ™g—Jšœ%˜%JšžœžœA˜^Jšœ%˜%Jšžœžœ@˜]šžœžœ˜˜Ošœ7ž˜<Ošœ˜Ošœ ,˜@OšœY˜YOšœ1 &˜WOšœ0˜0O˜ OšœY˜YOšœ1 #˜TOšœ0˜0Ošœ1 #˜TOšœ0˜0O˜ OšœY˜Yšœ˜OšœQ˜QOšœP˜PO˜—Ošœžœ˜!šœ-˜-O˜�—O™Jšœ?˜?Jšœ7˜7Jšœ˜Jšœ˜Jšœ%˜%Jšœ˜Jšœ'˜'J˜ O™Jšœ?˜?Jšœ˜Jšœ˜Jšœ˜Jšœ%˜%Jšœ'˜'J˜ J˜�Ošœ˜Ošœ˜Ošœ˜O˜—JšžœžœR˜d—J˜——šŸ&™&š¡œžœžœžœ˜JJšžœžœžœ˜+JšœžœH™XJšœžœ"™2Jšžœ3™9Jšœžœžœ˜:Jšœ( ˜<Jšœ!˜!Jšœ!˜!Jšœ˜Jšœ( ˜<Jšœ"˜"Jšœ"˜"Jšœ˜Jšœ, ˜IJšœ ˜ Jšœ"˜"Jšœ˜JšœA˜AJšœ˜—š¡ œžœ žœžœžœžœ˜GJšœ žœ˜$Jšœ žœ˜!šžœžœžœ˜7Jšžœ˜J˜—šžœžœžœžœ˜=J˜3Jšžœ?˜EJ˜MJ˜—šžœžœžœ˜%J˜Jšžœ5˜;J˜—šžœ ˜+J˜Jšžœ5˜;J˜—J˜—š¡ œžœ žœžœžœžœžœ˜iJšœžœž˜'J˜J˜&J˜J˜%Jšœ! ˜<J˜šœ(˜(Jšœ:˜:Jšœ9˜9Jšœ ˜ J˜—J˜—š¡ œžœžœžœžœ&žœ žœ žœ˜žOšœžœžœ˜.šžœžœ E˜ZOšœ 6˜FOšœI˜IO˜—O˜^O˜^O˜]O˜^O˜(J˜4J˜——šŸ(™(š ¡œžœžœžœ8žœ˜q˜0Jšœ"žœ*˜OJ˜—O˜—š ¡œžœžœžœžœ˜išœžœžœ˜"O˜6O˜—šžœžœ˜ Ošžœ˜Ošžœžœ:˜E—J˜—šŸœ˜Jš ™Jšœžœžœžœ ˜XJš œžœžœžœžœ˜8Jšœžœw˜†J˜$J˜&J˜&J˜&J˜&J˜J˜�—šŸ œ˜Jš ™ Jšœžœžœžœ ˜XJš œžœžœžœžœ˜8Jšœžœw˜†J˜$J˜GJ˜GJ˜GJ˜&J˜J˜�—šŸœ˜Jš ™Jšœžœžœžœ ˜XJš œžœžœžœžœ˜8Jšœžœ ˜AJšœ žœ˜$Jšœ žœ˜$Jšœžœ $œ>˜‹Jšœžœ˜J˜$J˜#J˜*J˜&J˜.J˜&J˜—š¡œ˜Jšœžœžœžœ ˜XJš œžœžœžœžœ˜8Jšœžœ˜Jšœžœ3˜>Jšœ žœ<˜JJšœžœžœ˜0Jšœžœ˜ J˜�šœ žœžœ˜$Jšžœ+˜3Jšžœžœ,˜4—Jšœžœ ˜<Jšœžœ ˜<Jšœžœ ˜>Jšœžœ ˜?J˜IJ˜IJ˜6J˜7Jšœžœ'˜6J˜ J˜ J˜ J˜—š¡ œ˜Jšœžœžœžœ ˜XJš œžœžœžœžœ˜8Jšœžœ˜Jšœžœ3˜>Jšœ žœ<˜JJšœžœ˜)Jšœžœ˜ J˜�šœ žœžœ˜$Jšžœ+˜3Jšžœžœ,˜4—Jšœžœ ˜<Jšœžœ ˜<Jšœžœ ˜>Jšœžœ ˜?J˜IJ˜IJ˜6J˜7Jšœžœ'˜6J˜ J˜ J˜ J˜—šŸœ˜J™Jšœžœžœžœ ˜XJš œžœžœžœžœ˜8Jšœžœh˜wJ˜$J˜+J˜&J˜&J˜&J˜—š¡œžœžœ žœžœžœ˜4Jšœžœ˜ Jšœžœ˜šžœžœžœž˜J˜ J˜J˜ Jšžœ˜—Jšžœ˜J˜—J˜�Icodešœžœ žœžœ˜'Q˜4 ˜Q˜�—š ¡œžÏsžœžœžœ˜7™#J˜�š¡œžœ žœžœžœžœ˜5Jš œžœžœžœžœ˜Q˜/Qšœ žœžœžœ˜!Qšœžœ$žœžœ˜OQšžœ˜J˜—š ¡œžœžœžœžœ˜(™WJ˜�Jšžœ˜—˜J˜�——™J˜Jšœžœ˜Jšœ3žœ˜8—™ šžœ žœžœ˜Jšœ žœ˜šžœžœžœ ž˜J˜J˜(Jšžœ˜—J˜——™J˜J˜J˜—šœÏdœS™WJ˜J˜J˜—šœ£œ£œ*™OJ˜J˜J˜—šœ£œ7™;J˜ J˜ J˜ —Jšœ ˜"š žœžœžœžœ <˜Tšžœžœ 2˜@J˜:J˜.J˜5J˜.J˜;J˜.J˜4J˜-Jšžœ˜—™J˜J˜_—Jšžœ˜—Jšžœ˜—J˜——šŸ;™;š¡œžœžœžœ˜:˜#O˜O˜Ošžœ"˜%O˜—˜#O˜O˜Ošžœ'˜*O˜—O˜—š¡œ˜#Jšžœ žœžœžœžœžœžœžœ™eOšœžœžœžœ˜ šœ žœ0˜<O˜*Ošœžœ˜O˜—O˜1šžœžœž˜˜QOšœ<ž˜=Ošœžœžœ˜0Ošœžœžœ˜0Ošœžœžœ˜0Ošœžœžœ˜.O˜—˜SOšœ žœžœ˜/Ošœ žœžœ˜.Ošœžœžœ˜0Ošœžœžœ˜0Ošœžœžœ˜*Ošœžœžœ˜*Ošœžœžœ˜.Ošœžœžœ˜(O˜—O˜šžœžœ˜%O˜-O˜——Ošœ& ˜<Ošœ%žœžœžœ˜^Ošžœ ˜O˜—š¡œ˜(Jšžœ žœžœžœžœžœžœžœ™eOšœžœžœžœ˜ Ošœ&žœ˜:Ošžœ ˜O˜——šŸ,™,š¡œžœžœžœžœžœ˜…Jšœžœ˜Jšœžœ@˜JOšœ žœžœ2˜TJšœžœ žœ-˜KJšžœ žœžœ žœ˜Bšžœžœžœžœ˜GJšœ1 (˜YJ˜0J˜—J˜%šžœžœžœžœ˜%J˜%J˜%Ošžœ˜—Q˜KO˜—Oš¡œžœžœžœžœ,žœ˜˜OJšœžœ@˜JJšœ žœžœ1˜SJšœžœžœžœ˜šžœ4žœžœ˜@JšžœM˜SJ˜TJ˜—Jšžœžœžœžœ%˜Hšžœ0žœ,žœ/žœ,˜¿JšžœžœF˜Q—šžœžœžœžœ˜)Jšœžœ˜!Jšœžœ ˜Jšœ: ˜LJ™*—Jšœ> ˜SJ˜?J˜>J˜?Jšœ8 ˜KJ˜6Jšžœ˜ —J˜$J˜NJ˜OJšœžœ žœžœ˜BJšœžœžœ˜BJšœžœžœ˜CJšœžœžœ˜DJšœžœ-žœ˜[J˜JJ˜—š¡œžœžœžœ˜QJ˜—Jš ¡œžœžœžœžœ˜T˜ÅJšœžœ@˜JJšœ žœžœ1˜SJšœžœžœžœ˜Ošœžœ!žœ˜@Jšœ žœ žœžœ˜#Jšœžœ /˜MJ˜�šžœ3žœžœ˜?JšžœM˜SJ˜TJ˜—Jšžœžœžœžœ%˜Hšžœ1žœžœ˜>Jšœ, *˜VJšœ* %˜OJ˜—šžœžœžœ!˜EJšžœžœH˜S—šžœžœ"˜(Jšžœžœ ˜Ušžœžœžœ!˜,Jšžœžœ ˜T——J˜�Jšžœžœžœ* ˜ZJšžœžœžœ*˜KJšžœžœžœ*˜KJšžœžœžœ+˜Kšžœ žœžœžœ˜+Jšœžœ˜.Jšœ žœ˜šžœžœžœž˜-J™BJšœžœ˜'JšœžœE˜UJšœžœ?˜Nšœ žœ6˜DJ™A—Jšœžœ& &˜WJšœžœ&˜1Jšœžœ# ˜NJšœžœ ˜+šžœ˜!Jšžœ*˜.šžœžœ˜&Jšžœ)˜-——Jšœ9 ˜MJšœ; ˜GJšœžœžœ˜5Jšœžœžœ˜5Jšžœžœžœ ˜Ošžœ˜Jšœžœ˜Jšžœžœ˜<Jšžœžœ˜?—Jšžœ˜—J˜$J˜NJšžœ* &˜Rš žœžœžœžœž˜2Jšœžœ˜'šžœ ˜"Jšžœ&˜*—Jšžœ˜ —J˜J˜š žœžœžœžœ ˜Lšžœžœžœ˜Jšœžœ˜'Jšžœžœ˜<Jšžœžœ˜<J˜—Jšžœ˜—š žœžœžœžœ #˜QJšœžœ˜'Jšžœžœžœ-˜GJšžœ˜ —J˜*š žœžœžœžœ "˜QJšœžœ˜'Jšœžœ9˜DJšœžœ9˜DJšœžœ;˜FJšœžœ<˜GJ˜;J˜:Jšžœžœ˜<Jšžœžœ˜<Jšžœ˜ —J˜*J˜*š žœžœžœžœ ˜FJšœžœ˜'J˜,J˜,Jšžœ˜ —Jšžœ˜ —J˜PJšœžœ žœžœ˜CJšœžœžœ˜CJšœžœ žœ˜CJšœžœ žœ˜DJšœžœžœ˜5Jšœžœžœ˜5Jšœžœžœ˜5Jšœžœžœ˜5J˜JJ˜——šŸ2™2š¡œžœžœ žœžœžœžœžœ žœ˜«Mšœžœ˜Jšœžœ˜Jšœžœ ˜J™šžœžœžœž˜+šžœ*žœžœ˜5š žœžœ;žœžœž˜ešžœžœž˜šœ žœžœžœ˜AJ˜ Jšžœ6žœ˜BJšž˜J˜Jšžœžœ 0˜F—Jšžœ˜ —Jšžœ˜——Jšžœ˜—J™'šœ žœžœ˜?Mšžœ˜Mšžœ˜—M˜DJšžœžœ+˜PJšœ' &˜MJšœ+ !œ6žœ˜‰šžœžœ žœ˜IM˜EMšœ6 ˜PJšœ1 œ<žœ˜J˜—MšœB ˜UJšœFžœ˜XJšœžœ %˜\Jšœ žœ /˜KJ˜#J˜J˜Gšžœ˜Jšžœ?žœžœ˜W—J˜J˜�—š¡œžœžœ žœ˜;š¡ œžœ+žœžœ žœžœ˜wJšžœ žœžœ žœ(˜IJšžœ žœžœ žœ˜BJ˜J˜�šžœžœžœž˜!Jšœžœ˜šžœžœ˜Jšœ0 ˜Bšžœžœ @˜PJ˜I—J˜—šžœžœžœ˜Jšœ1 ˜A—Jšžœ˜—Jšžœ ˜J˜—J˜J˜ J˜"Ošœžœ˜!O˜�Ošžœžœžœžœ6˜Ršžœžœž˜J˜.Jšœžœžœ ˜;Jšžœžœ?˜P—J˜˜+M˜!M˜—Ošœžœ+˜>šžœžœžœžœ˜)O˜ašžœžœžœž˜,O˜NOšžœ˜—Ošžœ˜—J˜J˜+J˜J˜�——J˜Jšžœ˜—�…—����³��éÌ��