ValidatePolyhedron:
PUBLIC 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: BOOLEAN ← TRUE;
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];
IF shape.faces =
NIL
THEN {
-- always get face normals
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];
IF NOT shape.visible THEN RETURN[shape]; -- don't bother, not visible
Create or update patch-by-patch description
IF render.patch = NIL THEN {
Build patch sequence for shape
render.patch ← NEW[PatchSequenceRep[shape.surfaces.length]];
render.patch.length ← shape.surfaces.length;
FOR i:
NAT
IN [0..render.patch.length)
DO
render.patch[i] ← NEW[Patch[shape.surfaces[i].vertices.length]];
FOR j:
NAT
IN [0..shape.surfaces[i].vertices.length)
DO
vtx: NAT ← shape.surfaces[i].vertices[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].vertices.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);
};
IF render.patch[i].clipState # out
THEN {
Check for backfacing
eyeNorm: Triple ← G3dMatrix.TransformVec[ shape.faces[i].normal, xfm];
awayness:
REAL ← G3dVector.Dot[
-- normal front or back facing?
G3dVector.Unit[[render.patch[i][0].coord.ex, render.patch[i][0].coord.ey, render.patch[i][0].coord.ez]],
G3dVector.Unit[eyeNorm]
];
IF render.patch[i].type = $ConvexPolygon
THEN {
IF awayness > 0.0
THEN render.patch[i].dir ← back
ELSE
IF awayness < 0.0
THEN render.patch[i].dir ← front
ELSE render.patch[i].dir ← unknown;
IF (render.patch[i].oneSided AND render.patch[i].dir = back) THEN LOOP;
};
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.r ← render.patch[i][0].shade.r;
render.patch[i][j].shade.g ← render.patch[i][0].shade.g;
render.patch[i][j].shade.b ← render.patch[i][0].shade.b;
render.patch[i][j].shade.t ← render.patch[i][0].shade.t;
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;
render.patch[i][j].shade.et ← render.patch[i][0].shade.et;
ENDLOOP;
};
Get screen coordinates for on-screen polygons
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
shape.renderValid ← TRUE;
RETURN[shape];
};
SetEyeSpace:
PUBLIC
PROC[ context: Context ] ~ {
in, right, up, normal: Triple;
mtx: Matrix;
wndw: Rectangle;
viewSize, xSize, ySize, aspectRatio: REAL;
IF context.viewPort.h <= 0 OR context.viewPort.w <= 0 THEN RETURN[];
transform from world to eye
in ← G3dVector.Unit[G3dVector.Sub[context.lookAt, context.eyePoint]];
IF G3dVector.Null[in]
THEN
SIGNAL G3dRender.Error[$MisMatch, "Eye and Pt of Interest identical"];
right ← G3dVector.Unit[G3dVector.Cross[in, context.upDirection]];
IF G3dVector.Null[right] THEN right ← [1.0, 0.0, 0.0]; -- looking straight down
up ← G3dVector.Unit[G3dVector.Cross[right, in]];
unit not really needed (to avoid roundoff problems)
context.eyeSpaceXfm ← G3dMatrix.Identity[];
context.eyeSpaceXfm[0][0] ← right.x;
context.eyeSpaceXfm[1][0] ← right.y;
context.eyeSpaceXfm[2][0] ← right.z;
context.eyeSpaceXfm[3][0] ← -G3dVector.Dot[right, context.eyePoint];
context.eyeSpaceXfm[0][1] ← up.x;
context.eyeSpaceXfm[1][1] ← up.y;
context.eyeSpaceXfm[2][1] ← up.z;
context.eyeSpaceXfm[3][1] ← -G3dVector.Dot[up, context.eyePoint];
context.eyeSpaceXfm[0][2] ← in.x;
context.eyeSpaceXfm[1][2] ← in.y;
context.eyeSpaceXfm[2][2] ← in.z;
context.eyeSpaceXfm[3][2] ← -G3dVector.Dot[in, context.eyePoint];
mtx ← G3dMatrix.Identity[]; -- Roll about z-axis, clockwise
mtx[0][0] ← RealFns.CosDeg[context.rollAngle];
mtx[0][1] ← -RealFns.SinDeg[context.rollAngle];
mtx[1][0] ← RealFns.SinDeg[context.rollAngle];
mtx[1][1] ← RealFns.CosDeg[context.rollAngle];
context.eyeSpaceXfm ← G3dMatrix.Mul[context.eyeSpaceXfm, mtx];
bound window by -1.0 < x, y < 1.0
IF NOT (context.window.w > 0.0 AND context.window.h > 0.0) THEN RETURN[];
aspectRatio ← context.pixelAspectRatio * (context.viewPort.w/context.viewPort.h);
xSize ← IF aspectRatio > 1.0 THEN 1.0 ELSE aspectRatio;
ySize ← IF aspectRatio < 1.0 THEN 1.0 ELSE 1.0 / aspectRatio;
wndw.x ← MIN[xSize, MAX[-xSize, context.window.x] ];
wndw.w ← MIN[xSize-wndw.x, context.window.w ];
wndw.y ← MIN[ySize, MAX[-ySize, context.window.y] ];
wndw.h ← MIN[ySize-wndw.y, context.window.h ];
viewSize ← RealFns.TanDeg[context.fieldOfView/2.0];
-- scale factor for angle of view
generate clipping planes
context.clippingPlanes[Near] ← [0., 0., 1., -context.hitherLimit];
context.clippingPlanes[Far] ← [0., 0., -1., context.yonLimit];
unit plane equation for true distances
normal ← G3dVector.Unit[[1., 0., -(wndw.x * viewSize)]];
context.clippingPlanes[Left] ← [normal.x, 0., normal.z, 0.];
normal ← G3dVector.Unit[[-1., 0., (wndw.x + wndw.w) * viewSize]];
context.clippingPlanes[Right] ← [normal.x, 0., normal.z, 0.];
normal ← G3dVector.Unit[[0., 1., -(wndw.y * viewSize)]];
context.clippingPlanes[Bottom] ← [0., normal.y, normal.z, 0.];
normal ← G3dVector.Unit[[0., -1., (wndw.y + wndw.h) * viewSize]];
context.clippingPlanes[Top] ← [0., normal.y, normal.z, 0.];
compute the transformation from the eye coord sys to unit display coordinates (0.—1.) after perspective divide
context.eyeToNdc.scaleX ← (2.0 / wndw.w) * (1.0 / viewSize) / 2.0;
context.eyeToNdc.addX ← -(wndw.x / wndw.w); -- center after perspective divide
context.eyeToNdc.scaleY ← (2.0 / wndw.h) * (1.0 / viewSize) / 2.0;
context.eyeToNdc.addY ← -(wndw.y / wndw.h); -- center after perspective divide
context.eyeToNdc.scaleZ ← context.yonLimit / (context.yonLimit - context.hitherLimit);
context.eyeToNdc.addZ ← -(context.hitherLimit*context.yonLimit) /
(context.yonLimit - context.hitherLimit);
};