PolySilhouetteProcs.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Last Edited by: Crow, May 30, 1989 11:37:40 am PDT
Bloomenthal, September 14, 1988 1:06:44 pm PDT
DIRECTORY Atom, Basics, G3dBasic, G3dMatrix, G3dRender, G3dScanConvert, G3dClipXfmShade, G3dShape, G3dVector;
PolySilhouetteProcs: CEDAR MONITOR
IMPORTS Atom, Basics, G3dMatrix, G3dRender, G3dClipXfmShade, G3dVector
= BEGIN
Types
Ray:      TYPE ~ G3dBasic.Ray;
NatSequence:   TYPE ~ G3dRender.NatSequence;
NatSequenceRep:  TYPE ~ G3dBasic.NatSequenceRep;
Pair:     TYPE ~ G3dRender.Pair;
Triple:    TYPE ~ G3dRender.Triple;
TripleSequence:  TYPE ~ G3dRender.TripleSequence;
TripleSequenceRep: TYPE ~ G3dBasic.TripleSequenceRep;
RGB:     TYPE ~ G3dRender.RGB;
RealSequence:  TYPE ~ G3dRender.RealSequence;
RealSequenceRep: TYPE ~ G3dBasic.RealSequenceRep;
Context:    TYPE ~ G3dRender.Context;
SixSides:    TYPE ~ G3dRender.SixSides;
Matrix:    TYPE ~ G3dRender.Matrix;
Shape:     TYPE ~ G3dRender.Shape;
Vertex:     TYPE ~ G3dShape.Vertex;
Patch:     TYPE ~ G3dRender.Patch;
PatchSequence:  TYPE ~ G3dRender.PatchSequence;
PatchSequenceRep: TYPE ~ G3dRender.PatchSequenceRep;
FaceRep:    TYPE ~ G3dShape.FaceRep;
FaceSequenceRep: TYPE ~ G3dShape.FaceSequenceRep;
PatchProc:   TYPE ~ G3dRender.PatchProc;
CtlPoint:    TYPE ~ G3dRender.CtlPoint;
CtlPtInfo:    TYPE ~ G3dRender.CtlPtInfo;
RECORD[coord: CtlPoint, shade: Shading, vtxPtr: NAT, data: REF];
CtlPtInfoSequence: TYPE ~ G3dRender.CtlPtInfoSequence;
CtlPtInfoProc:  TYPE ~ G3dRender.CtlPtInfoProc;
Shading:    TYPE ~ G3dRender.Shading;
ShadingClass:  TYPE ~ G3dRender.ShadingClass;
RenderStyle:   TYPE ~ G3dRender.RenderStyle;
RenderData:   TYPE ~ G3dRender.RenderData;
ShapeClass:   TYPE ~ G3dRender.ShapeClass;
ShapeProc:   TYPE ~ G3dRender.ShapeProc;
ClipState:    TYPE ~ G3dRender.ClipState;
OutCode:    TYPE ~ G3dRender.OutCode;
AllOut:    OutCode ~ G3dRender.AllOut;
NoneOut:    OutCode ~ G3dRender.NoneOut;
FacingDir:   TYPE ~ G3dRender.FacingDir;
LORA:     TYPE = LIST OF REF ANY;
MidPtProc: TYPE ~ PROC[v0, v1: REF CtlPtInfo] RETURNS[REF CtlPtInfo];
nullTriple: Triple ~ [0.0, 0.0, 0.0];
Corner: TYPE ~ RECORD [ inVtx, outVtx: NAT ← 0,
        inDir, outDir, normal, interiorKnot: Triple ← nullTriple,
        concave: BOOLEANFALSE ];
Represents a corner of a polygon,
- inVtx is the vertex at the other end of the incoming edge (previous vertex in order),
- outVtx is other vertex on outgoing edge,
- inDir, outDir are the corresponding outward direction vectors,
- normal is the carefully determined normal vector
- concave = TRUE indicates corner is concave vertex needing reversed cross product.
CornerSeq: TYPE ~ RECORD [ length: NAT ← 0, s: SEQUENCE maxLength: NAT OF Corner ];
CornerSeqSeq: TYPE ~ RECORD [ length: NAT ← 0,
          s: SEQUENCE maxLength: NAT OF REF CornerSeq ];
TangentSet: TYPE ~ RECORD [t0, et0, t1, et1: Triple ← nullTriple];
Represents the tangents at the endpoints of an edge (in object space and eyespace), the edge is ordered in the vertex order of the polygon (t0 at leading endpoint, t1 at trailing endpoint)
TangentSeq: TYPE ~ RECORD [length: NAT ← 0, s: SEQUENCE maxLength: NAT OF TangentSet];
TangentTriple: TYPE ~ ARRAY [0..3) OF TangentSet;
TangentQuad: TYPE ~ ARRAY [0..4) OF TangentSet;
TangentSeqSeq: TYPE ~ RECORD [
       length: NAT ← 0, s: SEQUENCE maxLength: NAT OF REF TangentSeq];
       
Triangle: TYPE ~ RECORD [ v: ARRAY[0..3) OF CtlPtInfo, t: ARRAY[0..3) OF TangentSet ];
NatSequenceSequence: TYPE ~ RECORD [
       length: NAT ← 0, s: SEQUENCE maxLength: NAT OF NatSequence ];
BoolSequence: TYPE ~ RECORD [length: NAT ← 0, s: SEQUENCE maxLength: NAT OF BOOLEAN];
Renamed Procedures
Add: PROC[v1, v2: Triple] RETURNS[Triple] ~ G3dVector.Add;
Mul: PROC[v: Triple, s: REAL] RETURNS[Triple] ~ G3dVector.Mul;
Cross: PROC[v1, v2: Triple] RETURNS[Triple] ~ G3dVector.Cross;
Div: PROC[v: Triple, s: REAL] RETURNS[Triple] ~ G3dVector.Div;
Dot: PROC[v1, v2: Triple] RETURNS[REAL] ~ G3dVector.Dot;
Length: PROC[v: Triple] RETURNS[REAL] ~ G3dVector.Length;
Negate: PROC[v: Triple] RETURNS[Triple] ~ G3dVector.Negate;
Nmlize: PROC[v: Triple] RETURNS[Triple] ~ G3dVector.Normalize;
Sub3: PROC[v1, v2: Triple] RETURNS[Triple] ~ G3dVector.Sub;
ReleasePatch: PROC [p: REF Patch] ~ G3dClipXfmShade.ReleasePatch;
GetPatch: PROC [size: NAT] RETURNS [REF Patch] ~ G3dClipXfmShade.GetPatch;
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
-- showLines: BOOLEAN;        -- debug and pedagogical aid
Utility Procedures
Sqr: PROCEDURE [number: REAL] RETURNS [REAL] ~ INLINE { RETURN[number * number]; };
Display Procedures
DisplayNothing: PatchProc ~ {      -- dummy routine for timing tests
RETURN[ patch ];
};
DisplayPatchSilhouette: PatchProc ~ {
PROC[ context: Context, patch: REF Patch, data: REF ANYNIL ] RETURNS[REF Patch]
shape: Shape ← NARROW[ GetProp[patch.props, $Shape] ];
patchNo: NATNARROW[ GetProp[patch.props, $PatchNo], REF NAT ]^;
renderData: REF RenderData ← patch.renderData;
RETURN[patch];
};
Initialization of Classes
InitClasses: PROC[] ~ {    -- register procedures for basic surface types
standardClass: ShapeClass ← G3dRender.GetShapeClass[$ConvexPolygon];
standardClass.type ← $PolygonWithSilhouette;
standardClass.validate ← ValidatePolyWSilhouette;
standardClass.displayPatch ← DisplayPatchSilhouette;
G3dRender.RegisterShapeClass[standardClass, $PolygonWithSilhouette];
};
Validation Procedure
ValidatePolyWSilhouette: ShapeProc ~ {
PROC[context: Context, shape: Shape, data: REF] RETURNS[Shape];
Update patch structure under renderData to ready everything for display, etc.
Shape class, shading class, matrix, bounding Sphere, and gross clip state are expected valid
ValidateScreenCoords: PROC[] ~ {
FOR i: NAT IN [0..shape.vertices.length) DO
eVtx: Triple ← G3dMatrix.Transform[ shape.vertices[i].point, xfm];
eVtx ← G3dClipXfmShade.XfmPtToDisplay[ context, eVtx ];
shape.vertices[i].screen.x ← eVtx.x; shape.vertices[i].screen.y ← eVtx.y;
ENDLOOP;
shape.screenValid ← TRUE;
};
render: REF RenderData ← G3dRender.RenderDataFrom[shape];
shapeClass: REF ShapeClass ← render.class;
shadingClass: REF ShadingClass ← render.shadingClass;
renderStyle: RenderStyle;
xfm: Matrix ← G3dMatrix.Mul[shape.matrix, context.eyeSpaceXfm];
allPolysIn, allPolysOut: BOOLEANTRUE;
WITH shadingClass.renderMethod SELECT FROM
style: REF RenderStyle => renderStyle ← style^;
ENDCASE => renderStyle ← smooth;  -- default to smooth to get normals and shading
Get vertex, face normals if not yet present
IF renderStyle # faceted
THEN {       -- get vertex normals
IF NOT shape.vertices.valid.normal THEN G3dClipXfmShade.GetVtxNmls[context, shape]; }
ELSE {      -- get face normals
IF shape.faces = NIL THEN {
shape.faces ← NEW[FaceSequenceRep[shape.surfaces.length]];
shape.faces.length ← shape.surfaces.length;
FOR i: NAT IN [0..shape.faces.length) DO shape.faces[i] ← NEW[FaceRep]; ENDLOOP;
};
IF NOT shape.faces.valid.normal THEN G3dClipXfmShade.GetPolyNmls[context, shape];
};
Create or update patch-by-patch description
IF GetProp[render.props, $Hidden] # NIL THEN RETURN[shape]; -- don't bother, not visible
IF NOT render.patchesValid OR context.changed OR render.patch = NIL THEN {
IF render.patch = NIL THEN {
Build patch sequence for shape
render.patch ← NEW[PatchSequenceRep[shape.surfaces.length]];
render.patch.lengthshape.surfaces.length;
FOR i: NAT IN [0..render.patch.length) DO
render.patch[i] ← NEW[Patch[shape.surfaces[i].length]];
FOR j: NAT IN [0..shape.surfaces[i].length) DO
vtx: NAT ← shape.surfaces[i][j];
render.patch[i][j].coord.x ← shape.vertices[vtx].point.x;
render.patch[i][j].coord.y ← shape.vertices[vtx].point.y;
render.patch[i][j].coord.z ← shape.vertices[vtx].point.z;
render.patch[i][j].shade.xn ← shape.vertices[vtx].normal.x;
render.patch[i][j].shade.yn ← shape.vertices[vtx].normal.y;
render.patch[i][j].shade.zn ← shape.vertices[vtx].normal.z;
render.patch[i][j].shade.txtrX ← shape.vertices[vtx].texture.x;
render.patch[i][j].shade.txtrY ← shape.vertices[vtx].texture.y;
render.patch[i][j].vtxPtr ← vtx;
ENDLOOP;
render.patch[i].nVtces ← shape.surfaces[i].length;
render.patch[i].type ← shape.type;
render.patch[i].oneSided ← NOT shape.showBackfaces;
render.patch[i].renderData ← render;    -- store REF to shading information
Store REF to parent shape and polygon number in object
render.patch[i].props ← PutProp[render.patch[i].props, $Shape, shape];
render.patch[i].props ← PutProp[render.patch[i].props, $PatchNo, NEW[NAT ← i]];
ENDLOOP;
};
Patches built, update shading, transformed vertices, clip state
Catch unclipped line drawings and expedite
IF renderStyle = lines AND shape.clipState = in AND render.class.display # NIL
THEN { ValidateScreenCoords[]; RETURN [ shape ]; };
shape.screenExtent ← [[32768, 32768], [0, 0]]; -- initialize for computing screenExtent
FOR i: NAT IN [0..render.patch.length) DO
Transform and shade vertices, get clip state for each polygon
andOfCodes: OutCode ← AllOut;     -- test for trivially out
orOfCodes: OutCode ← NoneOut;     -- test for trivially in
FOR j: NAT IN [0..render.patch[i].nVtces) DO
OPEN render.patch[i][j].coord; -- transform control points to eyespace
[ [ex, ey, ez] ] ← G3dMatrix.Transform[ [x, y, z] , xfm];
IF shape.clipState = clipped
THEN {
clip ← G3dClipXfmShade.GetClipCodeForPt[ context, [ex, ey, ez] ];
orOfCodes ← LOOPHOLE[
Basics.BITOR[LOOPHOLE[orOfCodes], LOOPHOLE[ clip] ], OutCode];
andOfCodes ← LOOPHOLE[
Basics.BITAND[ LOOPHOLE[andOfCodes], LOOPHOLE[ clip] ], OutCode];
}
ELSE clip ← NoneOut;
If vertex normals used for shading, transform and get shading
IF renderStyle = smooth OR renderStyle = shadedLines THEN {
OPEN render.patch[i][j].shade;
tmpShininess: REAL; vtx: NAT ← render.patch[i][j].vtxPtr;
[xn, yn, zn] ← shape.vertices[vtx].normal;
[r, g, b] ← shape.vertices[vtx].color; t ← shape.vertices[vtx].transmittance;
IF shape.faces # NIL THEN IF shape.faces.valid.color THEN { -- scale by face color
clr: Triple ← shape.faces[i].color; r ← clr.x * r; g ← clr.y * g; b ← clr.z * b;
};
[[exn, eyn, ezn]] ← G3dMatrix.TransformVec[ [xn, yn, zn] , xfm];
Cache shininess to get vertex shades without highlights
tmpShininess ← shadingClass.shininess; shadingClass.shininess ← 0.0;
render.patch[i][j] ← shadingClass.shadeVtx[
context, render.patch[i][j], shadingClass
];
shadingClass.shininess ← tmpShininess;
};
Plain lines take object color
IF renderStyle = lines THEN {
OPEN render.patch[i][j].shade;
[er, eg, eb] ← shadingClass.color;
};
ENDLOOP;
Collect clipping data for vertices to tag patches, patches to tag shapes
IF orOfCodes = NoneOut  THEN render.patch[i].clipState ← in -- default case
ELSE IF andOfCodes # NoneOut THEN render.patch[i].clipState ← out
          ELSE render.patch[i].clipState ← clipped;
IF shape.clipState = clipped THEN {
allPolysIn ← allPolysIn AND (render.patch[i].clipState = in);
allPolysOut ← allPolysOut AND (render.patch[i].clipState = out);
};
Update polygons for faceted shading
IF renderStyle = faceted THEN {
OPEN render.patch[i][0].shade;
[xn, yn, zn] ← shape.faces[i].normal;
[r, g, b] ← shape.faces[i].color; t ← shape.faces[i].transmittance;
[[exn, eyn, ezn]] ← G3dMatrix.TransformVec[ shape.faces[i].normal, xfm];
render.patch[i][0] ← shadingClass.shadeVtx[context, render.patch[i][0], shadingClass];
FOR j: NAT IN [1..render.patch[i].nVtces) DO
render.patch[i][j].shade.exn ← render.patch[i][0].shade.exn;
render.patch[i][j].shade.eyn ← render.patch[i][0].shade.eyn;
render.patch[i][j].shade.ezn ← render.patch[i][0].shade.ezn;
render.patch[i][j].shade.er ← render.patch[i][0].shade.er;
render.patch[i][j].shade.eg ← render.patch[i][0].shade.eg;
render.patch[i][j].shade.eb ← render.patch[i][0].shade.eb;
ENDLOOP;
};
Get screen coordinates for on-screen polygons
IF render.patch[i].clipState # out THEN FOR j: NAT IN [0..render.patch[i].nVtces) DO
OPEN render.patch[i][j].coord; -- xfm to display, update shape screen extent
[ [sx, sy, sz] ] ← G3dClipXfmShade.XfmPtToDisplay[ context, [ex, ey, ez], shape ];
ENDLOOP;
ENDLOOP;
Refine shape clipstate based on polygon state
IF shape.clipState = clipped THEN   -- refine shape clipstate based on polygon states
IF allPolysIn THEN shape.clipState ← in ELSE IF allPolysOut THEN shape.clipState ← out;
render.patchesValid ← TRUE;
IF shape.clipState = in AND renderStyle = lines AND render.class.display # NIL
THEN ValidateScreenCoords[];     -- prepare for unclipped line drawings
};
RETURN[shape];
};
InitClasses[];
END.