G3dTimeTreesImpl.mesa
Copyright Ó 1988, 1992 by Xerox Corporation. All rights reserved.
Glassner, July 19, 1990 12:21:11 pm PDT
Bloomenthal, July 22, 1992 11:28 pm PDT
DIRECTORY G2dBasic, G2dSpline, G3dBasic, G3dMatrix, G3dQuaternion, G3dRender, G3dShape, G3dSpline, G3dVector, LinearSystem, Real, Rope, SVD, G3dTimeTrees;
Type Declarations
Spline1dSequence: TYPE ~ G2dSpline.Spline1dSequence;
Pair: TYPE ~ G3dBasic.Pair;
PairSequence: TYPE ~ G3dBasic.PairSequence;
RealSequence: TYPE ~ G3dBasic.RealSequence;
Matrix: TYPE ~ G3dMatrix.Matrix;
Triple: TYPE ~ G3dMatrix.Triple;
TripleSequence: TYPE ~ G3dBasic.TripleSequence;
Quaternion: TYPE ~ G3dQuaternion.Quaternion;
QuaternionSequence: TYPE ~ G3dTimeTrees.QuaternionSequence;
QuaternionSequenceRep: TYPE ~ G3dTimeTrees.QuaternionSequenceRep;
Shape: TYPE ~ G3dShape.Shape;
SplineSequence: TYPE ~ G3dSpline.SplineSequence;
ColumnN: TYPE ~ LinearSystem.ColumnN;
MatrixN: TYPE ~ LinearSystem.MatrixN;
RowN: TYPE ~ LinearSystem.RowN;
ROPE: TYPE ~ Rope.ROPE;
TTInterpolateProc: TYPE ~ G3dTimeTrees.TTInterpolateProc;
TTReportProc: TYPE ~ G3dTimeTrees.TTReportProc;
TTNodeActionProc: TYPE ~ G3dTimeTrees.TTNodeActionProc;
InterpolationType: TYPE ~ G3dTimeTrees.InterpolationType;
KeyType: TYPE ~ G3dTimeTrees.KeyType;
Key: TYPE ~ G3dTimeTrees.Key;
KeyRep: TYPE ~ G3dTimeTrees.KeyRep;
Node: TYPE ~ G3dTimeTrees.Node;
NodeRep: TYPE ~ G3dTimeTrees.NodeRep;
FlavorInfo: TYPE ~ G3dTimeTrees.FlavorInfo;
FlavorInfoRep: TYPE ~ G3dTimeTrees.FlavorInfoRep;
KeyHead: TYPE ~ G3dTimeTrees.KeyHead;
KeyHeadRep: TYPE ~ G3dTimeTrees.KeyHeadRep;
TimeTree: TYPE ~ G3dTimeTrees.TimeTree;
TimeTreeRep: TYPE ~ G3dTimeTrees.TimeTreeRep;
KeySequence: TYPE ~ G3dTimeTrees.KeySequence;
KeySequenceRep: TYPE ~ G3dTimeTrees.KeySequenceRep;
NodeSequence: TYPE ~ G3dTimeTrees.NodeSequence;
NodeSequenceRep: TYPE ~ G3dTimeTrees.NodeSequenceRep;
RrsrtpNode: TYPE ~ G3dTimeTrees.RrsrtpNode;
RrsrtpNodeRep: TYPE ~ G3dTimeTrees.RrsrtpNodeRep;
RealRef: TYPE ~ G3dTimeTrees.RealRef;
TripleRef: TYPE ~ G3dTimeTrees.TripleRef;
MatrixRef: TYPE ~ G3dTimeTrees.MatrixRef;
MatrixAccelerators: TYPE ~ G3dTimeTrees.MatrixAccelerators;
MatrixAcceleratorsRep: TYPE ~ G3dTimeTrees.MatrixAcceleratorsRep;
TripleAccelerators: TYPE ~ G3dTimeTrees.TripleAccelerators;
TripleAcceleratorsRep: TYPE ~ G3dTimeTrees.TripleAcceleratorsRep;
RealAccelerators: TYPE ~ G3dTimeTrees.RealAccelerators;
RealAcceleratorsRep: TYPE ~ G3dTimeTrees.RealAcceleratorsRep;
TransformOrder: TYPE ~ G3dTimeTrees.TransformOrder;
Error: PUBLIC SIGNAL [reason: ROPE] = CODE;
TimeTree Support
CreateTimeTree:
PUBLIC
PROC
RETURNS [timeTree: TimeTree] ~ {
timeTree ¬ NEW[TimeTreeRep ¬ []];
timeTree.activeNode ¬ timeTree.root ¬ NEW[NodeRep ¬ [name: "root"]];
timeTree.activeNode.timeTree ¬ timeTree;
};
PushTimeTree:
PUBLIC
PROC [timeTree: TimeTree] ~ {
newNode: Node ¬ NEW[NodeRep ¬ []];
newNode.parent ¬ timeTree.activeNode;
newNode.timeTree ¬ timeTree;
newNode.locked ¬ timeTree.activeNode.locked;
timeTree.activeNode.children ¬ AddToNodeSequence[timeTree.activeNode.children, newNode];
timeTree.activeNode ¬ newNode;
};
PopTimeTree:
PUBLIC
PROC [timeTree: TimeTree] ~ {
IF timeTree.activeNode.parent #
NIL
THEN timeTree.activeNode ¬ timeTree.activeNode.parent;
};
RegisterKeyType:
PUBLIC
PROC [timeTree: TimeTree, name:
ROPE, type: KeyType, report: TTReportProc ¬
NIL, interp: TTInterpolateProc ¬
NIL, interpType: InterpolationType ¬ smooth] ~ {
flavor: FlavorInfo ¬
NEW[FlavorInfoRep ¬ [
name: name, type: type, interpProc: interp, reportProc: report, interpolationType: interpType]];
timeTree.flavorList ¬ CONS[flavor, timeTree.flavorList];
};
Node Support
SetNodeObject:
PUBLIC
PROC [
timeTree: TimeTree, object:
REF ¬
NIL, clientData:
REF ¬
NIL]
RETURNS [Node] ~ {
timeTree.activeNode.object ¬ object;
timeTree.activeNode.clientData ¬ clientData;
RETURN[timeTree.activeNode];
};
NodeFromName:
PUBLIC
PROC [timeTree: TimeTree, name:
ROPE]
RETURNS [Node] ~ {
GetName:
PROC [n: Node] ~ {
IF Rope.Equal[n.name, name,
FALSE]
THEN {
result ¬ n;
RETURN;
};
FOR i:
INT
IN [0 .. n.children.length)
DO
IF result = NIL THEN GetName[n.children[i]];
ENDLOOP;
RETURN;
};
result: Node ¬ NIL;
GetName[timeTree.root];
RETURN[result];
};
RenameNode:
PUBLIC
PROC [timeTree: TimeTree, oldName, newName:
ROPE] ~ {
Rename:
PROC [n: Node] ~ {
IF Rope.Equal[n.name, oldName,
FALSE]
THEN {
n.name ¬ newName;
done ¬ TRUE;
RETURN;
};
FOR i:
INT
IN [0 .. n.children.length)
DO
IF NOT done THEN Rename[n.children[i]];
ENDLOOP;
};
done: BOOL ¬ FALSE;
Rename[timeTree.root];
};
ClearNodeObject:
PUBLIC
PROC [node:
REF
ANY] ~ {
IF node #
NIL
THEN {
n: Node ¬ NARROW[node];
n.object ¬ NIL;
};
};
PruneNode:
PUBLIC
PROC [node: Node] ~ {
parent: Node ¬ node.parent;
new: NodeSequence ¬ NIL;
FOR i:
INT
IN [0 .. parent.children.length)
DO
IF parent.children[i] # node THEN new ¬ AddToNodeSequence[new, parent.children[i]];
ENDLOOP;
node.parent ¬ NIL;
parent.children ¬ new;
};
GraftNode:
PUBLIC
PROC [orphan, newParent: Node] ~ {
IF newParent #
NIL
THEN
newParent.children ¬ AddToNodeSequence[newParent.children, orphan];
};
MoveNode:
PUBLIC
PROC [moveNode: Node, newParent: Node] ~ {
PruneNode[moveNode];
GraftNode[moveNode, newParent];
};
SetLocalTransform:
PUBLIC
PROC [tt: TimeTree, m: Matrix] ~ {
IF tt = NIL OR tt.activeNode = NIL THEN RETURN;
tt.activeNode.localTransform ¬ G3dMatrix.CopyMatrix[m];
};
GetActiveNode:
PUBLIC
PROC [tt: TimeTree]
RETURNS [
REF
ANY] ~ {
RETURN[tt.activeNode];
};
SetActiveNode:
PUBLIC
PROC [tt: TimeTree, n:
REF
ANY] ~ {
tt.activeNode ¬ NARROW[n];
};
GetRoot:
PUBLIC
PROC [node: Node]
RETURNS [root: TimeTree] ~ {
IF node # NIL THEN root ¬ node.timeTree;
};
EnumerateNodes:
PUBLIC
PROC [tt: TimeTree, action: TTNodeActionProc] ~ {
ActOn:
PROC [node: Node] ~ {
continue ¬ action[node];
IF node.children #
NIL
THEN
FOR i:
INT
IN [0 .. node.children.length)
DO
IF continue THEN continue ¬ action[node.children[i]];
ENDLOOP;
};
continue: BOOL ¬ TRUE;
ActOn[tt.root];
};
Key Support
InsertKey:
PUBLIC
PROC [
timeTree: TimeTree,
time: REAL,
data: REF,
name: ROPE ¬ NIL,
keyHead: KeyHead ¬ NIL]
~ {
key: Key ¬ NEW[KeyRep ¬ [time, data]];
IF keyHead = NIL THEN keyHead ¬ GetKeyHeadFromName[timeTree, name];
IF keyHead = NIL THEN keyHead ¬ PrependNewKeyHead[timeTree, name, data];
InsertKeyInKeyHead[key, keyHead];
};
InsertDirtyKeys:
PUBLIC
PROC [timeTree: TimeTree, time:
REAL] ~ {
InsertKeys:
PROC [node: Node] ~ {
IF node.keyHeads = NIL THEN RETURN;
FOR k:
LIST
OF KeyHead ¬ node.keyHeads, k.rest
UNTIL k=
NIL
DO
IF k.first.dirty
THEN {
key: Key ¬ NEW[KeyRep ¬ [time, k.first.strobe]];
InsertKeyInKeyHead[key, k.first];
};
ENDLOOP;
};
InsertKeys[timeTree.root];
};
MakeNewTransformKeyHead:
PROC
RETURNS [kh: KeyHead] ~ {
kh ¬ NEW[KeyHeadRep ¬ []];
kh.flavorInfo ¬ NEW[FlavorInfoRep ¬ [type: matrix]];
};
InsertTransform:
PUBLIC
PROC [timeTree: TimeTree, time:
REAL, transform: Matrix] ~ {
key: Key ¬ NEW[KeyRep ¬ [time, transform]];
IF timeTree.activeNode.transformKeyHead =
NIL
THEN {
timeTree.activeNode.transformKeyHead ¬ MakeNewTransformKeyHead[];
};
InsertKeyInKeyHead[key, timeTree.activeNode.transformKeyHead];
timeTree.activeNode.transformKeyHead.acceleratorsValid ¬ FALSE;
};
SetTransformDirty:
PUBLIC
PROC [node: Node, dirty:
BOOL ¬
TRUE] ~ {
IF node.transformKeyHead =
NIL
THEN node.transformKeyHead ¬ MakeNewTransformKeyHead[];
node.transformKeyHead.dirty ¬ dirty;
};
GetTransformDirty:
PUBLIC
PROC [node: Node]
RETURNS [
BOOL ¬
FALSE] ~ {
IF node.transformKeyHead # NIL THEN RETURN[node.transformKeyHead.dirty];
};
GetKeyHeadFromName:
PROC [timeTree: TimeTree, name:
ROPE ¬
NIL]
RETURNS [KeyHead] ~ {
an: Node ¬ timeTree.activeNode;
IF an.keyHeads = NIL THEN RETURN [NIL];
FOR k:
LIST
OF KeyHead ¬ an.keyHeads, k.rest
UNTIL k=
NIL
DO
IF Rope.Equal[name, k.first.flavorInfo.name, FALSE] THEN RETURN [k.first];
ENDLOOP;
RETURN[NIL];
};
SetKeyHeadProcs:
PUBLIC
PROC [
timeTree: TimeTree, keyName:
ROPE, interpProc: TTInterpolateProc ¬
NIL, reportProc: TTReportProc ¬
NIL] ~ {
IF timeTree.activeNode #
NIL
THEN {
kh: KeyHead ¬ GetKeyHeadFromName[timeTree, keyName];
IF kh = NIL THEN RETURN;
IF interpProc #
NIL
THEN {
kh.flavorInfo.interpProc ¬ interpProc;
kh.flavorInfo.gotInterpProc ¬ TRUE;
};
IF reportProc #
NIL
THEN {
kh.flavorInfo.reportProc ¬ reportProc;
kh.flavorInfo.gotReportProc ¬ TRUE;
};
};
};
SetKeyHeadInterpType:
PUBLIC
PROC [timeTree: TimeTree, keyName:
ROPE, interpType: InterpolationType ¬ smooth] ~ {
IF timeTree.activeNode #
NIL
THEN {
kh: KeyHead ¬ GetKeyHeadFromName[timeTree, keyName];
IF kh = NIL THEN kh ¬ PrependNewKeyHead[timeTree, keyName, NIL];
kh.flavorInfo.interpolationType ¬ interpType;
};
};
PrependNewKeyHead:
PROC [timeTree: TimeTree, name:
ROPE, data:
REF]
RETURNS [kh: KeyHead] ~ {
kh ¬ NEW[KeyHeadRep ¬ []];
kh.flavorInfo ¬ NEW[FlavorInfoRep ¬ []];
kh.flavorInfo.name ¬ name;
WITH data
SELECT
FROM
d: MatrixRef => kh.flavorInfo.type ¬ matrix;
d: TripleRef => kh.flavorInfo.type ¬ triple;
d: RealRef => kh.flavorInfo.type ¬ real;
ENDCASE => kh.flavorInfo.type ¬ unknown;
timeTree.activeNode.keyHeads ¬ CONS[kh, timeTree.activeNode.keyHeads];
};
SetTransformProcs:
PUBLIC
PROC [timeTree: TimeTree, report: TTReportProc ¬
NIL, interp: TTInterpolateProc ¬
NIL, interpType: InterpolationType ¬ smooth] ~ {
timeTree.transformFlavor ¬
NEW[FlavorInfoRep ¬
[interpProc: interp, reportProc: report, interpolationType: interpType, type: matrix]];
};
SetTransformInterpType:
PUBLIC
PROC [timeTree: TimeTree, interpType: InterpolationType ¬ smooth] ~ {
IF timeTree.activeNode #
NIL
THEN {
IF timeTree.activeNode.transformKeyHead =
NIL
THEN
timeTree.activeNode.transformKeyHead ¬ MakeNewTransformKeyHead[];
timeTree.activeNode.transformKeyHead.flavorInfo.interpolationType ¬ interpType;
};
};
InsertKeyInKeyHead:
PROC [newKey: Key, kh: KeyHead] ~ {
IF kh.keys =
NIL
THEN {
kh.keys ¬ AddToKeySequence[NIL, newKey];
RETURN;
}
ELSE {
InsertKeyAt:
PROC [p:
INT] ~ {
new: KeySequence ¬ NEW[KeySequenceRep[kh.keys.length+1]];
new.length ¬ kh.keys.length+1;
FOR i: INT IN [0 .. p) DO new[i] ¬ kh.keys[i]; ENDLOOP;
new[p] ¬ newKey;
FOR i: INT IN [p .. kh.keys.length) DO new[i+1] ¬ kh.keys[i]; ENDLOOP;
kh.keys ¬ new;
};
ReplaceKeyAt:
PROC [p:
INT] ~ {
kh.keys[p] ¬ newKey;
};
-- linear insertion sort
inserted: BOOL ¬ FALSE;
FOR i:
INT
IN [0 .. kh.keys.length)
DO
IF newKey.time <= kh.keys[i].time
AND
NOT inserted
THEN {
IF newKey.time # kh.keys[i].time
THEN InsertKeyAt[i]
ELSE ReplaceKeyAt[i];
inserted ¬ TRUE;
};
ENDLOOP;
IF NOT inserted THEN kh.keys ¬ AddToKeySequence[kh.keys, newKey];
};
};
Matrix Support Procs
RrsrtpToMatrix:
PUBLIC
PROC [rrsrtp: RrsrtpNode]
RETURNS [m: Matrix] ~ {
qm1: Matrix ¬ G3dQuaternion.MakeRotateQ[rrsrtp.q1];
qmc: Matrix ¬ G3dQuaternion.MakeRotateQ[rrsrtp.qc];
qm2: Matrix ¬ G3dQuaternion.MakeRotateQ[rrsrtp.q2];
smat: Matrix ¬ G3dMatrix.MakeScale[rrsrtp.scale];
tmat: Matrix ¬ G3dMatrix.MakeTranslate[rrsrtp.translate];
iterm: Matrix ¬ G3dMatrix.Mul[qmc, G3dMatrix.Mul[smat, qm2]];
m ¬ G3dMatrix.Mul[qm1, G3dMatrix.Mul[iterm, tmat]];
};
RrsrtpComponentsToMatrix:
PUBLIC
PROC
[q1: Quaternion, qc: Quaternion, s: Triple, q2: Quaternion, t: Triple, p: Triple]
RETURNS [Matrix] ~ {
rrsrtp: RrsrtpNode ¬ NEW[RrsrtpNodeRep ¬ [q1: q1, qc: qc, scale: s, q2: q2, translate: t, perspective: p]];
RETURN[RrsrtpToMatrix[rrsrtp]];
};
FindSVD:
PROC [m: Matrix]
RETURNS [u, w, v: Matrix] ~ {
aMat: LinearSystem.MatrixN ¬ LinearSystem.Create[3, 3];
vMat: LinearSystem.MatrixN ¬ LinearSystem.Create[3, 3];
wCol: LinearSystem.ColumnN ¬ NEW[LinearSystem.VecSeq[3]];
u ¬ G3dMatrix.Identity[];
v ¬ G3dMatrix.Identity[];
FOR i:
INT
IN [0 .. 3)
DO
FOR j:
INT
IN [0 .. 3)
DO
aMat[i][j] ¬ m[i][j];
ENDLOOP;
ENDLOOP;
SVD.SVDecomp[aMat, 3, 3, wCol, vMat];
FOR i:
INT
IN [0 .. 3)
DO
FOR j:
INT
IN [0 .. 3)
DO
u[i][j] ¬ aMat[i][j];
v[i][j] ¬ vMat[j][i]; -- use V Transpose!
ENDLOOP;
ENDLOOP;
w ¬ G3dMatrix.MakeScale[[wCol[0], wCol[1], wCol[2]]];
};
GetConformanceMatrix:
PROC [m1, m2, oldQ: Matrix, propagate:
BOOL ¬
TRUE]
RETURNS [conf: Matrix] ~ {
IF propagate
THEN {
try to get m1 as close as possible to oldQ
IF oldQ =
NIL
THEN conf ¬ G3dMatrix.Identity[]
ELSE {
c: Matrix ¬ G3dMatrix.Mul[G3dMatrix.Transpose[m1], oldQ];
u, w, v: Matrix;
[u, w, v] ¬ FindSVD[c];
conf ¬ G3dMatrix.Mul[u, v];
};
}
ELSE {
force polar decomposition
conf ¬ G3dMatrix.CopyMatrix[m2];
};
};
MatrixToRrsrtp:
PUBLIC
PROC [m: Matrix, oldQ: Matrix ¬
NIL, propagate:
BOOL ¬
TRUE]
RETURNS [rrsrtp: RrsrtpNode] ~ {
CheckConstruction:
PROC ~ {
-- debugging
smat: Matrix ¬ G3dMatrix.MakeScale[rrsrtp.scale];
tmat: Matrix ¬ G3dMatrix.MakeTranslate[rrsrtp.translate];
r1mat: Matrix ¬ G3dQuaternion.ToMatrix[rrsrtp.q1];
r2mat: Matrix ¬ G3dQuaternion.ToMatrix[rrsrtp.q2];
rcmat: Matrix ¬ G3dQuaternion.ToMatrix[rrsrtp.qc];
iterm: Matrix ¬ G3dMatrix.Mul[rcmat, G3dMatrix.Mul[smat, r2mat]];
composite: Matrix ¬ G3dMatrix.Mul[r1mat, G3dMatrix.Mul[iterm, tmat]];
CheckEqualMats[composite, rrsrtp.matrix];
};
ExtractReflections:
PROC [m: Matrix]
RETURNS [
REAL] ~ {
Sgn:
PROC [a:
REAL]
RETURNS [
REAL] ~ {
IF a < 0.0 THEN RETURN[-1.0] ELSE RETURN[1.0]; };
row1: Triple ¬ [m[0][0], m[0][1], m[0][2]];
row2: Triple ¬ [m[1][0], m[1][1], m[1][2]];
row3: Triple ¬ [m[2][0], m[2][1], m[2][2]];
b: Triple ¬ G3dVector.Cross[row1, row2];
dot: REAL ¬ G3dVector.Dot[row3, b];
RETURN[Sgn[dot]];
};
m1, m2, m3: REAL;
aMat: Matrix ¬ G3dMatrix.Identity[];
cMat: Matrix ¬ G3dMatrix.Identity[];
firstMat: Matrix ¬ G3dMatrix.Identity[];
secondMat: Matrix ¬ G3dMatrix.Identity[];
scaleMat: Matrix ¬ G3dMatrix.Identity[];
confMat: Matrix ¬ G3dMatrix.Identity[];
rrsrtp ¬ NEW[RrsrtpNodeRep ¬ []];
rrsrtp.matrix ¬ G3dMatrix.CopyMatrix[m];
rrsrtp.translate ¬ [m[3][0], m[3][1], m[3][2]];
[aMat, scaleMat, secondMat] ¬ FindSVD[m];
cMat ¬ GetConformanceMatrix[aMat, secondMat, oldQ, propagate];
firstMat ¬ G3dMatrix.Mul[aMat, cMat];
confMat ¬ G3dMatrix.Transpose[cMat];
rrsrtp.scale ¬ [scaleMat[0][0], scaleMat[1][1], scaleMat[2][2]];
m1 ¬ ExtractReflections[firstMat];
m2 ¬ ExtractReflections[secondMat];
m3 ¬ ExtractReflections[confMat];
firstMat ¬ G3dMatrix.Scale[firstMat, m1];
secondMat ¬ G3dMatrix.Scale[secondMat, m2];
confMat ¬ G3dMatrix.Scale[confMat, m3];
rrsrtp.scale ¬ G3dVector.Mul[rrsrtp.scale, m1*m2*m3];
rrsrtp.q1 ¬ G3dQuaternion.FromMatrix[firstMat];
rrsrtp.q2 ¬ G3dQuaternion.FromMatrix[secondMat];
rrsrtp.qc ¬ G3dQuaternion.FromMatrix[confMat];
rrsrtp.matrix ¬ G3dMatrix.CopyMatrix[m];
rrsrtp.perspective ¬ [0.0, 0.0, 0.0];
rrsrtp.decomposed ¬ TRUE;
CheckConstruction[]; -- debugging
};
Evaluating Trees
LookupInterpProc:
PROC [timeTree: TimeTree, name:
ROPE]
RETURNS [TTInterpolateProc] ~ {
FOR f:
LIST
OF FlavorInfo ¬ timeTree.flavorList, f.rest
UNTIL f =
NIL
DO
IF Rope.Equal[f.first.name, name, FALSE] THEN RETURN [f.first.interpProc];
ENDLOOP;
RETURN[NIL];
};
LookupReportProc:
PROC [timeTree: TimeTree, name:
ROPE]
RETURNS [TTReportProc] ~ {
FOR f:
LIST
OF FlavorInfo ¬ timeTree.flavorList, f.rest
UNTIL f =
NIL
DO
IF Rope.Equal[f.first.name, name, FALSE] THEN RETURN [f.first.reportProc];
ENDLOOP;
RETURN[NIL];
};
GetGlobalTransform: PUBLIC PROC [node: Node] RETURNS [Matrix] ~ {
GetNodeTransform: PROC [node: Node] RETURNS [Matrix] ~ {
IF node.transformKeyHead # NIL THEN RETURN[NARROW[node.transformKeyHead.strobe, Matrix]];
IF node.strobedTransform # NIL
THEN RETURN [node.strobedTransform]
ELSE RETURN[node.localTransform];
};
GetParentChain: PROC [node: Node, m: Matrix] RETURNS [Matrix] ~ {
cnode: Node ← node.parent;
composite: Matrix ← G3dMatrix.CopyMatrix[m];
WHILE cnode # NIL DO
m2: Matrix ← GetNodeTransform[cnode];
IF m2 # NIL THEN composite ← G3dMatrix.Mul[composite, m2]; -- chain down
cnode ← cnode.parent;
ENDLOOP;
RETURN[composite];
};
RETURN[GetParentChain[node, GetNodeTransform[node]]];
};
GetGlobalTransform:
PUBLIC
PROC [node: Node]
RETURNS [Matrix] ~ {
GetParentGlobal:
PROC [node: Node]
RETURNS [Matrix] ~ {
parent: Node ¬ node.parent;
IF parent = NIL THEN RETURN [G3dMatrix.Identity[]];
IF parent.globalTransform =
NIL
THEN RETURN[GetParentGlobal[parent]]
ELSE RETURN[parent.globalTransform];
};
parentGlobal: Matrix ¬ GetParentGlobal[node];
IF node.localTransform #
NIL
THEN RETURN[G3dMatrix.Mul[node.localTransform, parentGlobal]]
ELSE RETURN[parentGlobal];
};
PropagateStrobedMatrices:
PROC [node: Node] ~ {
PropagateNode:
PROC [node: Node] ~ {
IF node = NIL THEN RETURN;
node.globalTransform ¬ GetGlobalTransform[node];
IF node.children #
NIL
THEN
FOR i:
INT
IN [0 .. node.children.length)
DO
PropagateNode[node.children[i]];
ENDLOOP;
};
PropagateNode[node];
};
StrobeMatrix:
PROC [kh: KeyHead, segment:
CARDINAL, alpha:
REAL]
RETURNS [Matrix] ~ {
ma: MatrixAccelerators ¬ NARROW[kh.accelerators];
t, s, p: Triple ¬ [];
q1, q2, qc: Quaternion ¬ [];
sm1: CARDINAL ¬ MAX[0, segment-1];
sm0: CARDINAL ¬ segment;
sp0: CARDINAL ¬ MIN[segment+1, kh.keys.length-1];
sp1: CARDINAL ¬ MIN[segment+2, kh.keys.length-1];
rrsrtpm1: RrsrtpNode ¬ NARROW[kh.keys[sm1].data];
rrsrtpm0: RrsrtpNode ¬ NARROW[kh.keys[sm0].data];
rrsrtpp0: RrsrtpNode ¬ NARROW[kh.keys[sp0].data];
rrsrtpp1: RrsrtpNode ¬ NARROW[kh.keys[sp1].data];
SELECT kh.flavorInfo.interpolationType
FROM
constant => {
t ¬ rrsrtpm0.translate;
s ¬ rrsrtpm0.scale;
q1 ¬ ma.q1[sm0];
q2 ¬ ma.q2[sm0];
qc ¬ ma.qc[sm0];
};
linear => {
t ¬ G3dVector.Interp[alpha, rrsrtpm0.translate, rrsrtpp0.translate];
s ¬ G3dVector.Interp[alpha, rrsrtpm0.scale, rrsrtpp0.scale];
q1 ¬ G3dQuaternion.Slerp[ma.q1[sm0], ma.q1[sp0], alpha];
q2 ¬ G3dQuaternion.Slerp[ma.q2[sm0], ma.q2[sp0], alpha];
qc ¬ G3dQuaternion.Slerp[ma.qc[sm0], ma.qc[sp0], alpha];
};
smooth => {
t ¬ G3dSpline.Position[ma.transSpline[segment], alpha];
s ¬ G3dSpline.Position[ma.scaleSpline[segment], alpha];
q1 ¬ G3dQuaternion.CatmullRom[ma.q1[sm1], ma.q1[sm0], ma.q1[sp0], ma.q1[sp1], alpha];
q2 ¬ G3dQuaternion.CatmullRom[ma.q2[sm1], ma.q2[sm0], ma.q2[sp0], ma.q2[sp1], alpha];
qc ¬ G3dQuaternion.CatmullRom[ma.qc[sm1], ma.qc[sm0], ma.qc[sp0], ma.qc[sp1], alpha];
};
ENDCASE => Error["illegal interpolation type"];
RETURN[RrsrtpComponentsToMatrix[q1, qc, s, q2, t, p]];
};
StrobeTriple:
PROC [kh: KeyHead, segment:
INT, alpha:
REAL]
RETURNS [
REF Triple] ~ {
t: Triple;
lo: Triple ¬ NARROW[kh.keys[segment].data, TripleRef];
SELECT kh.flavorInfo.interpolationType
FROM
constant => t ¬ lo;
linear => {
segp1: INT ¬ (segment+1) MOD kh.keys.length;
hi: Triple ¬ NARROW[kh.keys[segp1].data, TripleRef];
t ¬ G3dVector.Interp[alpha, lo, hi];
};
smooth => {
ta: TripleAccelerators ¬ NARROW[kh.accelerators];
t ¬ G3dSpline.Position[ta.tSpline[segment], alpha];
};
ENDCASE => Error["illegal interpolation type"];
RETURN[NEW[Triple ¬ t]];
};
StrobeReal:
PROC [kh: KeyHead, segment:
INT, alpha:
REAL]
RETURNS [
REF
REAL] ~ {
r: REAL;
SELECT kh.flavorInfo.interpolationType
FROM
constant => {
r ¬ NARROW[kh.keys[segment].data, RealRef];
};
linear => {
segp1: INT ¬ (segment+1) MOD kh.keys.length;
oldr: REAL ¬ NARROW[kh.keys[segment].data, RealRef];
newr: REAL ¬ NARROW[kh.keys[segp1].data, RealRef];
r ¬ oldr + (alpha * (newr - oldr));
};
smooth => {
ra: RealAccelerators ¬ NARROW[kh.accelerators];
r ¬ G2dSpline.Position1d[ra.vSpline[segment], alpha];
};
ENDCASE => Error["illegal interpolation type"];
RETURN[NEW[REAL ¬ r]];
};
StrobeNode:
PROC [timeTree: TimeTree, node: Node, time:
REAL] ~ {
GetSegment:
PROC [kh: KeyHead, time:
REAL]
RETURNS [
INT] ~ {
IF kh = NIL OR kh.keys = NIL THEN RETURN [0];
FOR i:
INT
IN [0 .. kh.keys.length)
DO
IF time < kh.keys[i].time THEN RETURN[IF i > 0 THEN i-1 ELSE 0];
ENDLOOP;
RETURN[IF kh.keys.length > 1 THEN kh.keys.length-2 ELSE 0];
};
GetAlpha:
PROC [kh: KeyHead, segment:
CARDINAL, time:
REAL]
RETURNS [
REAL] ~ {
leftTime: REAL ¬ kh.keys[segment].time;
rightTime: REAL ¬ kh.keys[MIN[segment+1, kh.keys.length-1]].time;
dTime: REAL ¬ rightTime - leftTime;
IF dTime = 0.0 THEN dTime ¬ 1.0;
RETURN [(time - leftTime)/dTime];
};
FOR kh:
LIST
OF KeyHead ¬ node.keyHeads, kh.rest
UNTIL kh=
NIL
DO
IF kh.first.keys #
NIL
AND kh.first.keys.length > 0
THEN {
ckh: KeyHead ¬ kh.first;
segment: INT;
alpha: REAL;
IF NOT kh.first.acceleratorsValid THEN ComputeKeyHeadAccelerators[kh.first];
segment ¬ GetSegment[kh.first, time];
alpha ¬ GetAlpha[kh.first, segment, time];
SELECT kh.first.flavorInfo.type
FROM
matrix => kh.first.strobe ¬ StrobeMatrix[kh.first, segment, alpha];
triple => kh.first.strobe ¬ StrobeTriple[kh.first, segment, alpha];
real => kh.first.strobe ¬ StrobeReal[kh.first, segment, alpha];
ENDCASE;
IF ckh.flavorInfo.gotInterpProc =
FALSE
THEN
ckh.flavorInfo.interpProc ¬ LookupInterpProc[timeTree, ckh.flavorInfo.name];
IF ckh.flavorInfo.interpProc #
NIL
THEN
ckh.strobe ¬ kh.first.flavorInfo.interpProc[time, ckh.keys, node.object, node.clientData];
IF ckh.flavorInfo.gotReportProc = FALSE THEN -- now in ReportNodes
ckh.flavorInfo.reportProc ← LookupReportProc[timeTree, ckh.flavorInfo.name];
IF ckh.flavorInfo.reportProc # NIL THEN {
ckh.flavorInfo.reportProc[ckh.strobe, node.object, node.clientData];
};
};
ENDLOOP;
-- and the distinguished transform
IF node.transformKeyHead #
NIL
THEN {
kh: KeyHead ¬ node.transformKeyHead;
segment: INT;
alpha: REAL;
IF kh.keys #
NIL
THEN {
IF NOT kh.acceleratorsValid THEN ComputeKeyHeadAccelerators[kh];
segment ¬ GetSegment[kh, time];
alpha ¬ GetAlpha[kh, segment, time];
kh.strobe ¬ StrobeMatrix[kh, segment, alpha];
node.localTransform ¬ NARROW[kh.strobe, Matrix];
};
};
};
ReportNodes:
PROC [timeTree: TimeTree] ~ {
ReportKeyHeads:
PROC [node: Node] ~ {
FOR kh:
LIST
OF KeyHead ¬ node.keyHeads, kh.rest
UNTIL kh=
NIL
DO
IF kh.first.flavorInfo.gotReportProc =
FALSE
THEN
kh.first.flavorInfo.reportProc ¬ LookupReportProc[timeTree, kh.first.flavorInfo.name];
IF kh.first.flavorInfo.reportProc #
NIL
THEN {
kh.first.flavorInfo.reportProc[kh.first.strobe, node.object, node.clientData];
};
ENDLOOP;
IF timeTree.transformFlavor.reportProc #
NIL
THEN {
timeTree.transformFlavor.reportProc[node.globalTransform, node.object, node.clientData];
};
};
ReportNode:
PROC [node: Node] ~ {
ReportKeyHeads[node];
IF node.children #
NIL
THEN
FOR i:
INT
IN [0 .. node.children.length)
DO
ReportNode[node.children[i]];
ENDLOOP;
};
ReportNode[timeTree.root];
};
StrobeTimeTree:
PUBLIC
PROC [timeTree: TimeTree, time:
REAL] ~ {
EvaluateNode:
PROC [node: Node] ~ {
IF node = NIL THEN RETURN;
StrobeNode[timeTree, node, time];
IF node.children #
NIL
THEN
FOR i:
INT
IN [0 .. node.children.length)
DO
EvaluateNode[node.children[i]];
ENDLOOP;
};
IF timeTree = NIL THEN RETURN;
EvaluateNode[timeTree.root];
ReportTimeTree[timeTree]; -- picked up by the eventual draw or render command
};
ReportTimeTree:
PUBLIC
PROC [timeTree: TimeTree] ~ {
IF timeTree = NIL THEN RETURN;
PropagateStrobedMatrices[timeTree.root];
ReportNodes[timeTree];
};
Sequence Support
AddToNodeSequence:
PROC
[nodes: NodeSequence, node: Node]
RETURNS [NodeSequence] ~ {
IF nodes = NIL THEN nodes ¬ NEW[NodeSequenceRep[1]];
IF nodes.length = nodes.maxLength THEN nodes ¬ LengthenNodes[nodes];
nodes[nodes.length] ¬ node;
nodes.length ¬ nodes.length+1;
RETURN[nodes];
};
LengthenNodes:
PROC
[nodes: NodeSequence, amount:
REAL ¬ 1.3]
RETURNS [new: NodeSequence] ~ {
newLength: NAT ¬ MAX[Real.Ceiling[amount*nodes.maxLength], 3];
new ¬ NEW[NodeSequenceRep[newLength]];
FOR i: NAT IN [0..nodes.length) DO new[i] ¬ nodes[i]; ENDLOOP;
new.length ¬ nodes.length;
};
AddToKeySequence:
PROC
[keys: KeySequence, key: Key]
RETURNS [KeySequence] ~ {
IF keys = NIL THEN keys ¬ NEW[KeySequenceRep[1]];
IF keys.length = keys.maxLength THEN keys ¬ LengthenKeys[keys];
keys[keys.length] ¬ key;
keys.length ¬ keys.length+1;
RETURN[keys];
};
LengthenKeys:
PROC
[keys: KeySequence, amount:
REAL ¬ 1.3]
RETURNS [new: KeySequence] ~ {
newLength: NAT ¬ MAX[Real.Ceiling[amount*keys.maxLength], 3];
new ¬ NEW[KeySequenceRep[newLength]];
FOR i: NAT IN [0..keys.length) DO new[i] ¬ keys[i]; ENDLOOP;
new.length ¬ keys.length;
};
Report Procs
ReportDiffuse:
PUBLIC TTReportProc ~ {
diffuse: REF REAL ¬ NARROW[strobe];
shape: Shape ¬ NARROW[object];
IF shape # NIL THEN G3dRender.SetDiffuse[shape, diffuse];
};
ReportSpecular:
PUBLIC TTReportProc ~ {
specular: REF REAL ¬ NARROW[strobe];
shape: Shape ¬ NARROW[object];
IF shape # NIL THEN G3dRender.SetSpecular[shape, specular];
};
ReportMetallicity:
PUBLIC TTReportProc ~ {
metallicity: REF REAL ¬ NARROW[strobe];
shape: Shape ¬ NARROW[object];
IF shape # NIL THEN G3dRender.SetMetallicity[shape, metallicity];
};
ReportShininess:
PUBLIC TTReportProc ~ {
shininess: REF REAL ¬ NARROW[strobe];
shape: Shape ¬ NARROW[object];
IF shape # NIL THEN G3dRender.SetShininess[shape, shininess];
};
ReportTransmittance:
PUBLIC TTReportProc ~ {
transmittance: REF REAL ¬ NARROW[strobe];
shape: Shape ¬ NARROW[object];
IF shape # NIL THEN G3dRender.SetTransmittance[shape, transmittance];
};
ReportColor:
PUBLIC TTReportProc ~ {
color: REF Triple ¬ NARROW[strobe];
shape: Shape ¬ NARROW[object];
IF shape # NIL THEN G3dRender.SetColor[shape, [color.x, color.y, color.z]];
};
ReportTransform:
PUBLIC TTReportProc ~ {
transform: Matrix ¬ NARROW[strobe];
shape: Shape ¬ NARROW[object];
IF shape # NIL THEN shape.matrix ← G3dMatrix.Mul[shape.matrix, transform];
IF shape #
NIL
THEN {
IF transform #
NIL
THEN shape.matrix ¬ G3dMatrix.CopyMatrix[transform]
ELSE shape.matrix ¬ G3dMatrix.Identity[];
};
};
Assignment Procs
AssignDiffuse:
PUBLIC
PROC [tt: TimeTree, time:
REAL, diffuse:
REAL] ~ {
localDiffuse: REF REAL ¬ NEW[REAL ¬ diffuse];
[] ¬ InsertKey[tt, time, localDiffuse, diffuseName];
};
AssignSpecular:
PUBLIC
PROC [tt: TimeTree, time:
REAL, specular:
REAL] ~ {
localSpecular: REF REAL ¬ NEW[REAL ¬ specular];
[] ¬ InsertKey[tt, time, localSpecular, specularName];
};
AssignMetallicity:
PUBLIC
PROC [tt: TimeTree, time:
REAL, metallicity:
REAL] ~ {
localMetallicity: REF REAL ¬ NEW[REAL ¬ metallicity];
[] ¬ InsertKey[tt, time, localMetallicity, metallicityName];
};
AssignShininess:
PUBLIC
PROC [tt: TimeTree, time:
REAL, shininess:
REAL] ~ {
localShininess: REF REAL ¬ NEW[REAL ¬ shininess];
[] ¬ InsertKey[tt, time, localShininess, shininessName];
};
AssignTransmittance:
PUBLIC
PROC [tt: TimeTree, time:
REAL, transmittance:
REAL] ~ {
localTransmittance: REF REAL ¬ NEW[REAL ¬ transmittance];
[] ¬ InsertKey[tt, time, localTransmittance, transmittanceName];
};
AssignColor:
PUBLIC
PROC [tt: TimeTree, time:
REAL, color: Triple] ~ {
localColor: REF Triple ¬ NEW[Triple ¬ color];
[] ¬ InsertKey[tt, time, localColor, colorName];
};
AssignTransform:
PUBLIC
PROC [tt: TimeTree, time:
REAL, transform: Matrix] ~ {
localTransform: Matrix ¬ G3dMatrix.CopyMatrix[transform];
[] ¬ InsertTransform[tt, time, localTransform];
};
Interpolation Procs
SetDiffuseInterpolationType:
PUBLIC
PROC [tt: TimeTree, type: InterpolationType] ~ {
SetKeyHeadInterpType[tt, diffuseName, type];
};
SetSpecularInterpolationType:
PUBLIC
PROC [tt: TimeTree, type: InterpolationType] ~ {
SetKeyHeadInterpType[tt, specularName, type];
};
SetMetallicityInterpolationType:
PUBLIC
PROC [tt: TimeTree, type: InterpolationType] ~ {
SetKeyHeadInterpType[tt, metallicityName, type];
};
SetShininessInterpolationType:
PUBLIC
PROC [tt: TimeTree, type: InterpolationType] ~ {
SetKeyHeadInterpType[tt, shininessName, type];
};
SetTransmittanceInterpolationType:
PUBLIC
PROC [tt: TimeTree, type: InterpolationType] ~ {
SetKeyHeadInterpType[tt, transmittanceName, type];
};
SetColorInterpolationType:
PUBLIC
PROC [tt: TimeTree, type: InterpolationType] ~ {
SetKeyHeadInterpType[tt, colorName, type];
};
SetTransformInterpolationType:
PUBLIC
PROC [tt: TimeTree, type: InterpolationType] ~ {
SetTransformInterpType[tt, type];
};