<> <> <> <> DIRECTORY Atom, Checksum, G3dBasic, G3dClipXfmShade, G3dColorDisplaySupport, G3dMappedAndSolidTexture, G3dRender, G3dRenderWithPixels, G3dScanConvert, G3dShade, G3dShape, G3dSortandDisplay, G3dVector, ImagerPixel, ImagerSample, IO, List, Real, RealFns, Rope, Vector2; G3dMappedAndSolidTextureImpl: CEDAR PROGRAM IMPORTS Atom, Checksum, G3dClipXfmShade, G3dColorDisplaySupport, G3dRender, G3dRenderWithPixels, G3dShade, G3dSortandDisplay, G3dVector, ImagerPixel, IO, List, Real, RealFns, Rope, Vector2 EXPORTS G3dMappedAndSolidTexture = BEGIN <> ROPE: TYPE ~ Rope.ROPE; Context: TYPE ~ G3dRender.Context; RGB: TYPE ~ G3dRender.RGB; Box: TYPE ~ ImagerSample.Box; Rectangle: TYPE ~ G3dRender.Rectangle; Pair: TYPE ~ G3dRender.Pair; -- [ x, y: REAL]; PairSequence: TYPE ~ G3dRender.PairSequence; Triple: TYPE ~ G3dRender.Triple; -- [ x, y, z: REAL] TripleSequence: TYPE ~ G3dRender.TripleSequence; Vertex: TYPE ~ G3dShape.Vertex; CtlPoint: TYPE ~ G3dRender.CtlPoint; CtlPtInfo: TYPE ~ G3dRender.CtlPtInfo; CtlPtInfoSequence: TYPE ~ G3dRender.CtlPtInfoSequence; IntSequence: TYPE ~ G3dRender.IntSequence; IntSequenceRep: TYPE ~ G3dBasic.IntSequenceRep; RealSequence: TYPE ~ G3dRender.RealSequence; RealSequenceRep: TYPE ~ G3dBasic.RealSequenceRep; TextureFunction: TYPE ~ G3dRender.TextureFunction; TextureMap: TYPE ~ G3dRender.TextureMap; SumSequence: TYPE ~ G3dRender.SumSequence; SummedTexture: TYPE ~ G3dRender.SummedTexture; Patch: TYPE ~ G3dRender.Patch; PatchSequence: TYPE ~ G3dRender.PatchSequence; Spot: TYPE ~ G3dRender.Spot; SpotProc: TYPE ~ G3dRender.SpotProc; RenderData: TYPE ~ G3dRender.RenderData; ShadingClass: TYPE ~ G3dRender.ShadingClass; RenderStyle: TYPE ~ G3dRender.RenderStyle; Shape: TYPE ~ G3dRender.Shape; ShapeProc: TYPE ~ G3dRender.ShapeProc; DisplayMode: TYPE ~ G3dRender.DisplayMode; 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 ~ G3dScanConvert.justNoticeable; -- 0.02 registeredTextureFunctions: Atom.PropList _ NIL; -- keeps active solid texture functions maxTxtrRange: REAL _ 32.0; -- texture coordinate range, small numbers expected <> Init: PROC[] ~ { txtrShadingClass: ShadingClass _ [ type: $G3dMappedAndSolidTexture, cnvrtVtx: GetLerpedVals, getColor: RecoverColor, shadeVtx: G3dShade.ShadeVtx ]; G3dShade.RegisterShadingClass[txtrShadingClass, $G3dMappedAndSolidTexture]; <> RegisterTextureFunction[ $Spots, Spots ]; RegisterTextureFunction[ $Wurlitzer, Wurlitzer ]; RegisterTextureFunction[ $TwistedStripes, TwistedStripes ]; RegisterTextureFunction[ $BurlWood, BurlWood ]; RegisterTextureFunction[ $ZebraBurl, ZebraBurl ]; RegisterTextureFunction[ $Marble, Marble ]; }; <> CheckAndAddProcs: PUBLIC PROC[shape: Shape ] ~ { <> renderData: REF RenderData _ G3dRender.RenderDataFrom[shape]; IF renderData.shadingClass = NIL THEN G3dShade.LoadShadingClass[shape, $G3dMappedAndSolidTexture] ELSE IF renderData.shadingClass.type # $G3dMappedAndSolidTexture THEN { renderData.shadingClass.type _ $G3dMappedAndSolidTexture; renderData.shadingClass.cnvrtVtx _ GetLerpedVals; renderData.shadingClass.getColor _ RecoverColor; renderData.shadingClass.shadeVtx _ G3dShade.ShadeVtx; }; }; AddSolidTexture: PUBLIC PROC[context: Context, shapeName: Rope.ROPE, name: ATOM ] ~{ shape: Shape _ G3dRender.FindShape[ context, shapeName ]; renderData: REF RenderData _ G3dRender.RenderDataFrom[shape]; txtrFn: TextureFunction _ GetRegisteredTextureFunction[name]; txtrFn.props _ PutProp[txtrFn.props, $Shape, shape]; CheckAndAddProcs[shape]; -- make sure texture Procs loaded in shadingClass renderData.shadingClass.texture _ List.Nconc1[ -- append to existing list renderData.shadingClass.texture, NEW[TextureFunction _ txtrFn] ]; }; AddMappedTexture: PUBLIC PROC[context: Context, shapeName: Rope.ROPE, texture: REF TextureMap] ~ { <> shape: Shape _ G3dRender.FindShape[ context, shapeName ]; renderData: REF RenderData _ G3dRender.RenderDataFrom[shape]; CheckAndAddProcs[shape]; renderData.shadingClass.texture _ List.Nconc1[ renderData.shadingClass.texture, texture ]; }; SumAllMappedTextures: PUBLIC PROC[context: Context, shapeName: Rope.ROPE] ~ { shape: Shape _ G3dRender.FindShape[ context, shapeName ]; renderData: REF RenderData _ G3dRender.RenderDataFrom[shape]; texture: LIST OF REF ANY _ renderData.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: Context, shapeName: Rope.ROPE] ~ { shape: Shape _ G3dRender.FindShape[ context, shapeName ]; renderData: REF RenderData _ G3dRender.RenderDataFrom[shape]; renderData.shadingClass.texture _ NIL; }; <> GetLerpedVals: G3dRender.CtlPtToRealSeqProc ~ { <> maxLength: NAT _ IF data = $PixelShading THEN 16 ELSE 10; IF dest = NIL OR dest.maxLength < maxLength THEN dest _ NEW[RealSequenceRep[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] _ source.shade.txtrX; dest[12] _ source.shade.txtrY; 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] _ source.shade.txtrX; dest[6] _ source.shade.txtrY; 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 G3dRenderWithPixels.ShadeSpot[context, shading, spot]; }; <> AdjustTexture: PUBLIC PROC[poly: REF Patch, texture: LORA, txtrRange: Pair] ~ { <> mappedTexture: BOOLEAN _ FALSE; halfXRange: REAL _ txtrRange.x / 2.0; halfYRange: REAL _ txtrRange.y / 2.0; FOR txtrList: LORA _ NARROW[texture], txtrList.rest UNTIL txtrList = NIL DO WITH txtrList.first SELECT FROM -- look for mapped texture texture: REF G3dRender.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 IF i = 0 THEN { maxXtxtr _ minXtxtr _ poly[i].shade.txtrX; maxYtxtr _ minYtxtr _ poly[i].shade.txtrY; } ELSE { IF maxXtxtr < poly[i].shade.txtrX THEN maxXtxtr _ poly[i].shade.txtrX; IF maxYtxtr < poly[i].shade.txtrY THEN maxYtxtr _ poly[i].shade.txtrY; IF minXtxtr > poly[i].shade.txtrX THEN minXtxtr _ poly[i].shade.txtrX; IF minYtxtr > poly[i].shade.txtrY THEN minYtxtr _ poly[i].shade.txtrY; }; ENDLOOP; IF maxXtxtr - minXtxtr > halfXRange THEN { -- seam in x FOR i: CARDINAL IN [0..poly.nVtces) DO -- push small ones beyond maximum IF maxXtxtr - poly[i].shade.txtrX > poly[i].shade.txtrX - minXtxtr THEN WHILE maxXtxtr > poly[i].shade.txtrX DO -- closer to minTxtr, so boost poly[i].shade.txtrX _ poly[i].shade.txtrX + 1.0; ENDLOOP; ENDLOOP; minXtxtr _ maxXtxtr; FOR i: CARDINAL IN [0..poly.nVtces) DO -- find minimum for normalization IF minXtxtr > poly[i].shade.txtrX THEN minXtxtr _ poly[i].shade.txtrX; ENDLOOP; }; IF maxYtxtr - minYtxtr > halfYRange THEN { -- seam in y FOR i: CARDINAL IN [0..poly.nVtces) DO -- push small ones beyond maximum IF maxYtxtr - poly[i].shade.txtrY > poly[i].shade.txtrY - minYtxtr THEN WHILE maxYtxtr > poly[i].shade.txtrY DO poly[i].shade.txtrY _ poly[i].shade.txtrY + 1.0; ENDLOOP; ENDLOOP; minYtxtr _ maxYtxtr; FOR i: CARDINAL IN [0..poly.nVtces) DO -- find minimum for normalization IF minYtxtr > poly[i].shade.txtrY THEN minYtxtr _ poly[i].shade.txtrY; ENDLOOP; }; <<>> minXtxtr _ Real.Float[Real.Fix[minXtxtr]]; minYtxtr _ Real.Float[Real.Fix[minYtxtr]]; FOR i: CARDINAL IN [0..poly.nVtces) DO -- unit to minima under 1.0 poly[i].shade.txtrX _ poly[i].shade.txtrX - minXtxtr; poly[i].shade.txtrY _ poly[i].shade.txtrY - minYtxtr; ENDLOOP; }; }; <> GetTxtrAt: PUBLIC SpotProc ~ { <> texture: LORA _ shading.texture; spot.partShiny _ 1.0; -- initialize to full shiny, texture procs can only reduce it 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 G3dRender.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 G3dRender.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 G3dRender.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; 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; FOR i: NAT IN [0..3) DO spot.val[i] _ MAX[ 0.0, spot.val[i] * txtrValue ]; ENDLOOP; }; ENDCASE => SIGNAL G3dRender.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; 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; 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; 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; 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.Unit[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.Unit[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.Unit[tnml]; spot.val[nmlX] _ nml.x; spot.val[nmlY] _ nml.y; spot.val[nmlZ] _ nml.z; }; ENDCASE => SIGNAL G3dRender.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; }; 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; [ 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 G3dRender.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]; }; <> GetRope: PROC[input: IO.STREAM] RETURNS[ROPE] ~ { <> output: ROPE _ NIL; char: CHAR; [] _ IO.SkipWhitespace[input]; -- Strip whitespace and comments char _ IO.GetChar[input]; WHILE char # IO.SP AND char # IO.CR DO -- do until trailing space or CR output _ Rope.Cat[ output, Rope.FromChar[char] ]; char _ IO.GetChar[input]; ENDLOOP; RETURN[output]; }; EnableStreamProcs: PUBLIC PROC[ context: 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: 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 G3dRender.Error[$Unimplemented, "Unknown texture coordType"]; [] _ 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: Context, shapeName: Rope.ROPE, scale: REAL, xRatio, yRatio: REAL _ 1.0] ~ { shape: Shape _ G3dRender.FindShape[context, shapeName]; oldScale, newScale: Pair _ G3dRender.GetTextureScale[shape]; IF oldScale.x # 0.0 AND oldScale.y # 0.0 THEN { newScale.x _ scale*xRatio/oldScale.x; -- multiply by new, divide by old values newScale.y _ scale*yRatio/oldScale.y; }; FOR i: NAT IN [0..shape.vertices.length) DO shape.vertices[i].texture.x _ shape.vertices[i].texture.x * newScale.x; shape.vertices[i].texture.y _ shape.vertices[i].texture.y * newScale.y; ENDLOOP; G3dRender.SetTextureScale[shape, newScale]; }; SetPatchTextureCoords: PUBLIC PROC [ context: Context, shapeName: ROPE, corner0: Pair _ [0.,0.], corner1: Pair _ [0.,1.], corner2: Pair _ [1.,1.], corner3: Pair _ [1.,0.] ] ~ { <> shape: Shape _ G3dRender.FindShape[context, shapeName]; renderData: REF RenderData _ G3dRender.RenderDataFrom[ shape ]; patch: PatchSequence; IF renderData = NIL OR renderData.patch = NIL THEN [] _ G3dSortandDisplay.ValidateContext[ context ]; -- do whole context, just in case patch _ renderData.patch; SELECT shape.type FROM $ConvexPolygon, $Poly => FOR i: NAT IN [0..patch.length) DO IF patch[i].nVtces = 4 THEN { patch[i][0].shade.txtrX _ corner0.x; patch[i][0].shade.txtrY _ corner0.y; patch[i][1].shade.txtrX _ corner1.x; patch[i][1].shade.txtrY _ corner1.y; patch[i][2].shade.txtrX _ corner2.x; patch[i][2].shade.txtrY _ corner2.y; patch[i][3].shade.txtrX _ corner3.x; patch[i][3].shade.txtrY _ corner3.y; }; ENDLOOP; $Bezier => FOR i: NAT IN [0..patch.length) DO IF patch[i].nVtces = 16 THEN { << Corners (see diagram in G3dStandardPatchProcs.PolygonFromBezierPatch)>> patch[i][ 0].shade.txtrX _ corner0.x; patch[i][ 0].shade.txtrY _ corner0.y; patch[i][12].shade.txtrX _ corner1.x; patch[i][12].shade.txtrY _ corner1.y; patch[i][15].shade.txtrX _ corner2.x; patch[i][15].shade.txtrY _ corner2.y; patch[i][ 3].shade.txtrX _ corner3.x; patch[i][ 3].shade.txtrY _ corner3.y; <> patch[i][ 1].shade.txtrX _ (2.0 * corner0.x + corner3.x) / 3.0 ; patch[i][ 1].shade.txtrY _ (2.0 * corner0.y + corner3.y) / 3.0; patch[i][ 2].shade.txtrX _ (2.0 * corner3.x + corner0.x) / 3.0 ; patch[i][ 2].shade.txtrY _ (2.0 * corner3.y + corner0.y) / 3.0; patch[i][ 4].shade.txtrX _ (2.0 * corner0.x + corner1.x) / 3.0 ; patch[i][ 4].shade.txtrY _ (2.0 * corner0.y + corner1.y) / 3.0; patch[i][ 8].shade.txtrX _ (2.0 * corner1.x + corner0.x) / 3.0 ; patch[i][ 8].shade.txtrY _ (2.0 * corner1.y + corner0.y) / 3.0; patch[i][ 7].shade.txtrX _ (2.0 * corner3.x + corner2.x) / 3.0 ; patch[i][ 7].shade.txtrY _ (2.0 * corner3.y + corner2.y) / 3.0; patch[i][11].shade.txtrX _ (2.0 * corner2.x + corner3.x) / 3.0 ; patch[i][11].shade.txtrY _ (2.0 * corner2.y + corner3.y) / 3.0; patch[i][13].shade.txtrX _ (2.0 * corner1.x + corner2.x) / 3.0 ; patch[i][13].shade.txtrY _ (2.0 * corner1.y + corner2.y) / 3.0; patch[i][14].shade.txtrX _ (2.0 * corner2.x + corner1.x) / 3.0 ; patch[i][14].shade.txtrY _ (2.0 * corner2.y + corner1.y) / 3.0; <> patch[i][5].shade.txtrX _ (2.0 * patch[i][4].shade.txtrX + patch[i][7].shade.txtrX) / 3.0 ; patch[i][5].shade.txtrY _ (2.0 * patch[i][4].shade.txtrY + patch[i][7].shade.txtrY) / 3.0; patch[i][6].shade.txtrX _ (2.0 * patch[i][7].shade.txtrX + patch[i][4].shade.txtrX) / 3.0 ; patch[i][6].shade.txtrY _ (2.0 * patch[i][7].shade.txtrY + patch[i][4].shade.txtrY) / 3.0; patch[i][9].shade.txtrX _ (2.0 * patch[i][8].shade.txtrX + patch[i][11].shade.txtrX) /3.0 ; patch[i][9].shade.txtrY _ (2.0 * patch[i][8].shade.txtrY + patch[i][11].shade.txtrY) /3.0; patch[i][10].shade.txtrX _ (2.0 * patch[i][11].shade.txtrX + patch[i][8].shade.txtrX)/3.0 ; patch[i][10].shade.txtrY _ (2.0 * patch[i][11].shade.txtrY + patch[i][8].shade.txtrY)/3.0; }; ENDLOOP; ENDCASE => SIGNAL G3dRender.Error[$Unimplemented, "Unknown shape type"]; }; MakeTxtrCoordsFromVtxNos: PUBLIC PROC[ context: Context, shapeName: Rope.ROPE, vtcesInRow, numberOfRows: NAT, row0col0, rowNcol0, rowNcolM, row0colM: Pair] ~ { shape: Shape _ G3dRender.FindShape[ context, shapeName ]; renderData: REF RenderData _ G3dRender.RenderDataFrom[shape]; args: LIST OF REAL; IF shape.vertices.valid.texture THEN SIGNAL G3dRender.Error[$MisMatch, "Overwriting original texture coords, OK?"]; 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 G3dRender.Error[$MisMatch, "Texture mapping dangerously dense"]; FOR i: NAT IN [0..shape.vertices.length) DO lPosX, lPosY, rPosX, rPosY: REAL; shape.vertices[i].texture.x _ Real.Float[i MOD vtcesInRow] / vtcesInRow; -- pct along row shape.vertices[i].texture.y _ Real.Float[i / vtcesInRow] / numberOfRows; -- pct across rows <> lPosX _ row0col0.x + shape.vertices[i].texture.y * (rowNcol0.x - row0col0.x); lPosY _ row0col0.y + shape.vertices[i].texture.y * (rowNcol0.y - row0col0.y); rPosX _ row0colM.x + shape.vertices[i].texture.y * (rowNcolM.x - row0colM.x); rPosY _ row0colM.y + shape.vertices[i].texture.y * (rowNcolM.y - row0colM.y); <> shape.vertices[i].texture.x _ lPosX + shape.vertices[i].texture.x * (rPosX - lPosX); shape.vertices[i].texture.y _ lPosY + shape.vertices[i].texture.x * (rPosY - lPosY); ENDLOOP; renderData.shadingProps _ PutProp[ renderData.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]; renderData.shadingProps _ PutProp[ renderData.shadingProps, $TxtrCoordParams, args]; renderData.shadingProps _ PutProp[ renderData.shadingProps, $TxtrCoordRange, NEW[ Pair _ [ MIN[ rowNcol0.x - row0col0.x, rowNcolM.x - row0colM.x], MIN[ row0colM.y - row0col0.y, rowNcolM.y - rowNcol0.y] ] ] ]; shape.vertices.valid.texture _ TRUE; G3dRender.RenderDataFrom[shape].patch _ NIL; -- force patches to be rebuilt }; MakeTxtrCoordsFromNormals: PUBLIC PROC[ context: 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: Shape _ G3dRender.FindShape[ context, shapeName ]; renderData: REF RenderData _ G3dRender.RenderDataFrom[shape]; args: LIST OF REAL; badTag: REAL _ -9999.99; minTxtrX, minTxtrY: REAL _ Real.LargestNumber; maxTxtrX: REAL _ 0.; lngtShift: REAL _ 0.0; -- longitude shift to allow < -180.0 and > 180.0 IF shape.vertices.valid.texture THEN SIGNAL G3dRender.Error[$MisMatch, "Overwriting original texture coords, OK?"]; IF NOT shape.vertices.valid.normal THEN G3dClipXfmShade.GetVtxNmls[context, shape]; IF MAX[sw.x, nw.x, se.x, ne.x] - MIN[sw.x, nw.x, se.x, ne.x] > 360.0 THEN SIGNAL G3dRender.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 i: NAT IN [0..shape.vertices.length) DO -- calculate normals vtx: Vertex _ shape.vertices[i]; <> hypotenuse: REAL _ RealFns.SqRt[Sqr[vtx.normal.y] + Sqr[vtx.normal.x]]; longitude: REAL _ RealFns.ArcTanDeg[vtx.normal.y, vtx.normal.x]; latitude: REAL _ RealFns.ArcTanDeg[vtx.normal.z, 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); vtx.texture.x _ (longitude - lPosX) / (rPosX - lPosX); -- percentage across vtx.texture.y _ lPosY + vtx.texture.x * (rPosY - lPosY); -- wtd av. % vtx.texture.x _ MIN[ 1.0, MAX[0.0, vtx.texture.x]]; vtx.texture.y _ MIN[ 1.0, MAX[0.0, vtx.texture.y]]; IF vtx.texture.x < minTxtrX THEN minTxtrX _ vtx.texture.x; IF vtx.texture.x > maxTxtrX THEN maxTxtrX _ vtx.texture.x; IF hypotenuse < 0.00001 THEN vtx.texture.x _ badTag; -- catch unstable arithmetic ENDLOOP; FOR i: NAT IN [0..shape.vertices.length) DO -- fix up unstable vertical normals vtx: Vertex _ shape.vertices[i]; IF vtx.texture.x = badTag THEN vtx.texture.x _ (maxTxtrX + minTxtrX) / 2.; ENDLOOP; minTxtrX _ minTxtrY _ Real.LargestNumber; FOR i: NAT IN [0..shape.vertices.length) DO -- slew according to corner coords vtx: Vertex _ shape.vertices[i]; lPosX: REAL _ botLeft.x + vtx.texture.y * (topLeft.x - botLeft.x); lPosY: REAL _ botLeft.y + vtx.texture.y * (topLeft.y - botLeft.y); rPosX: REAL _ botRight.x + vtx.texture.y * (topRight.x - botRight.x); rPosY: REAL _ botRight.y + vtx.texture.y * (topRight.y - botRight.y); vtx.texture.x _ lPosX + vtx.texture.x * (rPosX - lPosX); vtx.texture.y _ lPosY + vtx.texture.x * (rPosY - lPosY); IF vtx.texture.x < minTxtrX THEN minTxtrX _ vtx.texture.x; IF vtx.texture.y < minTxtrY THEN minTxtrY _ vtx.texture.y; ENDLOOP; minTxtrX _ Real.Float[Real.Fix[minTxtrX]]; minTxtrY _ Real.Float[Real.Fix[minTxtrY]]; FOR i: NAT IN [0..shape.vertices.length) DO -- translate to origin vtx: Vertex _ shape.vertices[i]; vtx.texture.x _ vtx.texture.x - minTxtrX; vtx.texture.y _ vtx.texture.y - minTxtrY; ENDLOOP; renderData.shadingProps _ PutProp[ renderData.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]; renderData.shadingProps _ PutProp[ renderData.shadingProps, $TxtrCoordParams, args]; renderData.shadingProps _ PutProp[ renderData.shadingProps, $TxtrCoordRange, NEW[ Pair _ [ MIN[ botRight.x - botLeft.x, topRight.x - topLeft.x], MIN[ topLeft.y - botLeft.y, topRight.y - botRight.y] ] ] ]; shape.vertices.valid.texture _ TRUE; G3dRender.RenderDataFrom[shape].patch _ NIL; -- force patches to be rebuilt }; <> TextureFromAIS: PUBLIC PROC[context: Context, fileName: Rope.ROPE, type: ATOM _ $Intensity, factor: REAL _ 1.0] RETURNS[texture: REF TextureMap] ~ { width, height: INTEGER _ 512; displayMode: DisplayMode; bufContext: Context; <> FOR i: NAT IN [0..context.shapes.length) DO textureList: LORA _ G3dRender.ShadingClassFrom[context.shapes[i]].texture; IF textureList # NIL THEN FOR txtrList: LORA _ textureList, 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; <> displayMode _ IF type = $Color OR type = $ColorAndTransmittance THEN fullColor ELSE gray; bufContext _ G3dRender.CreateUndisplayedContext[displayMode: displayMode, width: width, height: height]; IF type = $ColorAndTransmittance THEN G3dRenderWithPixels.AntiAliasing[bufContext]; bufContext.props _ context.props; -- bring along working directory, etc. [width, height] _ G3dColorDisplaySupport.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]]; G3dRenderWithPixels.AllocatePixelMemory[bufContext]; -- get bigger pixel buffer [width, height] _ G3dColorDisplaySupport.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, 512]; bufContext.pixels.box.min.s _ MAX[0, 512 - height]; -- getAIS pushes image up against max texture _ NEW[TextureMap]; -- now, gin up TextureMap, using context.pixels texture.pixels _ bufContext.pixels; bufContext.pixels _ NIL; -- to help garbage collector? bufContext.props _ Atom.RemPropFromList[bufContext.props, $FullDisplayMemory]; texture.type _ type; texture.props _ PutProp[texture.props, $FileName, fileName]; IF type = $Bump THEN texture.props _ PutProp[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[ IntSequenceRep[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 G3dRender.Error[$MisMatch, "No texture to Sum"]; WITH texture.pixels SELECT FROM pxMap: ImagerPixel.PixelMap => pixels _ pxMap; summed: REF SummedTexture => RETURN; -- previously summed ENDCASE => SIGNAL G3dRender.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.