G3dViewImpl.mesa
Copyright Ó 1988, 1992 by Xerox Corporation. All rights reserved.
Bloomenthal, April 6, 1993 11:30 pm PDT
Ken Shoemake, August 30, 1989 8:21:04 pm PDT
DIRECTORY G3dBasic, G3dMatrix, G3dVector, G3dView, Imager, ImagerBackdoor, Rope, ViewerClasses;
G3dViewImpl: CEDAR PROGRAM
IMPORTS G3dMatrix, G3dVector, Imager, ImagerBackdoor
EXPORTS G3dView
~ BEGIN
Errors, Types, and Constants
Error:     PUBLIC SIGNAL [code: ATOM, reason: Rope.ROPE] ~ CODE;
Pair:     TYPE ~ G3dBasic.Pair;
PairSequence:  TYPE ~ G3dBasic.PairSequence;
Quad:    TYPE ~ G3dBasic.Quad;
Triple:    TYPE ~ G3dBasic.Triple;
TripleSequence: TYPE ~ G3dBasic.TripleSequence;
Matrix:    TYPE ~ G3dMatrix.Matrix;
Viewport:   TYPE ~ G3dMatrix.Viewport;
PairClip:   TYPE ~ G3dView.PairClip;
Viewer:    TYPE ~ ViewerClasses.Viewer;
origin:    Triple ~ G3dBasic.origin;
xAxis:    Triple ~ G3dBasic.xAxis;
yAxis:    Triple ~ G3dBasic.yAxis;
zAxis:    Triple ~ G3dBasic.zAxis;
GetViewport: PUBLIC PROC [viewer: Viewer ¬ NIL, context: Imager.Context ¬ NIL]
RETURNS [v: Viewport]
~ {
size: Pair;
SELECT TRUE FROM
viewer # NIL => size ¬ [viewer.cw, viewer.ch];
viewer # NIL => size ¬ [viewer.ww-viewer.wx, viewer.wh-viewer.wy];
context # NIL => {
b: Imager.Rectangle ¬ [0.0, 0.0, 8.5*72.0, 11.0*72.0];
b ¬ ImagerBackdoor.GetBounds[context ! Imager.Error => CONTINUE];
size ¬ [b.w, b.h];
};
ENDCASE => RETURN[[]];
v.scale ¬ v.translate ¬ [0.5*size.x, 0.5*size.y];
IF v.scale.x # 0.0 THEN v.aspectRecip ¬ v.scale.y/v.scale.x;
};
Transformation
TransformAndClipInZ: PUBLIC PROC [point: Triple, view: Matrix, viewport: Viewport ¬ []]
RETURNS [pc: PairClip]
~ {
IF G3dMatrix.HasPerspective[view]
THEN {
q: Quad ¬ G3dMatrix.TransformH[point, view];
pc ¬ IF q.w = 0.0 OR q.z+q.w < 0.0
THEN [G3dMatrix.TransformByViewport[[0.0, 0.0], viewport], TRUE]
ELSE [G3dMatrix.TransformByViewport[[q.x/q.w, q.y/q.w], viewport], FALSE];
}
ELSE pc ¬ [
G3dMatrix.TransformByViewport[G3dMatrix.TransformD[point, view], viewport],
FALSE];
};
PairsFromTriples: PUBLIC PROC [
triples: TripleSequence,
view: Matrix,
pairs: PairSequence ¬ NIL]
RETURNS [PairSequence]
~ {
IF triples = NIL THEN RETURN[NIL];
IF pairs = NIL OR pairs.maxLength < triples.length THEN
pairs ¬ NEW[G3dBasic.PairSequenceRep[triples.length]];
FOR n: NAT IN [0..pairs.length ¬ triples.length) DO
pairs[n] ¬ G3dMatrix.TransformD[triples[n], view];
ENDLOOP;
RETURN[pairs];
};
Transformation Procedures
TransformByViewport: PUBLIC PROC [viewer: Viewer, in: Matrix, out: Matrix ¬ NIL]
RETURNS [Matrix]
~ {
vp: Viewport ¬ GetViewport[viewer];
Translate and scale to viewport; don't scale z so can compare z with w for far clipping:
out ¬ G3dMatrix.DiffScale[in, [vp.scale.x, vp.scale.y, 1.0], out];
out ¬ G3dMatrix.Translate[out, [vp.translate.x, vp.translate.y, 0.0], out];
RETURN[out];
};
MakeCameraMatrix: PUBLIC PROC [
worIncr:   Matrix ¬ NIL,
scale:    REAL ¬ 1.0,
move:    Triple ¬ [0.0, 0.0, 0.0],
rotate:    Triple ¬ [0.0, 0.0, 0.0],
eyeIncr:   Matrix ¬ NIL,
fieldOfView:  REAL ¬ 0.0,
swapYZ:   BOOL ¬ TRUE,
out:    Matrix ¬ NIL]
RETURNS [Matrix]
~ {
The canonical transformation sequence for cameras is:
(world) WiSTRXEi (eye) P (view) V (screen)
where S=scale, T=translate, R=RxRyRz=rotate around x, y, and z axes, X=swap y and z, P=perspective, V=view transform (a scale and translate), and the coordinate systems in parentheses are labels, not terms in the matrix product.
World space is right-handed, generally having x=right, y=away, z=up,
Eye and view spaces are left-handed with x=right, y=up, z=away.
(Screen space is typically x=right, y=up, z=away, but that's not really our concern here.)
View and Screen space are typically perspective spaces, but the others are orthogonal.
See the G3dControl Camera Model notes by Paul Heckbert.
out ¬ G3dMatrix.Identity[out];
At this point we're in WORLD SPACE
IF worIncr # NIL THEN [] ¬ G3dMatrix.Mul[out, worIncr, out];
Previously:
[] ← G3dMatrix.Scale[out, scale, out];
[] ← G3dMatrix.Translate[out, move, out];
[] ← G3dMatrix.Rotate[out, xAxis, rotate.x,,, out];
[] ← G3dMatrix.Rotate[out, yAxis, rotate.y,,, out];
[] ← G3dMatrix.Rotate[out, zAxis, rotate.z,,, out];
Try order used in G3dArcBallImpl:
[] ¬ G3dMatrix.Rotate[out, xAxis, rotate.x,,, out];
[] ¬ G3dMatrix.Rotate[out, yAxis, rotate.y,,, out];
[] ¬ G3dMatrix.Rotate[out, zAxis, rotate.z,,, out];
qAbs: Quaternion←G3dQuaternion.FromXYZAngles[rotate.x*deg,rotate.y*deg,rotate.z*deg];
deg: REAL ~ 3.1415926535/180.0;
out ← G3dQuaternion.ToMatrix[qAbs, out];
[] ¬ G3dMatrix.Scale[out, scale, out];
[] ¬ G3dMatrix.Translate[out, move, out];
IF swapYZ THEN
FOR n: NAT IN [0..4) DO
Go from right-handed world space to left-handed view space (swap y and z):
temp: REAL ¬ out[n][1];
out[n][1] ¬ out[n][2];
out[n][2] ¬ temp;
ENDLOOP;
At this point we're in EYE SPACE
IF eyeIncr # NIL THEN [] ¬ G3dMatrix.Mul[out, eyeIncr, out];
IF fieldOfView # 0.0 THEN {
t: Matrix ¬ G3dMatrix.ObtainMatrix[];
[] ¬ G3dMatrix.Mul[out, G3dMatrix.MakePerspective[--1e4-- 10., 0., fieldOfView, t], out];
G3dMatrix.ReleaseMatrix[t];
};
At this point we're in VIEW SPACE
RETURN[out];
};
PrintMatrix: PROC [name: Rope.ROPE, matrix: Matrix] ~ {
IF name # NIL THEN TerminalIO.PutF["%g\n", IO.rope[name]];
FOR i: NAT IN [0..3] DO
TerminalIO.PutF["%6.3f\t%6.3f\t%6.3f\t%6.3f\n",
IO.real[matrix[i][0]], IO.real[matrix[i][1]], IO.real[matrix[i][2]], IO.real[matrix[i][3]]];
ENDLOOP;
TerminalIO.PutF["\n"];
};
WorldToViewFromVectors: PUBLIC PROC [
eyePoint:  Triple ¬ origin,
lookAt:  Triple ¬ zAxis,
upDirection: Triple ¬ yAxis,
fieldOfView: REAL ¬ 40.0,
out:   Matrix ¬ NIL]
RETURNS [Matrix]
~ {
in: Triple ¬ G3dVector.Unit[G3dVector.Sub[lookAt, eyePoint]];
right: Triple ¬ G3dVector.Unit[G3dVector.Cross[in, G3dVector.Unit[upDirection]]];
up: Triple ¬ G3dVector.Unit[G3dVector.Cross[right, in]];
IF G3dVector.Null[right] THEN {right ¬ [1.0, 0.0, 0.0]; up ¬ [0.0, -in.z, in.y];};
out ¬ G3dMatrix.MakeFromTriad[right, up, in, eyePoint, FALSE, out];
IF fieldOfView # 0.0 THEN {
t: Matrix ¬ G3dMatrix.ObtainMatrix[];
out ¬ G3dMatrix.Mul[out, G3dMatrix.MakePerspective[--1e4-- 10., 0, fieldOfView, t], out];
G3dMatrix.ReleaseMatrix[t];
};
RETURN[out];
};
FromScaleMovesRots: PUBLIC PROC [scale: REAL, moves, rotates: Triple]
RETURNS [eyePoint, lookAt, upDirection: Triple]
~ {
m: Matrix ¬ G3dMatrix.ObtainMatrix[];
Formerly: right handed: x-axis to right, y into screen, z up;
Presently: right handed: x-axis to right, y up, z out of screen.
worldToEye: Matrix ¬ MakeCameraMatrix[, scale, moves, rotates,, 0.0, FALSE, m]; -- no persp
eyeToWorld: Matrix ¬ G3dMatrix.Invert[worldToEye, m];
upDirection ¬ G3dMatrix.TransformVec[[0., 1., 0.], eyeToWorld]; -- no differential scaling
eyePoint ¬ G3dMatrix.Transform[[0., 0., 0.], eyeToWorld];
lookAt ¬ G3dVector.Sub[G3dMatrix.Transform[[0., 0., 1.], eyeToWorld], eyePoint];
G3dMatrix.ReleaseMatrix[m];
};
FromEyeLookUp: PUBLIC PROC [eyePoint, lookAt, upDirection: Triple, fieldOfView: REAL]
RETURNS [moves, rotates: Triple]
~ {
m: Matrix ¬ G3dMatrix.ObtainMatrix[];
worldToEye: Matrix ¬ G3dMatrix.Transpose[
WorldToViewFromVectors[eyePoint, G3dVector.Add[eyePoint, lookAt], upDirection, 0.0, m], m];
FOR n: NAT IN [0..4) DO
Go from left-handed view space to right-handed world space (swap y and z):
temp: REAL ¬ worldToEye[n][1];
worldToEye[n][1] ¬ worldToEye[n][2];
worldToEye[n][2] ¬ temp;
ENDLOOP;
rotates ¬ G3dMatrix.ExtractRotate[worldToEye];
moves ¬ G3dVector.Negate[eyePoint];
G3dMatrix.ReleaseMatrix[m];
};
END..
Discarded code:
WorldToViewFromVectors: PUBLIC PROC [
eyePoint:  Triple ¬ origin,
lookAt:  Triple ¬ zAxis,
upDirection: Triple ¬ yAxis,
fieldOfView: REAL ¬ 40.0,
out:   Matrix ¬ NIL]
RETURNS [Matrix]
~ {
Should we exchange y and z axes here (to go from right to left-handedness)?
out ¬ G3dMatrix.Identity[out];
out ¬ G3dMatrix.Translate[out, G3dVector.Negate[eyePoint], out];
IF lookAt.x = 0.0 AND lookAt.z = 0.0
THEN out ¬ G3dMatrix.Rotate[out, xAxis, 90.0,,, out]
ELSE {
Alternatively, use G3dMatrix.MakeFromTriad:
transformedY: Triple;
phi, theta, gamma: REAL ¬ 0.0;
xz: Triple ¬ G3dVector.Unit[[lookAt.x, 0.0, lookAt.z]];
lookAt ¬ G3dVector.Unit[lookAt];
upDirection ¬ G3dVector.Unit[upDirection];
phi ¬ G2dBasic.ArcCos[xz.z];
theta ¬ G2dBasic.ArcCos[G3dVector.Dot[xz, lookAt]];
out ¬ G3dMatrix.Rotate[out, xAxis, phi, FALSE,, out];
out ¬ G3dMatrix.Rotate[out, yAxis, theta, FALSE,, out];
transformedY ¬ G3dMatrix.TransformVec[yAxis, out];
gamma ¬ G2dBasic.ArcCos[G3dVector.Dot[upDirection, transformedY]];
out ¬ G3dMatrix.Rotate[out, zAxis, gamma,,, out];
};
out ¬ G3dMatrix.Translate[out, eyePoint, out];
IF fieldOfView # 0.0 THEN {
t: Matrix ¬ G3dMatrix.ObtainMatrix[];
out ¬ G3dMatrix.Mul[out, G3dMatrix.MakePerspective[--1e4-- 10., 0, fieldOfView, t], out];
G3dMatrix.ReleaseMatrix[t];
};
RETURN[out];
};
FromEyeLookUp: PUBLIC PROC [eyePoint, lookAt, upDirection: Triple, fieldOfView: REAL]
RETURNS [moves, rotates: Triple]
~ {
m: Matrix ¬ G3dMatrix.ObtainMatrix[];
eyeToWorld: Matrix ¬
WorldToViewFromVectors[eyePoint, lookAt, upDirection, fieldOfView, m];
temp: Triple ¬ rotates ¬ G3dMatrix.ExtractRotate[eyeToWorld];
worldToEye: Matrix ¬ G3dMatrix.Invert[eyeToWorld, m];
moves ¬ G3dMatrix.Transform[eyePoint, worldToEye];
G3dMatrix.ReleaseMatrix[m];
};