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
Stretch as indicated by corner coordinates - interpolate 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);
Interpolate along row
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
};
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];
Map from sphere to Cartesian coordinates 1st quadrant 0 - 1 range.
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];
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);
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
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
};