<<>> <> <> <> <> <> <> <> <> <<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 <> 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; <> 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]; }; <> }; <> 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]; }; <> 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]; }; <> 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] ~ { <> <> < GOTO EOF];>> < GOTO 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; }; <> Blink: PROC [r: ROPE] ~ { MessageWindow.Append[Rope.Concat["\t\t", r], TRUE]; MessageWindow.Blink[]; }; END... <> 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]; }; <> 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 ¬ []]; };