G3dDrawShapeImpl.mesa
Copyright Ó 1985, 1992 by Xerox Corporation. All rights reserved.
Bloomenthal, April 14, 1993 4:12 pm PDT
DIRECTORY G3dBasic, G3dClip, G3dDraw, G3dMatrix, G3dShape, G3dVector, Draw2d, Imager, IO, Process, Real;
G3dDrawShapeImpl: CEDAR MONITOR
IMPORTS G3dClip, G3dDraw, G3dMatrix, G3dShape, G3dVector, Draw2d, Imager, IO, Process, Real
EXPORTS G3dDraw
~ BEGIN
Type Declarations
DrawType:    TYPE ~ Draw2d.DrawType;
BoolSequence:   TYPE ~ G3dBasic.BoolSequence;
IntegerPair:   TYPE ~ G3dBasic.IntegerPair;
NatSequence:   TYPE ~ G3dBasic.NatSequence;
Pair:      TYPE ~ G3dBasic.Pair;
Screen:     TYPE ~ G3dBasic.Screen;
SurfaceSequence:  TYPE ~ G3dBasic.SurfaceSequence;
Triple:     TYPE ~ G3dBasic.Triple;
Matrix:     TYPE ~ G3dMatrix.Matrix;
Viewport:    TYPE ~ G3dMatrix.Viewport;
Edge:      TYPE ~ G3dShape.Edge;
ScreenSequence:  TYPE ~ G3dShape.ScreenSequence;
Vertex:     TYPE ~ G3dShape.Vertex;
Context:     TYPE ~ Imager.Context;
huge:      REAL ~ Real.LargestNumber;
strokeWidth:   REAL ¬ 0.0;
vectorScale:   REAL ¬ 0.03;
Shape Drawing
Shape: PUBLIC PROC [
context:   Context,
shape:    G3dShape.Shape,
view:    Matrix,
inverseView:  Matrix ¬ NIL,
viewport:   Viewport ¬ [],
lightVector:  Triple ¬ [1.0, 0.0, 0.0],
screens:   ScreenSequence ¬ NIL,
forceTransform: BOOL ¬ FALSE,
options:   G3dDraw.Options ¬ []]
RETURNS [ScreenSequence]
~ {
All the loops through shape polygons or shape vertices should contain a CheckForAbort[]:
IF shape # NIL AND shape.surfaces # NIL AND shape.vertices # NIL THEN { -- don't wedge!
SetColor: PROC [color: Triple] ~ {
IF color # nowColor THEN G3dDraw.SetColor[context, nowColor ¬ color, useCG6];
};
DrawLine: PROC [i0, i1: INT] ~ {
s0: G3dBasic.Screen ¬ screens[i0];
s1: G3dBasic.Screen ¬ screens[i1];
IF color THEN
SetColor[G3dVector.Midpoint[shape.vertices[i0].color, shape.vertices[i1].color]];
SELECT TRUE FROM
s0.visible AND s1.visible => G3dDraw.Line2d[context, s0.intPos, s1.intPos, type, useCG6];
(s0.l AND s1.l)OR(s0.r AND s1.r)OR(s0.b AND s1.b)OR(s0.t AND s1.t)OR(s0.n AND s1.n) =>
NULL;
ENDCASE => {
cp: G3dClip.ClippedPair ¬ G3dClip.NearH[s0.quad, s1.quad]; -- let Imager do 2d clip
IF NOT cp.off THEN {
p0: Pair ¬ [vp.scale.x*cp.c0.x+vp.translate.x, vp.scale.y*cp.c0.y+vp.translate.y];
p1: Pair ¬ [vp.scale.x*cp.c1.x+vp.translate.x, vp.scale.y*cp.c1.y+vp.translate.y];
ip0: IntegerPair ¬ [Real.Round[p0.x], Real.Round[p0.y]];
ip1: IntegerPair ¬ [Real.Round[p1.x], Real.Round[p1.y]];
G3dDraw.Line2d[context, ip0, ip1, type, useCG6];
};
};
};
DrawPolygon: PROC [poly: NatSequence] ~ {
IF poly # NIL AND poly.length > 0 THEN {
stop: CARDINAL ~ poly.length-1;
FOR i: INT IN [0..stop) DO DrawLine[poly[i], poly[i+1]]; ENDLOOP;
DrawLine[poly[stop], poly[0]];
};
};
DrawTaperedPolygon: PROC [poly: NatSequence] ~ {
IF poly # NIL AND poly.length > 0 THEN {
stop: CARDINAL ~ poly.length-1;
FOR i: INT IN [0..stop) DO DrawTaperedSegment[poly[i], poly[i+1]]; ENDLOOP;
DrawTaperedSegment[poly[stop], poly[0]];
};
};
DrawPoly: PROC [p: NatSequence] ~ {
IF options.tapered
THEN DrawTaperedPolygon[p]
ELSE DrawPolygon[p];
};
DrawTaperedSegment: PROC [i0, i1: INT] ~ {
G3dDraw.TaperedSegment[context, shape.vertices[i0].point, shape.vertices[i1].point, 0.003, 0.003, x, inverse, vp];
};
DrawVec: PROC [base, vector, color: Triple] ~ {
Process.CheckForAbort[];
SetColor[color];
G3dDraw.Vector[context, base, vector, x, viewport,, vectorScale];
};
zMin, zDiv: REAL;
nowColor: Triple ¬ [1.0, 1.0, 1.0];
polys: SurfaceSequence ¬ shape.surfaces;
type: DrawType ¬ options.lineType;
mat: Matrix ¬ IF shape.matrix # NIL THEN shape.matrix ELSE G3dMatrix.Identity[];
x: Matrix ¬ G3dMatrix.Mul[shape.matrix ¬ mat, view, G3dMatrix.ObtainMatrix[]];
inverse: Matrix ¬ IF options.flatShade OR options.hiddenLineElim OR options.tapered
THEN (IF inverseView # NIL
THEN inverseView
ELSE G3dMatrix.Invert[x, G3dMatrix.ObtainMatrix[]])
ELSE NIL;
vp: Viewport ~ viewport;
useCG6: G3dDraw.UseCG6 ¬ IF Imager.GetProp[context, $CG6] # NIL THEN y ELSE n;
color: BOOL ¬ G3dShape.VertexValid[shape, color];
IF options.labelVertices OR options.labelPolygons OR options.faceNormals
THEN G3dShape.SetFaceCenters[shape];
IF options.faceNormals THEN G3dShape.SetFaceNormals[shape];
IF options.vertexNormals THEN G3dShape.SetVertexNormals[shape];
IF strokeWidth # 0.0 THEN Imager.SetStrokeWidth[context, strokeWidth];
IF options.dimFurtherLines THEN {
zDif: REAL;
IF shape.objectExtent.min = shape.objectExtent.max
THEN shape.objectExtent ¬ G3dShape.BoundingBox[shape];
zMin ¬ G3dMatrix.Transform[shape.objectExtent.min, x].z;
zDif ¬ G3dMatrix.Transform[shape.objectExtent.max, x].z-zMin;
zDiv ¬ IF zDif # 0.0 THEN 0.8/zDif ELSE 0.8;
};
IF forceTransform OR screens = NIL OR NOT screens.screensValid
THEN screens ¬ SetScreenCoords[context, shape, x, viewport, screens];
IF shape.edges = NIL THEN shape.edges ¬ G3dShape.MakeEdges[shape];
SELECT TRUE FROM
options.hiddenLineElim, options.flatShade => {
DoPoly: PROC [id: INT] ~ {
DoTriangle: PROC [i1, i2, i3: INT] ~ {
need (why?) reverse poly order so back-face removal of agrees with line-drawn
t.p3 ¬ shape.vertices[i1].point;
t.p2 ¬ shape.vertices[i2].point;
t.p1 ¬ shape.vertices[i3].point;
IF options.hiddenLineElim
THEN G3dDraw.FillTriangle[context, t, [1.0, 1.0, 1.0], x, vp]
ELSE G3dDraw.ShadeTriangle[context, t, lightVector, x, vp, f.color, f.color];
};
poly: G3dBasic.NatSequence ¬ shape.surfaces[id].vertices;
f: G3dShape.Face ¬ shape.faces[id];
t.normal ¬ f.normal;
IF poly.length = 3
THEN DoTriangle[poly[0], poly[1], poly[2]]
ELSE FOR i: INT IN [1..poly.length-2] DO
DoTriangle[poly[0], poly[i], poly[i+1]];
ENDLOOP;
IF options.hiddenLineElim THEN {
G3dDraw.SetColor[context, f.color];
DrawPolygon[poly];
};
};
t: REF G3dDraw.TriangleInfo ¬ NEW[G3dDraw.TriangleInfo];
ApplyInDepthOrder[shape, x, inverse, TRUE, DoPoly];
};
options.silhouettesOnly => {
G3dShape.SetFwdFacingFaces[shape, x];
FOR n: INT IN [0..shape.edges.length) DO
e: Edge ¬ shape.edges[n];
IF e.p0 = -1 OR e.p1 = -1 OR
shape.faces[e.p0].fwdFacing # shape.faces[e.p1].fwdFacing
THEN DrawLine[e.v0, e.v1];
ENDLOOP;
};
options.backFaces = on => {
IF NOT options.verticesOnly THEN
FOR n: INT IN [0..shape.edges.length) DO
e: Edge ¬ shape.edges[n];
Process.CheckForAbort[];
IF options.dimFurtherLines THEN {
z: REAL ¬ 0.5*(
G3dMatrix.Transform[shape.vertices[e.v0].point, x].z+
G3dMatrix.Transform[shape.vertices[e.v1].point, x].z);
Imager.SetGray[context, 0.2+zDiv*(z-zMin)];
};
IF options.tapered
THEN DrawTaperedSegment[e.v0, e.v1]
ELSE DrawLine[e.v0, e.v1];
ENDLOOP;
IF options.vertexNormals OR options.verticesOnly OR options.labelVertices THEN
FOR n: INT IN [0..shape.vertices.length) DO
v: Vertex ¬ shape.vertices[n];
Process.CheckForAbort[];
IF options.vertexNormals
THEN DrawVec[v.point, v.normal, v.color]
ELSE IF options.verticesOnly
THEN G3dDraw.Mark2d[context, screens[n].intPos, dot, useCG6];
IF options.labelVertices THEN
Draw2d.Label[context, screens[n].pos, IO.PutFR1["%g", IO.int[n]]];
ENDLOOP;
IF options.faceNormals OR options.labelPolygons THEN
FOR n: INT IN [0..shape.faces.length) DO
f: G3dShape.Face ¬ shape.faces[n];
Process.CheckForAbort[];
IF options.faceNormals THEN DrawVec[f.center, f.normal, f.color];
IF options.labelPolygons THEN
G3dDraw.Mark[context, f.center, x, viewport, IO.PutFR1["%g", IO.int[n]], none];
ENDLOOP;
};
options.backFaces = off => {
G3dShape.SetFwdFacingFaces[shape, x];
IF NOT options.tapered AND NOT options.verticesOnly THEN
FOR n: INT IN [0..shape.edges.length) DO
e: Edge ¬ shape.edges[n];
IF (e.p0 # -1 AND shape.faces[e.p0].fwdFacing) OR
(e.p1 # -1 AND shape.faces[e.p1].fwdFacing)
THEN DrawLine[e.v0, e.v1];
ENDLOOP;
IF options.vertexNormals OR options.verticesOnly OR options.faceNormals OR
options.labelVertices OR options.labelPolygons OR options.tapered THEN
FOR n: INT IN [0..shape.surfaces.length) DO
poly: NatSequence ¬ shape.surfaces[n].vertices;
IF NOT shape.faces[n].fwdFacing THEN LOOP;
IF options.vertexNormals OR options.verticesOnly OR
options.labelVertices THEN
FOR n: INT IN [0..poly.length) DO
id: INT ¬ poly[n];
v: Vertex ¬ shape.vertices[id];
IF options.vertexNormals
THEN DrawVec[v.point, v.normal, v.color]
ELSE IF options.verticesOnly THEN
Draw2d.Mark[context, screens[poly[n]].pos, dot];
IF options.labelVertices THEN
Draw2d.Label[context, screens[id].pos, IO.PutFR1["%g", IO.int[id]]];
ENDLOOP;
IF options.faceNormals OR options.labelPolygons THEN {
f: G3dShape.Face ¬ shape.faces[n];
IF options.faceNormals THEN DrawVec[f.center, f.normal, f.color];
IF options.labelPolygons THEN
G3dDraw.Mark[context, f.center, x, viewport,
IO.PutFR1["%g", IO.int[n]], none];
};
IF options.tapered THEN DrawTaperedPolygon[poly];
ENDLOOP;
};
options.backFaces = dashed => {
save: DrawType ¬ type;
G3dShape.SetFwdFacingFaces[shape, x];
FOR n: INT IN [0..shape.edges.length) DO
e: Edge ¬ shape.edges[n];
type ¬ IF (e.p0 # -1 AND shape.faces[e.p0].fwdFacing) OR
   (e.p1 # -1 AND shape.faces[e.p1].fwdFacing)
   THEN solid ELSE dashed;
Process.CheckForAbort[];
DrawLine[e.v0, e.v1];
ENDLOOP;
type ¬ save;
};
ENDCASE;
IF inverseView = NIL THEN G3dMatrix.ReleaseMatrix[inverse];
G3dMatrix.ReleaseMatrix[x];
};
RETURN[screens];
};
ApplyInDepthOrder: PUBLIC PROC [
shape:    G3dShape.Shape,
view:    Matrix,
inverseView:  Matrix ¬ NIL,
backToFront:  BOOL ¬ TRUE,
Action:   PROC [id: INT]]
~ {
Depth: TYPE ~ RECORD [z: REAL, poly: INT];
ApplyForFacing: PROC [fwdFacing: BOOL] ~ {
AddToList: PROC [p: Triple, poly: INT] ~ {
qz: REAL ¬ p.x*view[0][2]+p.y*view[1][2]+p.z*view[2][2]+view[3][2];
qw: REAL ¬ p.x*view[0][3]+p.y*view[1][3]+p.z*view[2][3]+view[3][3];
d: Depth ¬ [qz/qw, poly];
IF list = NIL
THEN list ¬ LIST[d]
ELSE IF backToFront
THEN SELECT TRUE FROM
d.z > list.first.z => list ¬ CONS[d, list];
ENDCASE => FOR l: LIST OF Depth ¬ list, l.rest WHILE l # NIL DO
IF l.rest = NIL OR d.z > l.rest.first.z THEN {
element: LIST OF Depth ¬ LIST[d];
element.rest ¬ l.rest;
l.rest ¬ element;
EXIT;
};
ENDLOOP
ELSE SELECT TRUE FROM
d.z < list.first.z => list ¬ CONS[d, list];
ENDCASE => FOR l: LIST OF Depth ¬ list, l.rest WHILE l # NIL DO
IF l.rest = NIL OR d.z < l.rest.first.z THEN {
element: LIST OF Depth ¬ LIST[d];
element.rest ¬ l.rest;
l.rest ¬ element;
EXIT;
};
ENDLOOP;
};
list: LIST OF Depth ¬ NIL;
FOR i: INT IN [0..shape.surfaces.length) DO
poly: G3dBasic.NatSequence ¬ shape.surfaces[i].vertices;
face: G3dShape.Face ¬ shape.faces[i];
IF poly.length >= 3 AND face.fwdFacing = fwdFacing
THEN AddToList[face.center, i];
ENDLOOP;
FOR l: LIST OF Depth ¬ list, l.rest WHILE l # NIL DO Action[l.first.poly]; ENDLOOP;
};
G3dShape.SetFaceCenters[shape];
The following would be useful if G3dShape.SetFwdFaces took inverseView:
IF inverseView = NIL THEN inverseView ¬ G3dMatrix.Invert[view];
G3dShape.SetFwdFacingFaces[shape, view];
IF shape.showBackfaces THEN ApplyForFacing[FALSE];
ApplyForFacing[TRUE];
};
SetScreenCoords: PUBLIC PROC [
context:   Context,
shape:    G3dShape.Shape,
view:    Matrix,
viewport:   Viewport ¬ [],
screens:   ScreenSequence ¬ NIL,
setScreenExtent: BOOL ¬ FALSE]
RETURNS [ScreenSequence]
~ {
IF shape # NIL AND shape.vertices # NIL AND view # NIL THEN {
hasPersp: BOOL ¬ G3dMatrix.HasPerspective[view];
IF screens = NIL OR screens.maxLength < shape.vertices.length
THEN screens ¬ NEW[G3dShape.ScreenSequenceRep[shape.vertices.length]];
screens.length ¬ shape.vertices.length;
IF setScreenExtent THEN screens.extent ¬ [[huge, huge], [-huge, -huge]];
FOR n: INT IN [0..shape.vertices.length) DO
screens[n] ¬ G3dMatrix.GetScreen[shape.vertices[n].point, view, hasPersp, viewport];
IF setScreenExtent THEN {
p: Pair ¬ screens[n].pos;
screens.extent.min.x ¬ MIN[p.x, screens.extent.min.x];
screens.extent.max.x ¬ MAX[p.x, screens.extent.max.x];
screens.extent.min.y ¬ MIN[p.y, screens.extent.min.y];
screens.extent.max.y ¬ MAX[p.y, screens.extent.max.y];
};
ENDLOOP;
screens.screensValid ¬ TRUE;
};
screens.extentValid ¬ setScreenExtent;
RETURN[screens];
};
END.
..
scratchBools: ARRAY [0..10) OF BoolSequence ¬ ALL[NIL];
ObtainBools: ENTRY PROC [requiredLength: INT] RETURNS [BoolSequence] ~ {
FOR i: NAT IN [0..10) DO
bools: BoolSequence ¬ scratchBools[i];
IF bools = NIL OR bools.maxLength < CARDINAL[requiredLength] THEN LOOP;
scratchBools[i] ¬ NIL;
RETURN[bools];
ENDLOOP;
RETURN[NEW[G3dBasic.BoolSequenceRep[requiredLength]]];
};
ReleaseBools: ENTRY PROC [bools: BoolSequence] ~ {
FOR i: INT IN [0..10) DO
IF scratchBools[i] = NIL THEN {scratchBools[i] ¬ bools; RETURN};
ENDLOOP;
};
ApplyInDepthOrder: PUBLIC PROC [
shape:    G3dShape.Shape,
view:    Matrix,
inverseView:  Matrix ¬ NIL,
backToFront:  BOOL ¬ TRUE,
Action:   PROC [id: INT]]
~ {
Depth: TYPE ~ RECORD [z: REAL, poly1, poly2: INT];
AddToList: PROC [p: Triple, poly1, poly2: INT] ~ {
qz: REAL ¬ p.x*view[0][2]+p.y*view[1][2]+p.z*view[2][2]+view[3][2];
qw: REAL ¬ p.x*view[0][3]+p.y*view[1][3]+p.z*view[2][3]+view[3][3];
d: Depth ¬ [qz/qw, poly1, poly2];
IF list = NIL
THEN list ¬ LIST[d]
ELSE IF backToFront
THEN SELECT TRUE FROM
d.z > list.first.z => list ¬ CONS[d, list];
ENDCASE => FOR l: LIST OF Depth ¬ list, l.rest WHILE l # NIL DO
IF l.rest = NIL OR d.z > l.rest.first.z THEN {
element: LIST OF Depth ¬ LIST[d];
element.rest ¬ l.rest;
l.rest ¬ element;
EXIT;
};
ENDLOOP
ELSE SELECT TRUE FROM
d.z < list.first.z => list ¬ CONS[d, list];
ENDCASE => FOR l: LIST OF Depth ¬ list, l.rest WHILE l # NIL DO
IF l.rest = NIL OR d.z < l.rest.first.z THEN {
element: LIST OF Depth ¬ LIST[d];
element.rest ¬ l.rest;
l.rest ¬ element;
EXIT;
};
ENDLOOP;
};
list: LIST OF Depth ¬ NIL;
sil: BoolSequence ¬ IF shape.showBackfaces THEN ObtainBools[shape.surfaces.length] ELSE NIL;
The following would be useful if G3dShape.SetFwdFaces took inverseView:
IF inverseView = NIL THEN inverseView ¬ G3dMatrix.Invert[view];
G3dShape.SetFwdFacingFaces[shape, view];
G3dShape.SetFaceCenters[shape];
IF shape.showBackfaces
THEN {
verts: G3dShape.VertexSequence ¬ shape.vertices;
IF shape.edges = NIL THEN shape.edges ¬ G3dShape.MakeEdges[shape];
FOR i: INT IN [0..shape.surfaces.length) DO sil[i] ¬ FALSE; ENDLOOP;
FOR i: INT IN [0..shape.edges.length) DO
e: Edge ¬ shape.edges[i];
IF e.p0 # -1 AND e.p1 # -1 AND
shape.faces[e.p0].fwdFacing # shape.faces[e.p1].fwdFacing THEN {
sil[e.p0] ¬ sil[e.p1] ¬ TRUE;
AddToList[G3dVector.Midpoint[verts[e.v0].point, verts[e.v1].point], e.p0, e.p1];
};
ENDLOOP;
}
ELSE IF NOT G3dShape.FaceValid[shape, normal] THEN G3dShape.SetFaceNormals[shape];
FOR i: INT IN [0..shape.surfaces.length) DO
poly: G3dBasic.NatSequence ¬ shape.surfaces[i].vertices;
face: G3dShape.Face ¬ shape.faces[i];
IF poly.length >= 3 AND
((NOT shape.showBackfaces AND face.fwdFacing) OR
(shape.showBackfaces AND NOT sil[i]))
THEN AddToList[face.center, i, -1];
ENDLOOP;
FOR l: LIST OF Depth ¬ list, l.rest WHILE l # NIL DO
IF l.first.poly2 # -1
THEN {
IF backToFront = shape.faces[l.first.poly2].fwdFacing
THEN {Action[l.first.poly1]; Action[l.first.poly2]}
ELSE {Action[l.first.poly2]; Action[l.first.poly1]};
}
ELSE Action[l.first.poly1];
ENDLOOP;
IF sil # NIL THEN ReleaseBools[sil];
};