G3dToolSceneImpl.mesa
Copyright Ó 1985, 1992 by Xerox Corporation. All rights reserved.
Bloomenthal, October 20, 1992 4:32 pm PDT
Heckbert, August 9, 1988 5:51:51 pm PDT
Glassner, July 19, 1990 12:21:59 pm PDT
Shoemake, November 23, 1989 3:43:56 am PST
Wyvill, September 19, 1989 10:34:42 am PDT
Crow, September 21, 1989 5:23:04 pm PDT
3d Tool: Scene Definition and Animation
DIRECTORY CedarProcess, Controls, Draw2d, FileNames, G3dBasic, G3dMatrix, G3dRender, G3dScene, G3dShape, G3dTimeTrees, G3dTool, ImagerSample, IO, MessageWindow, Process, Real, Rope, TiogaMenuOps, TiogaOps, ViewerOps, ViewerTools;
G3dToolSceneImpl: CEDAR PROGRAM
IMPORTS CedarProcess, Controls, Draw2d, FileNames, G3dMatrix, G3dScene, G3dTimeTrees, G3dTool, ImagerSample, IO, MessageWindow, Process, Real, Rope, TiogaMenuOps, TiogaOps, ViewerOps, ViewerTools
EXPORTS G3dTool
~ BEGIN
Imported Types
ROPE:      TYPE ~ Rope.ROPE;
ForkableProc:   TYPE ~ CedarProcess.ForkableProc;
ClickProc:    TYPE ~ Controls.ClickProc;
Viewer:     TYPE ~ Controls.Viewer;
Triple:     TYPE ~ G3dBasic.Triple;
RealSequence:   TYPE ~ G3dBasic.RealSequence;
Matrix:     TYPE ~ G3dMatrix.Matrix;
SourceText:    TYPE ~ G3dScene.SourceText;
Animation:    TYPE ~ G3dTool.Animation;
Animations:    TYPE ~ G3dTool.Animations;
Tool:      TYPE ~ G3dTool.Tool;
Shape:     TYPE ~ G3dShape.Shape;
ShapeProc:    TYPE ~ G3dShape.ShapeProc;
TimeTree:    TYPE ~ G3dTimeTrees.TimeTree;
TTNode:     TYPE ~ G3dTimeTrees.Node;
SampleMap:    TYPE ~ ImagerSample.SampleMap;
File Referencing
ShowFile: PROC [t: Tool] ~ {
FindViewer: PROC [name: ROPE] RETURNS [viewer: Viewer] = {
MatchName: PROC [v: Viewer] RETURNS [continue: BOOL ¬ TRUE] ~ {
continue ¬ NOT Rope.Equal[name, FileNames.ConvertToSlashFormat[v.name], FALSE];
IF continue THEN viewer ¬ v;
};
name ¬ FileNames.ConvertToSlashFormat[name];
ViewerOps.EnumerateViewers[MatchName];
};
node: TTNode ¬ NARROW[G3dTimeTrees.GetActiveNode[t.timeTree]];
st: SourceText ¬ NARROW[node.clientData];
viewer: Viewer ¬ FindViewer[st.fileName];
selection: ViewerTools.SelPos ¬ NEW[ViewerTools.SelPosRec ¬ [start: st.position, length: 1]];
IF viewer = NIL THEN {
viewer ¬ ViewerOps.CreateViewer[
flavor: $Text, paint: FALSE, info: [iconic: TRUE, column: right, openHeight: 140]];
TiogaMenuOps.Load[viewer: viewer, fileName: st.fileName];
};
ViewerOps.SetOpenHeight[viewer: viewer, clientHeight: 140];
IF ViewerOps.ViewerColumn[viewer]#right THEN ViewerOps.ChangeColumn[viewer, right];
ViewerOps.OpenIcon[icon: viewer, bottom: FALSE, paint: FALSE]; -- must Open before Bottom
ViewerOps.BottomViewer[viewer: viewer, paint: FALSE];
ViewerOps.ComputeColumn[right];
ViewerTools.SetSelection[viewer, selection];
TiogaOps.GrowSelection[];      -- select the entire word
};
SelectFromFile: PROC [t: Tool] ~ {
FindNode: PROC RETURNS [found: TTNode ¬ NIL] ~ {
Search: PROC [node: TTNode] ~ {
IF node # NIL AND node.clientData # NIL THEN {
st: SourceText ¬ NARROW[node.clientData];
IF Rope.Equal[st.fileName, viewerName] AND ABS[st.position - selPos.start] < 2 THEN {
found ¬ node;
RETURN;
};
IF node.children # NIL THEN
FOR i: INT IN [0..node.children.length) DO
IF found = NIL THEN Search[node.children[i]];
ENDLOOP;
};
};
viewerName: ROPE ¬ FileNames.ConvertToSlashFormat[viewer.name];
Search[t.timeTree.root];
};
node: TTNode;
viewer: Viewer;
save, selPos: ViewerTools.SelPos;
level: TiogaOps.SelectionGrain;
[viewer: viewer, level: level] ¬ TiogaOps.GetSelection[];
save ¬ ViewerTools.GetSelection[viewer];
WHILE level < word DO
TiogaOps.GrowSelection[];
[viewer: viewer, level: level] ¬ TiogaOps.GetSelection[];
ENDLOOP;
selPos ¬ ViewerTools.GetSelection[viewer];
ViewerTools.SetSelection[viewer, save];
node ¬ FindNode[];
IF node = NIL
THEN Controls.TypescriptWrite[t.typescript, "cannot find that node in this tree\n"]
ELSE {
s: Shape ¬ NARROW[node.object];
Controls.TypescriptWrite[t.typescript, "setting that node to selected\n"];
G3dTimeTrees.SetActiveNode[t.timeTree, node];
G3dTool.SelectShape[t, s, middle];
};
HaveFunWith[viewer.file, selPos.start];
};
TimeTrees Operations
TreeMove: PROC [t: Tool, dir: {up, down, left, right}] ~ {
Child: PROC [n: TTNode] RETURNS [c: TTNode] ~ {
GetNodeSequenceNumber: PROC [node: TTNode] RETURNS [INT] ~ {
p: TTNode ¬ node.parent;
IF p = NIL THEN RETURN[-1]; -- root is not a child of anything
FOR i: INT IN [0..p.children.length) DO IF p.children[i] = node THEN RETURN[i]; ENDLOOP;
RETURN[-2]; -- error
};
i: INT ¬ GetNodeSequenceNumber[n];
IF i < 0 THEN RETURN[NIL];
c ¬ n.parent.children[(IF dir = left THEN i-1 ELSE i+1) MOD n.parent.children.length];
};
newNode: TTNode ¬ SELECT dir FROM
up => t.focusNode.parent,
down => IF t.focusNode.children # NIL AND t.focusNode.children.length > 0
THEN t.focusNode.children[0] ELSE NIL,
left, right => Child[t.focusNode],
ENDCASE => NIL;
IF newNode = NIL
THEN Controls.TypescriptWrite[t.typescript, "no node available!\n"]
ELSE t.focusNode ¬ newNode;
};
TransformAndPropagateNode: PUBLIC PROC [
node: TTNode,
matrix: Matrix,
order: G3dMatrix.TransformOrder]
~ {
G3dTimeTrees.TransformNode[node, matrix, order];
G3dTool.PropagateShapeMatrices[node];
};
Scene Ops
SceneOpsButton: PUBLIC ClickProc ~ {
t: Tool ¬ NARROW[clientData];
oldFocus: TTNode ¬ t.focusNode;
choice: INT ¬ Controls.PopUpRequest[["Scene Ops"], LIST[
-- 1 -- ["Write Parameters", "All settings in RIB format to a viewer"],
-- 2 -- ["Write Camera Only", "Camera settings in RIB format to a viewer"],
-- 3 -- ["Up", "move selected node up one branch"],
-- 4 -- ["Down", "move selected node down to first child"],
-- 5 -- ["Left", "move selected node left to sibling"],
-- 6 -- ["Right", "move selected node right to sibling"],
-- 7 -- ["Root", "move selected node to root"],
-- 8 -- ["Show File", "show source file for focusNode"],
-- 9 -- ["From File", "select focusNode from selected text"],
-- 10 -- ["Read Parameters", "Set Parameters from a Viewer"]
]];
SELECT choice FROM
1 => WriteParameters[t, FALSE];
2 => WriteParameters[t, TRUE];
3 => TreeMove[t, up];
4 => TreeMove[t, down];
5 => TreeMove[t, left];
6 => TreeMove[t, right];
7 => t.focusNode ¬ t.timeTree.root;
8 => ShowFile[t];
9 => SelectFromFile[t];
10 => ReadParameters[t];
ENDCASE ;
IF oldFocus # t.focusNode THEN {
G3dTool.SelectNodeShapes[t.focusNode];
G3dTool.Repaint[t];
};
};
WriteParameters: PROC [t: Tool, cameraOnly: BOOL] ~ {
name: ROPE ¬ Controls.TypescriptReadFileName[t.typescript];
IF name = NIL THEN RETURN;
G3dTool.SetRenderView[t.context3d, t.camera];
IF cameraOnly
THEN G3dScene.WriteCameraParameters[t.context3d]
ELSE G3dScene.WriteParameters[t.context3d, name];
};
ReadParameters: PROC [t: Tool] ~ {
[] ¬ G3dScene.ReadParameters[t.context3d];
G3dTool.UpdateCamera[t];
IF Controls.GetPopUpButton[] = right THEN G3dTool.Repaint[t, $Scene];
};
Animation Ops
AnimateOpsButton: PUBLIC ClickProc ~ {
t: Tool ¬ NARROW[clientData];
choice: INT ¬ Controls.PopUpRequest[["Animation Ops"], LIST[
-- 1 -- [IO.PutFR1["Set # Frames (now %g)", IO.int[t.nFrames]], "# frames in animation"],
-- 2 -- [IO.PutFR["Set Times (now %g to %g)", IO.real[t.time0], IO.real[t.time1]], "times"],
-- 3 -- [IO.PutFR1["Set frame (now %g)", IO.int[t.frame]], "set current frame"],
-- 4 -- [IO.PutFR1["Set time (now: %g)", IO.real[t.time]], "set current time"],
-- 5 -- ["ANIMATE", "Show a sequence of frames"],
-- 6 -- ["RENDER Frames", "Render a sequence of frames"],
-- 7 -- [Rope.Cat["Ouptut IP (now ", t.animateIPName, ")"], "Write IP out per frame"],
-- 8 -- ["Store Animation", "Store previously created animation"],
-- 9 -- ["Restore Animation", "Restore previously stored animation"],
-- 10 -- [IO.PutFR1["Playback pause (now: %g)", [real[t.playbackPause]]], "frame wait"],
-- 11 -- ["Playback", "Replay animated frames"],
-- 12 -- ["Cycle", "continuous replay"],
-- 13 -- ["Shuttle", "replay back and forth"],
-- 14 -- ["Shuttle Ease In/Out", "replay back and forth"],
-- 15 -- ["--", ""],
-- 16 -- ["Insert Key", "insert key at this time"]
]];
G3dTool.SetCursor[t, TRUE];
SELECT choice FROM
1 => t.nFrames ¬ Controls.GetNat[t.typescript, "# frames", t.nFrames];
2 => SetTimes[t];
3 => SetCurrentFrame[t, Controls.GetNat[t.typescript, "current frame", t.frame]];
4 => SetCurrentTime[t, Controls.GetReal[t.typescript, "current time", t.time]];
5 => [] ¬ G3dTool.MaybeFork[t, ForkAnimation];
6 => Blink["unimplemented"];
7 => t.animateIPName ¬ Controls.TypescriptReadFileName[t.typescript];
8 => StoreAnimation[t];
9 => RestoreAnimation[t];
10 => t.playbackPause ¬
  Controls.GetReal[t.typescript, "playback pause (secs)", t.playbackPause];
11 => [] ¬ G3dTool.MaybeFork[t, ForkPlayback];
12 => [] ¬ G3dTool.MaybeFork[t, ForkCycle];
13 => [] ¬ G3dTool.MaybeFork[t, ForkShuttle];
14 => [] ¬ G3dTool.MaybeFork[t, ForkShuttleEasy];
16 => InsertKey[t];
ENDCASE;
G3dTool.SetCursor[t, FALSE];
};
InsertKey: PROC [t: Tool] ~ {
DoTransform: G3dTimeTrees.TTNodeActionProc ~ {
IF node # NIL AND G3dTimeTrees.GetTransformDirty[node] THEN
G3dTimeTrees.InsertTransform[t.timeTree, t.time, G3dMatrix.CopyMatrix[node.localTransform]];
};
G3dTimeTrees.EnumerateNodes[t.timeTree, DoTransform];
};
SetTimes: PROC [t: Tool] ~ {
s: IO.STREAMIO.RIS[Controls.TypescriptRead[t.typescript, "Start and end times: "]];
IF s = NIL THEN RETURN;
t.time0 ← IO.GetReal[s ! IO.EndOfStream => GOTO EOF];
t.time1 ← IO.GetReal[s ! IO.EndOfStream => GOTO EOF];
EXITS EOF => Blink["Missing value(s)"];
times: RealSequence ¬ Controls.TypescriptReadValues[
t.typescript, "times", LIST[["time0", t.time0], ["time1", t.time1]]];
IF times # NIL THEN {t.time0 ¬ times[0]; t.time1 ¬ times[1]};
};
SetCurrentFrame: PROC [t: Tool, frame: NAT] ~ {
IF frame >= t.nFrames THEN Blink[IO.PutFR1["frame must be < %g", IO.int[t.nFrames]]];
IF frame = t.frame OR frame >= t.nFrames THEN RETURN;
t.frame ¬ frame;
t.time ¬ t.time0+(REAL[t.frame]/REAL[MAX[1, t.nFrames-1]])*(t.time1-t.time0);
[] ¬ G3dTool.MaybeFork[t, ForkFrame];
};
SetCurrentTime: PROC [t: Tool, time: REAL] ~ {
IF time # t.time THEN {
t.time ¬ time;
IF t.time1 # t.time0 THEN t.frame ¬ Real.Round[REAL[t.nFrames]*t.time/(t.time1-t.time0)];
[] ¬ G3dTool.MaybeFork[t, ForkFrame];
};
};
ForkFrame: CedarProcess.ForkableProc ~ {DoFrame[NARROW[data, Tool]]};
ForkAnimation: CedarProcess.ForkableProc ~ {
t: Tool ¬ NARROW[data];
view: G3dTool.View ¬ G3dTool.GetView[NIL, t];
IF t.playback # NIL THEN t.playback.length ¬ 0;
FOR i: INT IN [0..t.nFrames) DO
map: SampleMap;
CedarProcess.CheckAbort[];
AnimateFrame[t, i];
map ¬ G3dTool.GetBufferedSampleMap[view];
IF map # NIL THEN t.playback ¬ AddToAnimation[ImagerSample.Copy[map], t.playback];
REPEAT FINISHED => G3dTool.Finish[t, "animation done"];
ENDLOOP;
};
AnimateFrame: PROC [t: Tool, frame: NAT] ~ {
Draw: Controls.DrawProc ~ {
v: G3dTool.View ¬ G3dTool.GetView[viewer, t];
G3dTool.Draw[context, viewer, NIL, v.camera.matrix, v.viewport, v, t, t];
};
alpha: REAL ¬ REAL[t.frame ¬ frame]/REAL[MAX[1, t.nFrames-1]];
t.time ¬ t.time0+alpha*(t.time1-t.time0);
MessageWindow.Append[IO.PutFR["frame %g (time:%g)", IO.int[frame], IO.real[t.time]], TRUE];
DoFrame[t];
IF t.animateIPName # NIL THEN Draw2d.IPOut[
IO.PutFR["%g.%g", IO.rope[t.animateIPName], IO.int[frame]], Draw, t];
};
DoFrame: PROC [t: Tool, repaint: BOOL ¬ TRUE, render: BOOL ¬ FALSE] ~ {
context3d: G3dRender.Context3d ¬ t.context3d;
IF t.client.animate # NIL THEN {
alpha: REAL ¬ REAL[t.frame]/REAL[MAX[1, t.nFrames-1]];
t.client.animate[t.frame, alpha, t.time, t.client.data];
};
IF t.shapes # NIL THEN {
tt: TimeTree ¬ G3dTimeTrees.GetRoot[NARROW[t.shapes[0].hierarchyData]];
G3dTimeTrees.StrobeTimeTree[tt, t.time];
G3dTimeTrees.ReportTimeTree[t.timeTree];
};
IF repaint THEN G3dTool.Repaint[t, $Scene];
IF render THEN G3dTool.Context3dOk[t];
};
AddToAnimation: PROC [m: SampleMap, a: Animation] RETURNS [ret: Animation] ~ {
IF (ret ¬ a) = NIL THEN ret ¬ NEW[G3dTool.AnimationRep[10]];
IF ret.length = ret.maxLength THEN {
old: Animation ¬ ret;
ret ¬ NEW[G3dTool.AnimationRep[2*old.length]];
FOR n: NAT IN [0..ret.length ¬ old.length) DO ret[n] ¬ old[n]; ENDLOOP;
};
ret[(ret.length ¬ ret.length+1)-1] ¬ m;
};
StoreAnimation: PROC [t: Tool] ~ {
AddToAnimations: PROC [a: Animation, anims: Animations] RETURNS [ret: Animations] ~ {
IF (ret ¬ anims) = NIL THEN ret ¬ NEW[G3dTool.AnimationsRep[10]];
FOR n: NAT IN [0..ret.length) DO
IF Rope.Equal[ret[n].name, a.name, FALSE] THEN {ret[n] ¬ a; RETURN};
ENDLOOP;
IF ret.length = ret.maxLength THEN {
old: Animations ¬ ret;
ret ¬ NEW[G3dTool.AnimationsRep[2*old.length]];
FOR n: NAT IN [0..ret.length ¬ old.length) DO ret[n] ¬ old[n]; ENDLOOP;
};
ret[(ret.length ¬ ret.length+1)-1] ¬ a;
};
IF (t.playback.name ¬ Controls.TypescriptRead[t.typescript, "animation name: "]) # NIL
THEN t.animations ¬ AddToAnimations[t.playback, t.animations];
};
RestoreAnimation: PROC [t: Tool] ~ {
IF t.animations = NIL OR t.animations.length = 0
THEN G3dTool.TSWrite[t, "no saved frames"]
ELSE {
c: INTEGER;
r, l: LIST OF Controls.Request;
FOR i: NAT IN [0..t.animations.length) DO l ¬ CONS[[t.animations[i].name], l]; ENDLOOP;
FOR s: LIST OF Controls.Request ¬ l, s.rest WHILE s#NIL DO r ¬ CONS[s.first, r]; ENDLOOP;
IF (c ¬ Controls.PopUpRequest[["Restore"], r]) > 0 THEN t.playback ¬ t.animations[c-1];
};
};
ForkPlayback: ForkableProc ~ {Playback[NARROW[data], playback]};
ForkCycle: ForkableProc ~ {Playback[NARROW[data], cycle]};
ForkShuttle: ForkableProc ~ {Playback[NARROW[data], shuttle]};
ForkShuttleEasy: ForkableProc ~ {Playback[NARROW[data], shuttleEasy]};
playbackFactor: REAL ¬ 250.0;
Playback: PROC [t: Tool, mode: {playback, cycle, shuttle, shuttleEasy}] ~ {
Once: PROC [op: {forward, backward}] ~ {
FOR n: NAT IN [0..t.playback.length) DO
map: SampleMap ¬ t.playback[IF op = forward THEN n ELSE t.playback.length-n-1];
CedarProcess.CheckAbort[];
G3dTool.SetBufferedSampleMap[map, view];
G3dTool.Repaint[t, $Restore];
Process.Pause[Process.MsecToTicks[Real.Round[playbackFactor*(IF mode = shuttleEasy
THEN ABS[0.5-REAL[n]/REAL[t.playback.length-1]] ELSE t.playbackPause)]]];
ENDLOOP;
};
view: G3dTool.View ¬ G3dTool.GetView[NIL, t];
IF t.playback # NIL THEN SELECT mode FROM
playback => Once[forward];
cycle => DO Once[forward]; ENDLOOP;
ENDCASE => DO Once[forward]; Once[backward]; ENDLOOP;
};
Support
Blink: PROC [r: ROPE] ~ {
MessageWindow.Append[Rope.Concat["\t\t", r], TRUE];
MessageWindow.Blink[];
};
END...
Debug
DumpTimeTree: PROC [tt: TimeTree] ~ {
DumpNode: PROC [prefix: ROPE, node: TTNode] ~ {
ShowMatrix: PROC [name: ROPE, m: Matrix] ~ {
TerminalIO.PutRope[IO.PutFR["%g%g matrix:\n", IO.rope[prefix], IO.rope[name]]];
IF m = NIL THEN {
TerminalIO.PutRope[IO.PutFR["%g -- NIL --\n", IO.rope[prefix]]];
RETURN;
};
FOR row: INT IN [0 .. 4) DO
TerminalIO.PutRope[
IO.PutFR["%g [%g %g %g %g]\n",
IO.rope[prefix], IO.real[m[row][0]], IO.real[m[row][1]], IO.real[m[row][2]], IO.real[m[row][3]]]];
ENDLOOP;
};
TerminalIO.PutRope[IO.PutFR["%g", IO.rope[prefix]]];
IF node.object = NIL
THEN TerminalIO.PutRope["node.object = NIL\n"]
ELSE TerminalIO.PutRope[IO.PutFR["node.object.name = %g\n", IO.rope[NARROW[node.object, Shape].name]]];
ShowMatrix["LOCAL", node.localTransform];
ShowMatrix["GLOBAL", node.globalTransform];
IF node.children = NIL
THEN TerminalIO.PutRope[IO.PutFR["%gno children\n", IO.rope[prefix]]]
ELSE FOR i: INT IN [0 .. node.children.length) DO
DumpNode[Rope.Cat[prefix, ".|."], node.children[i]];
ENDLOOP;
};
DumpNode["", tt.root];
};
Unused
ShadowNode:    TYPE ~ REF ShadowNodeRep;
ShadowNodeRep:   TYPE ~ RECORD [
parent:        ShadowNode ¬ NIL,
children:       LIST OF ShadowNode ¬ NIL,
timeTreeNode:     TTNode ¬ NIL
];
ShadowTree:    TYPE ~ REF ShadowTreeRep;
ShadowTreeRep:   TYPE ~ RECORD [root: ShadowNode ¬ NIL];
BuildShadow: PROC [timeTree: TimeTree] RETURNS [newTree: ShadowTree] ~ {
newTree ¬ NEW[ShadowTreeRep ¬ []];
newTree.root ¬ NEW[ShadowNodeRep ¬ []];
};