MappedAndSolidTextureImpl.mesa
Copyright © 1984, 1986 by Xerox Corporation. All rights reserved.
Last Edited by: Crow, March 16, 1989 9:50:21 am PST
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
Internal Declarations
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.];
};
Renamed Procedures
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;
Global Variables
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
Initialization
Init: PROC[] ~ {
txtrShadingClass: ShadingClass ← [
type: $MappedAndSolidTexture,
cnvrtVtx: GetLerpedVals,
getColor: RecoverColor,
loadShapeAux: LoadShapeAux,
loadVtxAux: LoadVtxAux,
lerpVtxAux: LerpVtxAux,
shadeVtx: ShapeUtilities.ShadeVtx
];
ThreeDBasics.RegisterShadingClass[txtrShadingClass, $MappedAndSolidTexture];
Register solid texture procs
RegisterTextureFunction[ $Spots, Spots ];
RegisterTextureFunction[ $Wurlitzer, Wurlitzer ];
RegisterTextureFunction[ $TwistedStripes, TwistedStripes ];
RegisterTextureFunction[ $BurlWood, BurlWood ];
RegisterTextureFunction[ $ZebraBurl, ZebraBurl ];
RegisterTextureFunction[ $Marble, Marble ];
};
Procedures for Controlling Texture
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;
};
Procedures for VtxToRealSeq and computing color at a spot
GetLerpedVals: ThreeDBasics.VtxToRealSeqProc ~ {
PROC[dest: REF RealSequence, source: VertexInfo, data: REF ANY] RTRNS[REF RealSequence];
maxLength: NATIF 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 ~ {
PROC[context: REF Context, shading: REF ShadingClass, spot: REF Spot]
IF shading.texture # NIL THEN GetTxtrAt[context, shading, spot];
IF shading.texture # NIL OR spot.val.length > 15
THEN RenderWithPixels.ShadeSpot[context, shading, spot];
};
Procedures for Auxiliary Clipping and Shading
LoadShapeAux: PUBLIC ShapeProc ~ {  -- load aux field in vtx
PROC[context: REF Context, shape: REF ShapeInstance, data: REF ANYNIL] RETURNS[REF ShapeInstance];
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
PROC[ context: REF Context, vtx: REF VertexInfo, data: REF ANYNIL ] RETURNS[REF VertexInfo];
vtxAux: REF Pair ← NEW[Pair];
WITH data SELECT FROM
input: LORA => {
auxInfo: REF PairSequence ← NARROW[ input.first ];
index: INTEGERNARROW[ 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
PROC[ context: REF Context, vtx: REF VertexInfo, data: REF ANYNIL ] RETURNS[REF VertexInfo];
input: LORANARROW[data];
vtxAux: REF Pair ← NEW[Pair];
vtxa: REF Pair ← NARROW[ input.first ];
vtxb: REF Pair ← NARROW[ input.rest.first ];
a: REALNARROW[input.rest.rest.first, REF REAL]^;
b: REALNARROW[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 ];
};
Support Procedures for setting up texture for tiler
AdjustTexture: PUBLIC PROC[poly: REF Patch, texture: LORA,
         halfXRange, halfYRange: REAL ← .5] ~ {
mappedTexture: BOOLEANFALSE;
FOR txtrList: LORANARROW[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;
};
};
Procedures for Evaluating Texture at a Spot
GetTxtrAt: PUBLIC SpotProc ~ {
PROC[context: REF Context, shading: REF ShadingClass, spot: REF Spot]
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: REALIF 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;
Increments in x and y
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
This section could be executed once per scan segment
maxXIncr, maxYIncr, area, length: REAL;
txtrXIncr is vector for texture change to next pixel on scanline
txtrXIncr: Pair ← [spot.xIncr[txtrX], spot.xIncr[txtrY]];
txtrYIncr is vector for texture change to corresponding pixel on scanline above
txtrYIncr: Pair ← [-txtrXIncr.y, txtrXIncr.x];     -- rotate txtrXIncr 90 deg.
scale txtrYIncr to projected length of texture increment along leading polygon edge
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]];
};
Find max size of texture offsets to adjacent pixels to estimate texture spot spread
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;
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)
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];
perturb in x (rotate about y)
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];
perturb in y (rotate about x)
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"]];
};
Support Procedures for Summed Textures
GetLerpedValue: PROC[ llVal, ulVal, urVal, lrVal: INT, xPos, yPos: REAL ]
RETURNS[ intPart: INT, fracPart: REAL ] ~ {
lowerValue: REAL ← llVal + xPos * (lrVal - llVal);  -- if we only had double precision!!
upperValue: REAL ← ulVal + xPos * (urVal - ulVal);
RETURN [ lowerValue + yPos * (upperValue - lowerValue) ];
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;
};
Procedures for solid texture calculation
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 ~ {
Regular array of dark spots
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 ~ {  
Wurlitzer colors, stripes in 3-d
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 ~ {
Rotating stripes (barber pole)
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: REALABS[ RealFns.Sin[midBrown] ];
greenLayer: REAL ← - brownLayer;
perturb: REALIF 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: REALIF 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 ~ {
Perlin's marble texture
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] ~ {
returns band limited noise over R3.
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] ~ {
map the unit interval into an "S shaped" cubic f[x] | f[0]=0, f'[0]=0, f[1]=1, f'[1]=0.
RETURN [x * x * (3 - 2 * x)];
};
declare local variables.
m: NAT;
ix, iy, iz: INT;
x, y, z, jx, jy, jz, sx, sy, sz, tx, ty, tz, s, f: REAL;
initialize random gradient table
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;
};
Force everything to be positive
x ← vx + 1000.;
y ← vy + 1000.;
z ← vz + 1000.;
ixyz ← the integer lattice point "just below" v (identifies the surrounding unit cube).
ix ← Real.Fix[x];
iy ← Real.Fix[y];
iz ← Real.Fix[z];
sxyz ← the vector difference v - ixyz biased with an S-Curve in each dimension.
sx ← SCurve[x - ix];
sy ← SCurve[y - iy];
sz ← SCurve[z - iz];
txyz ← the complementary set of S-Curves in each dimension.
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;
Add in each weighted component
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];
};
Procedures for Recovering Texture Descriptions from streams
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 ~ {
PROC[context: REF Context, shape: REF ShapeInstance, data: REF ANYNIL] RETURNS[REF ShapeInstance];
input: IO.STREAMNARROW[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 ~ {
PROC[context: REF Context, shape: REF ShapeInstance, data: REF ANYNIL] RETURNS[REF ShapeInstance];
input: IO.STREAMNARROW[data];
AddSolidTexture[ context, shape.name, IO.GetAtom[input] ];
RETURN[ shape ];
};
Procedures for Computing Texture Coordinates
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
Stretch as indicated by corner coordinates
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
Map from sphere to Cartesian coordinates 1st quadrant 0 - 1 range.
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];
Map polar coordinates into quadrilateral given by sw, nw, ne, se
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];
};
Procedures for Reading and Preparing Texture Files
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;
Previously used texture?
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;
Create context and load texture into it
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.