Copyright © 1984, 1986 by Xerox Corporation. All rights reserved.
Last Edited by: Crow, May 16, 1989 2:57:15 pm PDT
DIRECTORY Atom, Basics, BasicTime, Convert, G3dMatrix, G3dPlane, G3dRender, G3dScanConvert, G3dShadeClipXfm, G3dShape, G3dSortandDisplay, G3dVector, Real, Rope;
Internal Declarations
Context: TYPE ~ G3dRender.Context;
RGB: TYPE ~ G3dRender.RGB; -- [ r, g, b: REAL];
SixSides: TYPE ~ G3dRender.SixSides;
ScaleAndAddXfm: TYPE ~ G3dRender.ScaleAndAddXfm;
OutCode: TYPE ~ G3dRender.OutCode;
Matrix: TYPE ~ G3dRender.Matrix;
Triple: TYPE ~ G3dRender.Triple;
TripleSequence: TYPE ~ G3dRender.TripleSequence;
NatSequence: TYPE ~ G3dRender.NatSequence;
IntegerSequence: TYPE ~ G3dRender.IntegerSequence;
RealSequence: TYPE ~ G3dRender.RealSequence;
Shape: TYPE ~ G3dRender.Shape;
FacingDir: TYPE ~ G3dRender.FacingDir;
Patch: TYPE ~ G3dRender.Patch;
VertexSequence: TYPE ~ G3dShape.VertexSequence;
PatchSequence: TYPE ~ G3dRender.PatchSequence;
PatchSequenceRep: TYPE ~ G3dRender.PatchSequenceRep;
CtlPoint: TYPE ~ G3dRender.CtlPoint;
CtlPointSequence: TYPE ~ G3dRender.CtlPointSequence;
CtlPtInfo: TYPE ~ G3dRender.CtlPtInfo;
CtlPtInfoSequence: TYPE ~ G3dRender.CtlPtInfoSequence;
CtlPtInfoSequenceRep: TYPE ~ G3dRender.CtlPtInfoSequenceRep;
CtlPtInfoProc: TYPE ~ G3dRender.CtlPtInfoProc;
Shading: TYPE ~ G3dRender.Shading;
ShadingSequence: TYPE ~ G3dRender.ShadingSequence;
ShapeClass: TYPE ~ G3dRender.ShapeClass;
ShadingClass: TYPE ~ G3dRender.ShadingClass;
ShapeProc: TYPE ~ G3dRender.ShapeProc; -- PROC[ Context, Shape ]
LORA:
TYPE ~
LIST
OF
REF
ANY;
NoneOut: OutCode ~ G3dRender.NoneOut;
AllOut: OutCode ~ G3dRender.AllOut;
Renamed Procedures
Transform: PROC[p: Triple, mat: Matrix] RETURNS[Triple] ~ G3dMatrix.Transform;
TransformVec: PROC[vec: Triple, mat: Matrix] RETURNS[Triple] ~
G3dMatrix.TransformVec;
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;
Caching Procedures
allocation avoidance structures - caches of peculiar data types
vertexStore: CtlPtInfoSequence ← NEW[ CtlPtInfoSequenceRep[96] ]; -- CtlPoint Cache
vertexStoreLength: NAT ← 96;
vertexStorePtr: NAT ← 0; -- place to return next free record
GetCtlPtInfo:
PUBLIC
ENTRY
PROC[]
RETURNS[
REF CtlPtInfo] ~ {
ENABLE UNWIND => NULL;
vtx: REF CtlPtInfo;
IF vertexStorePtr = 0
THEN vtx ← NEW[CtlPtInfo]
ELSE {
vertexStorePtr ← vertexStorePtr - 1;
vtx ← vertexStore[vertexStorePtr];
vertexStore[vertexStorePtr] ← NIL;
};
RETURN[ vtx ];
};
ReleaseCtlPtInfo:
PUBLIC
ENTRY
PROC[vtx:
REF CtlPtInfo] ~ {
ENABLE UNWIND => NULL;
IF vertexStorePtr = vertexStoreLength
THEN {
vertexStore ← NEW[ CtlPtInfoSequenceRep[vertexStoreLength + 32] ];
vertexStoreLength ← vertexStoreLength + 32;
vertexStorePtr ← 0;
};
IF vertexStorePtr > 0
AND vertexStore[vertexStorePtr-1] = vtx
THEN SIGNAL G3dRender.Error[$MisMatch, "Double vertexInfo release"]
ELSE vertexStore[vertexStorePtr] ← vtx;
vertexStorePtr ← vertexStorePtr + 1;
};
patchCache: PatchSequence ← NEW[ PatchSequenceRep[16] ]; -- temps for clipping, etc.
patchCacheLength: NAT ← 16;
patchCachePtr: NAT ← 0; -- place to return next free record
GetPatch:
PUBLIC
ENTRY
PROC[size:
NAT]
RETURNS[
REF Patch] ~ {
ENABLE UNWIND => NULL;
p: REF Patch;
IF patchCachePtr = 0
THEN p ← NEW[Patch[size]]
ELSE {
patchCachePtr ← patchCachePtr - 1;
p ← patchCache[patchCachePtr];
patchCache[patchCachePtr] ← NIL;
IF p.maxLength < size THEN p ← NEW[Patch[size]];
};
RETURN[ p ];
};
ReleasePatch:
PUBLIC
ENTRY
PROC[p:
REF Patch] ~ {
ENABLE UNWIND => NULL;
IF p = NIL THEN RETURN[];
IF patchCachePtr = patchCacheLength
THEN {
patchCache ← NEW[ PatchSequenceRep[patchCacheLength + 2] ];
patchCacheLength ← patchCacheLength + 2;
patchCachePtr ← 0;
};
IF patchCachePtr > 0
AND patchCache[patchCachePtr-1] = p
THEN SIGNAL G3dRender.Error[$MisMatch, "Double patch release"]
ELSE patchCache[patchCachePtr] ← p;
patchCachePtr ← patchCachePtr + 1;
};
Utility Procedures
Sgn:
PROCEDURE [number:
REAL]
RETURNS [
INT] ~
INLINE {
IF number < 0. THEN RETURN[-1] ELSE RETURN[1];
};
Ceiling:
PROC[number:
REAL]
RETURNS[result:
INTEGER] ~ {
result ← Real.Round[number];
IF result < number THEN result ← result + 1;
};
ElapsedTime:
PROC[startTime:
REAL]
RETURNS[Rope.
ROPE] ~ {
timeX10: REAL ← 10.0 * (BasicTime.PulsesToSeconds[BasicTime.GetClockPulses[]] - startTime);
RETURN[ Rope.Cat[ Convert.RopeFromReal[ Real.Fix[timeX10] / 10.0 ], " secs. " ] ];
};
CurrentTime:
PROC[]
RETURNS[
REAL] ~ {
RETURN[ BasicTime.PulsesToSeconds[BasicTime.GetClockPulses[]] ];
};
DiffPosns:
PROC[vtx1, vtx2: CtlPoint]
RETURNS[Triple] ~ {
RETURN[[vtx1.x - vtx2.x, vtx1.y - vtx2.y, vtx1.z - vtx2.z]] };
GetNormal:
PROC[ vertices: VertexSequence, poly: NatSequence, cVtx:
NAT ]
RETURNS[ normal: Triple ] ~ {
lVtx: NAT ← poly[ (cVtx + poly.length - 1) MOD poly.length ];
nVtx: NAT ← poly[ (cVtx + 1) MOD poly.length ];
cVtx ← poly[cVtx];
normal ← G3dVector.Cross[
-- in object space so do right-handed
G3dVector.Sub[ vertices[lVtx].point, vertices[cVtx].point ],
G3dVector.Sub[ vertices[nVtx].point, vertices[cVtx].point ]
];
};
HoldEverything:
PROCEDURE [] ~ {
ERROR ABORTED;
};
Procedures for Manipulating Shapes
GetPolyNmls:
PUBLIC
PROC[context: Context, shape: Shape] ~ {
Compute Normals, Sum normals at polygon corners to get polygon normal
Sum normals for vertices given by adjacent polygon corners, only for polygons!
xfm: Matrix ← G3dMatrix.Mul[shape.matrix, context.eyeSpaceXfm];
IF shape.faces.valid.normal THEN RETURN; -- don't mess with pre-existing normals
IF shape.type # $ConvexPolygon
AND shape.type # $Poly
THEN {
SIGNAL G3dRender.Error[$MisMatch, "Operation only for polygons"];
RETURN[];
};
FOR i:
NAT
IN [0..shape.surfaces.length)
DO
sumNmls: Triple ← [0., 0., 0.];
FOR cVtx:
NAT
IN [0..shape.surfaces[i].length)
DO
sumNmls ← G3dVector.Add[ sumNmls,
GetNormal[shape.vertices, shape.surfaces[i], cVtx] ];
ENDLOOP;
shape.faces[i].normal ← G3dVector.Normalize[sumNmls];
ENDLOOP;
shape.faces.valid.normal ← TRUE;
};
GetVtxNmls:
PUBLIC
PROC[context: Context, shape: Shape] ~ {
Sum normals for vertices given by adjacent polygon corners, only for polygons!
xfm: Matrix ← G3dMatrix.Mul[shape.matrix, context.eyeSpaceXfm];
IF shape.vertices.valid.normal THEN RETURN; -- don't mess with pre-existing normals
IF shape.type # $ConvexPolygon
AND shape.type # $Poly
THEN {
SIGNAL G3dRender.Error[$MisMatch, "Operation only for polygons"];
RETURN[];
};
FOR i:
NAT
IN [0..shape.surfaces.length)
DO
-- get normals at vertices, add to earlier ones
IF shape.surfaces[i] #
NIL
THEN
FOR cVtx:
NAT
IN [0..shape.surfaces[i].length)
DO
OPEN shape.vertices[shape.surfaces[i][cVtx]];
cornerNormal: Triple ← GetNormal[ shape.vertices, shape.surfaces[i], cVtx ];
normal ← G3dVector.Add[ cornerNormal, normal]; -- assumes normal initally zero
ENDLOOP;
ENDLOOP;
FOR i:
NAT
IN [0..shape.vertices.length)
DO
IF shape.vertices[i] #
NIL
THEN {
OPEN shape.vertices[i];
IF G3dVector.Length[ normal ] > shape.sphereExtent.radius * .0001
THEN normal ← G3dVector.Normalize[ normal ]
ELSE normal ← nullTriple;
};
ENDLOOP;
shape.vertices.valid.normal ← TRUE;
};
Procedures for Transformations and Clipping
ClipBoundingSphere:
PUBLIC
PROC[context: Context, shape: Shape, xfm: Matrix]
RETURNS[G3dRender.ClipState] ~ {
Do gross clip test on bounding sphere, all in or all out can avoid clipping entire object
clipFlag: BOOLEAN ← FALSE;
center: Triple ← shape.sphereExtent.center;
eyeCtr: Triple ← G3dMatrix.Transform[ center, xfm];
FOR plane: SixSides
IN SixSides
DO
distance: REAL ← G3dPlane.DistanceToPoint[ eyeCtr, context.clippingPlanes[plane] ];
IF distance < -shape.sphereExtent.radius THEN RETURN[out]
ELSE IF distance < shape.sphereExtent.radius THEN clipFlag ← TRUE;
ENDLOOP;
IF clipFlag THEN RETURN[clipped] ELSE RETURN[in];
};
ClipPoly:
PUBLIC
PROC[ context: Context, poly:
REF Patch]
RETURNS [
REF Patch] ~ {
Clip:
PROC[side: SixSides, pIn, pOut:
REF Patch]
RETURNS [
REF Patch,
REF Patch] = {
Dist:
PROC[side: SixSides, vtx: CtlPoint]
RETURNS [
REAL] = {
-- + inside, - outside
RETURN[ G3dPlane.DistanceToPoint[
[vtx.ex, vtx.ey, vtx.ez],
context.clippingPlanes[side]
] ];
};
lastDist, dist: REAL; outCnt, last: NAT;
IF pIn.nVtces < 2 THEN RETURN[ pIn, pOut ]; -- return if degenerate (line needs 2)
outCnt ← 0;
IF pIn.type # $PolyLine
THEN {
lastDist ← Dist[side, pIn.ctlPt[pIn.nVtces - 1].coord];
last ← pIn.nVtces - 1;
};
IF pOut.maxLength < 2 * pIn.nVtces THEN pOut ← NEW[Patch[2 * pIn.nVtces]];
FOR i:
NAT
IN [0..pIn.nVtces)
DO
a, b: REAL;
dist ← Dist[side, pIn.ctlPt[i].coord];
IF (i # 0
OR pIn.type # $PolyLine)
AND lastDist * dist < 0.
THEN {
Put out point if clip plane crossed
b ← dist / (dist - lastDist); a ← 1.0 - b;
pOut.ctlPt[outCnt].coord.x ← pIn.ctlPt[i].coord.x * a + pIn.ctlPt[last].coord.x * b;
pOut.ctlPt[outCnt].coord.y ← pIn.ctlPt[i].coord.y * a + pIn.ctlPt[last].coord.y * b;
pOut.ctlPt[outCnt].coord.z ← pIn.ctlPt[i].coord.z * a + pIn.ctlPt[last].coord.z * b;
pOut.ctlPt[outCnt].coord.ex ← pIn.ctlPt[i].coord.ex * a + pIn.ctlPt[last].coord.ex * b;
pOut.ctlPt[outCnt].coord.ey ← pIn.ctlPt[i].coord.ey * a + pIn.ctlPt[last].coord.ey * b;
pOut.ctlPt[outCnt].coord.ez ← pIn.ctlPt[i].coord.ez * a + pIn.ctlPt[last].coord.ez * b;
pOut.ctlPt[outCnt].shade.exn ← pIn.ctlPt[i].shade.exn*a + pIn.ctlPt[last].shade.exn*b;
pOut.ctlPt[outCnt].shade.eyn ← pIn.ctlPt[i].shade.eyn*a + pIn.ctlPt[last].shade.eyn*b;
pOut.ctlPt[outCnt].shade.ezn ← pIn.ctlPt[i].shade.ezn*a + pIn.ctlPt[last].shade.ezn*b;
pOut.ctlPt[outCnt].shade.r ← pIn.ctlPt[i].shade.r * a + pIn.ctlPt[last].shade.r * b;
pOut.ctlPt[outCnt].shade.g ← pIn.ctlPt[i].shade.g * a + pIn.ctlPt[last].shade.g * b;
pOut.ctlPt[outCnt].shade.b ← pIn.ctlPt[i].shade.b * a + pIn.ctlPt[last].shade.b * b;
pOut.ctlPt[outCnt].shade.t ← pIn.ctlPt[i].shade.t * a + pIn.ctlPt[last].shade.t* b;
pOut.ctlPt[outCnt].shade.txtrX ← pIn.ctlPt[i].shade.txtrX * a
+ pIn.ctlPt[last].shade.txtrX* b;
pOut.ctlPt[outCnt].shade.txtrY ← pIn.ctlPt[i].shade.txtrY * a
+ pIn.ctlPt[last].shade.txtrY* b;
pOut.ctlPt[outCnt].shade.er ← pIn.ctlPt[i].shade.er * a + pIn.ctlPt[last].shade.er * b;
pOut.ctlPt[outCnt].shade.eg ← pIn.ctlPt[i].shade.eg * a + pIn.ctlPt[last].shade.eg * b;
pOut.ctlPt[outCnt].shade.eb ← pIn.ctlPt[i].shade.eb * a + pIn.ctlPt[last].shade.eb * b;
pOut.ctlPt[outCnt].shade.et ← pIn.ctlPt[i].shade.et * a + pIn.ctlPt[last].shade.et * b;
outCnt ← outCnt + 1;
};
IF dist >= 0.
THEN {
-- put out point if inside
pOut.ctlPt[outCnt].coord.x ← pIn.ctlPt[i].coord.x;
pOut.ctlPt[outCnt].coord.y ← pIn.ctlPt[i].coord.y;
pOut.ctlPt[outCnt].coord.z ← pIn.ctlPt[i].coord.z;
pOut.ctlPt[outCnt].coord.ex ← pIn.ctlPt[i].coord.ex;
pOut.ctlPt[outCnt].coord.ey ← pIn.ctlPt[i].coord.ey;
pOut.ctlPt[outCnt].coord.ez ← pIn.ctlPt[i].coord.ez;
pOut.ctlPt[outCnt].shade.exn ← pIn.ctlPt[i].shade.exn;
pOut.ctlPt[outCnt].shade.eyn ← pIn.ctlPt[i].shade.eyn;
pOut.ctlPt[outCnt].shade.ezn ← pIn.ctlPt[i].shade.ezn;
pOut.ctlPt[outCnt].shade.r ← pIn.ctlPt[i].shade.r;
pOut.ctlPt[outCnt].shade.g ← pIn.ctlPt[i].shade.g;
pOut.ctlPt[outCnt].shade.b ← pIn.ctlPt[i].shade.b;
pOut.ctlPt[outCnt].shade.t ← pIn.ctlPt[i].shade.t;
pOut.ctlPt[outCnt].shade.txtrX ← pIn.ctlPt[i].shade.txtrX;
pOut.ctlPt[outCnt].shade.txtrY ← pIn.ctlPt[i].shade.txtrY;
pOut.ctlPt[outCnt].shade.er ← pIn.ctlPt[i].shade.er;
pOut.ctlPt[outCnt].shade.eg ← pIn.ctlPt[i].shade.eg;
pOut.ctlPt[outCnt].shade.eb ← pIn.ctlPt[i].shade.eb;
pOut.ctlPt[outCnt].shade.et ← pIn.ctlPt[i].shade.et;
outCnt ← outCnt + 1;
};
lastDist ← dist; last ← i;
ENDLOOP;
pOut.type ← pIn.type;
pOut.oneSided ← pIn.oneSided;
pOut.dir ← pIn.dir;
pOut.nVtces ← outCnt;
pOut.renderData ← pIn.renderData;
pOut.props ← pIn.props;
RETURN [ pOut, pIn ];
}; -- end Clip Proc
orOfCodes: OutCode ← NoneOut;
poly2: REF Patch ← GetPatch[2 * poly.nVtces]; -- get temp patch, released below
IF poly.type # $ConvexPolygon
AND poly.type # $Poly
AND poly.type # $PolyLine
THEN {
SIGNAL G3dRender.Error[$Unimplemented, "Not clippable as polygon"];
RETURN[ NIL ];
};
FOR i:
NAT
IN [0..poly.nVtces)
DO
orOfCodes ←
LOOPHOLE[
Basics.BITOR[LOOPHOLE[orOfCodes], LOOPHOLE[poly.ctlPt[i].coord.clip]],
OutCode];
ENDLOOP;
IF orOfCodes.near THEN [poly, poly2] ← Clip[ Near, poly, poly2];
IF orOfCodes.far THEN [poly, poly2] ← Clip[ Far, poly, poly2];
IF orOfCodes.left THEN [poly, poly2] ← Clip[ Left, poly, poly2];
IF orOfCodes.right THEN [poly, poly2] ← Clip[ Right, poly, poly2];
IF orOfCodes.bottom THEN [poly, poly2] ← Clip[Bottom, poly, poly2];
IF orOfCodes.top THEN [poly, poly2] ← Clip[ Top, poly, poly2];
ReleasePatch[poly2]; -- done with temp patch
RETURN[ poly ];
};
GetPatchClipState:
PUBLIC
PROC[ patch:
REF Patch] ~ {
orOfCodes: OutCode ← NoneOut;
andOfCodes: OutCode ← AllOut;
FOR i:
NAT
IN [0..patch.nVtces)
DO
orOfCodes ←
LOOPHOLE[
Basics.BITOR[ LOOPHOLE[orOfCodes], LOOPHOLE[patch[i].coord.clip]],
OutCode];
andOfCodes ←
LOOPHOLE[
Basics.BITAND[LOOPHOLE[andOfCodes], LOOPHOLE[patch[i].coord.clip]],
OutCode];
ENDLOOP;
IF andOfCodes # NoneOut
THEN patch.clipState ← out
ELSE
IF orOfCodes = NoneOut
THEN patch.clipState ← in
ELSE patch.clipState ← clipped;
};
GetClipCodeForPt:
PUBLIC
PROC[context: Context, pt: Triple]
RETURNS[clip: OutCode] ~ {
Compute outcode for one set of coordinates in eyespace
clip.bottom← G3dPlane.DistanceToPoint[ pt, context.clippingPlanes[Bottom]] < 0.;
clip.top ← G3dPlane.DistanceToPoint[ pt, context.clippingPlanes[Top] ] < 0.;
clip.left ← G3dPlane.DistanceToPoint[ pt, context.clippingPlanes[Left] ] < 0.;
clip.right ← G3dPlane.DistanceToPoint[ pt, context.clippingPlanes[Right] ] < 0.;
clip.near ← G3dPlane.DistanceToPoint[ pt, context.clippingPlanes[Near] ] < 0.;
clip.far ← G3dPlane.DistanceToPoint[ pt, context.clippingPlanes[Far] ] < 0.;
};
XfmPtToEyeSpace:
PUBLIC
PROC[context: Context, pt: Triple, xfm: Matrix ←
NIL]
RETURNS[Triple, OutCode] ~ {
Transform CtlPoint to Eye Space
IF xfm = NIL THEN xfm ← context.eyeSpaceXfm;
pt ← Transform[ pt, xfm ];
RETURN[ pt, GetClipCodeForPt[context, pt] ];
};
XfmTripleToDisplay:
PUBLIC
PROC[pt: Triple, xfm, display: ScaleAndAddXfm, offset:
REAL]
RETURNS[result: Triple] ~ {
Transform vertex from eyespace to display coordinates - local utility
aLilBit: REAL ← G3dScanConvert.justNoticeable * G3dScanConvert.justNoticeable;
IF pt.z <= 0.0
THEN {
SIGNAL G3dRender.Error[$MisMatch, "Negative depth"];
pt.z ← .001; -- fudge bad depths
};
result.x ← xfm.scaleX*pt.x/pt.z + xfm.addX; -- convert to normalized display coords
result.y ← xfm.scaleY*pt.y/pt.z + xfm.addY;
result.z ← xfm.scaleZ/pt.z + xfm.addZ;
result.x ← display.scaleX * result.x + display.addX; -- convert to screen coordinates
result.y ← display.scaleY * result.y + display.addY;
result.z ← display.scaleZ * result.z + display.addZ;
result.x ← result.x - offset; -- nojaggy tiler offsets by 1/2 pixel and spreads out by 1
result.y ← result.y - offset;
RETURN [ result ];
};
XfmPtToDisplay:
PUBLIC
PROC[context: Context, pt: Triple,
shape: Shape ←
NIL]
RETURNS[Triple] ~ {
Transform vertex from eyespace to display coordinates
result: Triple ← XfmTripleToDisplay[
pt: pt,
xfm: context.eyeToNdc,
display: context.ndcToPixels,
offset: IF context.antiAliasing THEN .5 ELSE 0.0 -- nojaggy tiler offsets by 1/2 pixel
];
IF shape #
NIL
THEN {
xLo: INTEGER ← Real.Fix[ MAX[0.0, result.x - 2.0] ];
xHi: INTEGER ← Ceiling[ MIN[context.viewPort.w, result.x + 2.0] ];
yLo: INTEGER ← Real.Fix[ MAX[0.0, result.y - 2.0] ];
yHi: INTEGER ← Ceiling[ MIN[context.viewPort.h, result.y + 2.0] ];
IF shape.screenExtent.min.x > xLo THEN shape.screenExtent.min.x ← xLo;
IF shape.screenExtent.max.x < xHi THEN shape.screenExtent.max.x ← xHi;
IF shape.screenExtent.min.y > yLo THEN shape.screenExtent.min.y ← yLo;
IF shape.screenExtent.max.y < yHi THEN shape.screenExtent.max.y ← yHi;
};
RETURN [ result ];
};