<> <> <> 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; <> <<(This is independent of texture orientation, extreme aspect ratios in texture images should be avoided)>> 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.