G3dSceneImpl.mesa
Copyright Ó 1988, 1992 by Xerox Corporation. All rights reserved.
Bloomenthal, July 21, 1992 11:43 am PDT
Crow, May 27, 1989 6:30:47 pm PDT
Glassner, July 5, 1990 12:09:40 pm PDT
Shoemake, August 30, 1989 9:05:58 pm PDT
DIRECTORY Args, Ascii, Atom, CedarProcess, Commander, CommanderOps, FileNames, G3dBasic, G3dLight, G3dMatrix, G3dRender, G3dScene, G3dShape, G3dTimeTrees, IO, MessageWindow, PFS, Random, Real, RealFns, Rope, TiogaAccess, TiogaOps, ViewerOps, ViewerTools;
G3dSceneImpl:
CEDAR
PROGRAM
IMPORTS Args, Atom, CedarProcess, CommanderOps, FileNames, G3dMatrix, G3dRender, G3dTimeTrees, IO, MessageWindow, PFS, Random, Real, RealFns, Rope, TiogaOps, ViewerOps, ViewerTools
EXPORTS G3dScene
~ BEGIN
Type Declarations
Arg: TYPE ~ Args.Arg;
NatSequence: TYPE ~ G3dBasic.NatSequence;
SurfaceSequence: TYPE ~ G3dBasic.SurfaceSequence;
Pair: TYPE ~ G3dBasic.Pair;
Triple: TYPE ~ G3dBasic.Triple;
Light: TYPE ~ G3dLight.Light;
RGB: TYPE ~ G3dRender.RGB;
Context3d: TYPE ~ G3dRender.Context3d;
RenderData: TYPE ~ G3dRender.RenderData;
RenderStyle: TYPE ~ G3dRender.RenderStyle;
TextureStyle: TYPE ~ G3dRender.TextureStyle;
LogProc: TYPE ~ G3dScene.LogProc;
Material: TYPE ~ G3dScene.Material;
ParseState: TYPE ~ G3dScene.ParseState;
ParseStateRep: TYPE ~ G3dScene.ParseStateRep;
Registry: TYPE ~ G3dScene.Registry;
Variable: TYPE ~ G3dScene.Variable;
VariableSequence: TYPE ~ G3dScene.VariableSequence;
VariableSequenceRep: TYPE ~ G3dScene.VariableSequenceRep;
SceneProc: TYPE ~ G3dScene.SceneProc;
Shape: TYPE ~ G3dShape.Shape;
ShapeRep: TYPE ~ G3dShape.ShapeRep;
ShapeSequence: TYPE ~ G3dShape.ShapeSequence;
VertexSequenceRep: TYPE ~ G3dShape.VertexSequenceRep;
TimeTree: TYPE ~ G3dTimeTrees.TimeTree;
STREAM: TYPE ~ IO.STREAM;
RandomStream: TYPE ~ Random.RandomStream;
ROPE: TYPE ~ Rope.ROPE;
Writer: TYPE ~ TiogaAccess.Writer;
SelPos: TYPE ~ ViewerTools.SelPos;
Viewer: TYPE ~ ViewerTools.Viewer;
SourceText: TYPE ~ G3dScene.SourceText;
SourceTextRep: TYPE ~ G3dScene.SourceTextRep;
Write Scene Descriptions to a Viewer
Item: TYPE ~ RECORD [name: ROPE, value: REAL];
WriteInfo: TYPE ~ RECORD [
writer: Writer ← NIL,
fileName: ROPE ← NIL,
stream: STREAM ← NIL,
ok: BOOL ← TRUE];
GetWriteInfo: PROC RETURNS [i: WriteInfo] ~ {
v: Viewer ← ViewerTools.GetSelectedViewer[];
i.writer ← TiogaAccess.Create[];
IF Rope.Length[ViewerTools.GetSelectionContents[]] # 0
THEN i.fileName ←
FileNames.ConvertToSlashFormat[
FileNames.ResolveRelativePath[ViewerTools.GetSelectionContents[]]]
ELSE i.ok ← v # NIL AND (v.class.flavor = $Text OR v.class.flavor = $Typescript);
IF NOT i.ok THEN Blink["Make primary selection"];
};
PutWriter: PROC [i: WriteInfo] ~ {
WriteToViewer: PROC [root: TiogaOps.Ref] ~ {
TiogaAccess.WriteSelection[i.writer ! TiogaAccess.Error => {Blink[expl]; CONTINUE}];
};
SELECT TRUE FROM
i.stream # NIL =>
TiogaAccess.WriteOpenFile[i.writer, [PFS.OpenFileFromStream[i.stream]]
! PFS.Error => {Blink[error.explanation]; CONTINUE}];
i.fileName # NIL =>
TiogaAccess.WriteFile[i.writer, i.fileName
! PFS.Error => {Blink[error.explanation]; CONTINUE}]
ENDCASE => TiogaOps.CallWithLocks[WriteToViewer];
};
Nest: PROC [w: Writer] ~ {TiogaAccess.Nest[w, 1]};
UnNest: PROC [w: Writer] ~ {TiogaAccess.Nest[w, -1]};
WriteNode: PROC [w: Writer, rope: ROPE, looks: ROPE ← NIL, comment: BOOL ← FALSE] ~ {
tc: TiogaAccess.TiogaChar ← [charSet: 0, char: '\000, looks: ALL[FALSE], format: NIL, comment: comment, endOfNode: FALSE, deltaLevel: 0, propList: NIL];
FOR n: INT IN [0..Rope.Length[looks]) DO
c: CHAR ← Rope.Fetch[looks, n];
IF c IN TiogaAccess.Look THEN tc.looks[c] ← TRUE;
ENDLOOP;
FOR n: INT IN [0..Rope.Length[rope]) DO
tc.char ← Rope.Fetch[rope, n];
TiogaAccess.Put[w, tc];
ENDLOOP;
tc.endOfNode ← TRUE;
TiogaAccess.Put[w, tc];
};
WriteItem: PROC [w: Writer, item: Item] ~ {WriteReal[w, item.name, item.value]};
WriteBool: PROC [w: Writer, name: ROPE, bool: BOOL] ~ {
WriteNode[w, Rope.Cat[name, ":\t", IF bool THEN "True" ELSE "False"]];
};
WriteReal: PROC [w: Writer, name: ROPE, value: REAL] ~ {
WriteNode[w, IO.PutFR["%g:\t%g", IO.rope[name], IO.real[value]]];
};
WritePair: PROC [w: Writer, name: ROPE, p: Pair] ~ {
WriteNode[w, IO.PutFR["%g:\t%g, %g", IO.rope[name], IO.real[p.x], IO.real[p.y]]];
};
WriteTriple: PROC [w: Writer, name: ROPE, t: Triple] ~ {
WriteNode[w, IO.PutFR["%g:\t(%g, %g, %g)",
IO.rope[name], IO.real[t.x], IO.real[t.y], IO.real[t.z]]];
};
WriteRGB: PROC [w: Writer, name: ROPE, rgb: RGB] ~ {
WriteTriple[w, name, [rgb.R, rgb.G, rgb.B]];
};
WriteFour: PROC [w: Writer, name: ROPE, v1, v2, v3, v4: REAL] ~ {
WriteNode[w, IO.PutFR["%g:\t(%g, %g, %g, %g)",
IO.rope[name], IO.real[v1], IO.real[v2], IO.real[v3], IO.real[v4]]];
};
WriteTitle: PROC [w: Writer, title: ROPE] ~ {
WriteNode[w, Rope.Cat["-- ", title], "b", TRUE];
Nest[w];
};
WriteShape: PROC [w: Writer, s: Shape] ~ {
r: RenderData ← G3dRender.RenderDataFrom[s];
WriteNode[w, Rope.Cat["ShapeRead ", s.name], "b"];
Nest[w];
WriteBool[w, "ShapeBackFaces", s.showBackfaces];
WriteNode[w, Rope.Cat["ShapeRenderStyle: ",G3dRender.RopeFromRenderStyle[r.renderStyle]]];
WriteRGB[w, "ShapeColor", r.color];
WriteReal[w, "ShapeShininess", r.shininess];
IF r.transmittance # 0.0 THEN WriteReal[w, "ShapeTransmittance", r.transmittance];
WriteTriple[w, "ShapeTranslate", s.translation];
FOR l: LIST OF G3dRender.TextureMap ← r.textures, l.rest WHILE l # NIL DO
WriteNode[w, IO.PutFR["Shape%gMap %g, ShapeTextureFiltering: ",
IO.rope[G3dRender.RopeFromTextureStyle[l.first.style]],
IO.rope[l.first.name],
IO.rope[IF l.first.filter THEN "True" ELSE "False"]]];
ENDLOOP;
UnNest[w];
};
WriteLight: PROC [w: Writer, l: Light] ~ {
WriteNode[w, IO.PutFLR["%g: %g, (%g, %g, %g), (%g, %g, %g)", LIST[
IO.rope["LightAdd"],
IO.rope[l.name],
IO.real[l.position.x], IO.real[l.position.y], IO.real[l.position.z],
IO.real[l.color.R], IO.real[l.color.G], IO.real[l.color.B]]]];
};
WriteCamera: PROC [context3d: Context3d, w: Writer] ~ {
WriteTitle[w, "Camera Parameters"];
WriteReal[w, "FieldOfView", context3d.fieldOfView];
WriteTriple[w, "EyePoint", context3d.eyePoint];
WriteTriple[w, "LookAt", context3d.lookAt];
WriteTriple[w, "Up", context3d.upDirection];
WriteReal[w, "Roll", context3d.rollAngle];
UnNest[w];
};
WriteCameraParameters: PUBLIC PROC [context3d: Context3d] ~ {
i: WriteInfo ← GetWriteInfo[];
IF NOT i.ok THEN RETURN;
WriteCamera[context3d, i.writer];
WriteNode[i.writer, NIL];
UnNest[i.writer];
PutWriter[i];
};
WriteParameters: PUBLIC PROC [context3d: Context3d, fileName: ROPE ← NIL] ~ {
i: WriteInfo ← IF fileName = NIL THEN GetWriteInfo[] ELSE [TiogaAccess.Create[], fileName];
WriteParametersToWriter[context3d, i];
};
WriteParametersToStream: PUBLIC PROC [context3d: Context3d, out: STREAM] ~ {
WriteParametersToWriter[context3d, [writer: TiogaAccess.Create[], stream: out]];
};
WriteParametersToWriter: PUBLIC PROC [c: Context3d, i: WriteInfo] ~ {
w: Writer ← i.writer;
IF NOT i.ok OR c = NIL THEN RETURN;
WriteTitle[w, "3d Context3d Parameters"];
WriteCamera[c, w];
WriteTitle[w, "Image Parameters"];
IF c.window # [0, 0, 0, 0]
THEN WriteFour[w, "DisplayRegion", c.window.x, c.window.y, c.window.w, c.window.h];
IF c.viewport # [0.0, 0.0, 65536.0, 65536.0]
THEN WriteFour[w, "Viewport: ", c.viewport.x, c.viewport.y, c.viewport.w, c.viewport.h];
WriteRGB[w, "Background", c.backgroundColor];
IF c.backgroundName # NIL
THEN WriteNode[w, Rope.Cat["BackgroundImage", c.backgroundName]];
WriteBool[w, "AntiAliasing", c.antiAliasing];
UnNest[w];
WriteTitle[w, "Shapes"];
FOR n: NAT IN [0..c.shapes.length) DO WriteShape[w, c.shapes[n]]; ENDLOOP;
UnNest[w];
WriteTitle[w, "Lights"];
FOR n: NAT IN [0..c.lightSources.length) DO WriteLight[w, c.lightSources[n]]; ENDLOOP;
WriteRGB[w, "LightAmbience", G3dRender.GetAmbientLight[c]];
WriteNode[w, NIL];
UnNest[w];
UnNest[w];
PutWriter[i];
};
Read Scene Descriptions from a Viewer or a File
ReadInfo:
TYPE ~
RECORD [operations:
ROPE,
v: Viewer, sel: SelPos, fileName:
ROPE ¬
NIL];
GetReaderInfo:
PROC
RETURNS [i: ReadInfo] ~ {
i.operations ¬ ViewerTools.GetSelectionContents[];
IF Rope.Length[i.operations] = 0 THEN RETURN[[NIL, NIL, NIL]];
IF Rope.Length[i.operations] < 100
THEN {
i.fileName ¬ FileNames.ResolveRelativePath[i.operations];
i.fileName
¬
PFS.RopeFromPath[
PFS.FileInfo[
PFS.PathFromRope[i
.fileName]
! PFS.Error => {i.fileName ¬ NIL; CONTINUE}].fullFName];
IF i.fileName #
NIL
THEN i.operations ¬ PFS.RopeOpen[PFS.PathFromRope[i.fileName]].rope;
}
ELSE i.sel ¬ ViewerTools.GetSelection[i.v ¬ ViewerTools.GetSelectedViewer[]];
};
MakeFrameFromFile:
PUBLIC
PROC [context3d: Context3d, fileName:
ROPE] ~ {
[] ¬ ReadParameters[context3d, fileName];
temp.class.render[temp];
};
ReadParameters:
PUBLIC
PROC [
context3d: Context3d,
fileName: ROPE ¬ NIL,
cmdOut: STREAM ¬ NIL]
RETURNS [shouldRepaint: BOOL]
~ {
i: ReadInfo ¬
IF fileName =
NIL
THEN GetReaderInfo[]
ELSE [PFS.RopeOpen[PFS.PathFromRope[fileName]].rope, NIL, NIL, fileName];
shouldRepaint ¬ Parse[context3d, i.operations, cmdOut,,
TRUE
! ParseError => {IO.PutRope[cmdOut, explanation]; CONTINUE}].shouldRepaint;
};
Blink:
PROC [r:
ROPE] ~ {
MessageWindow.Append[Rope.Concat["\t\t", r], TRUE];
MessageWindow.Blink[];
};
Parsing a Scene Description
ParseError:
PUBLIC
ERROR [explanation:
ROPE] =
CODE;
GetRope:
PROC [in:
STREAM]
RETURNS [r:
ROPE] ~ {
Skip[in];
r ¬ IO.GetTokenRope[in, IO.IDProc].token;
};
GetBool: PROC [in: STREAM] RETURNS [b: BOOL] ~ {Skip[in]; b ¬ IO.GetBool[in]};
GetReal: PROC [in: STREAM] RETURNS [r: REAL] ~ {Skip[in]; r ¬ IO.GetReal[in]};
GetInt: PROC [in: STREAM] RETURNS [i: INTEGER] ~ {Skip[in]; i ¬ IO.GetInt[in]};
GetPair: PROC [in: STREAM] RETURNS [p: Pair] ~ {p.x ¬ GetReal[in]; p.y ¬ GetReal[in]};
GetRGB:
PROC [in:
STREAM]
RETURNS [r:
RGB] ~ {t: Triple ¬ GetTriple[in]; r ¬ [t.x, t.y, t.z]};
GetTriple:
PROC [in:
STREAM]
RETURNS [t: Triple]~{
t.x ¬ GetReal[in]; t.y ¬ GetReal[in]; t.z ¬ GetReal[in];
};
Skip:
PROC [in:
STREAM]
~
{
DO
c: CHAR ¬ IO.PeekChar[in];
IF c=') OR c='( OR c=': OR c=', OR c=Ascii.SP OR c='\t THEN [] ¬ IO.GetChar[in] ELSE EXIT;
ENDLOOP;
};
WriteLog:
PUBLIC PROC [ps: ParseState, msg:
ROPE] ~ {
IF ps.logProc = NIL THEN RETURN;
THROUGH [0..ps.nesting) DO msg ¬ Rope.Concat["*", msg]; ENDLOOP;
ps.logProc[msg, ps.clientData];
};
Eq:
PROC [r1, r2:
ROPE]
RETURNS [
BOOL] ~ {
RETURN[Rope.Equal[r1, r2,
FALSE]]};
ShapeOk:
PROC [ps: ParseState]
RETURNS [ok:
BOOL ¬
FALSE] ~ {
IF ps.currentShape # NIL THEN RETURN[TRUE];
Blink[Rope.Concat["No shape to perform: ", ps.command]];
};
Parse:
PUBLIC
PROC [
context3d: Context3d,
operation: ROPE,
cmdOut: STREAM ¬ NIL,
logProc: LogProc ¬ NIL,
logErrors: BOOL ¬ TRUE,
fileName: ROPE ¬ NIL,
clientData: REF ANY ¬ NIL,
parseState: ParseState ¬ NIL]
RETURNS [ParseState]
~ {
InnerParse:
PROC [operation:
ROPE] ~ {
ENABLE {
IO.EndOfStream => {
IF logErrors
AND nErrors > 0
THEN {
ps.errorMessage ¬ IO.PutFR["\t%g error", IO.int[nErrors]];
IF nErrors > 1 THEN ps.errorMessage ¬ Rope.Cat[ps.errorMessage, "s"];
};
CONTINUE;
};
IO.Error => {IF logErrors THEN ps.errorMessage ¬ "Error during read"; CONTINUE};
};
DoRegisteredOp:
PROC [args:
ROPE]
RETURNS [
BOOL] ~ {
FOR r: Registry ¬ registry, r.rest
WHILE r #
NIL
DO
IF Eq[ps.command, r.first.key]
THEN {
Let's not write to the log, since a scene proc will usually provide feedback:
WriteLog[ps, IF args#NIL THEN Rope.Cat[ps.command, ":", args] ELSE ps.command];
r.first.proc[ps, args, r.first.data ! ParseError => {LogError[explanation]; CONTINUE}];
RETURN[TRUE];
};
REPEAT FINISHED => RETURN[FALSE];
ENDLOOP;
};
LogError:
PROC [errMsg:
ROPE] ~ {
lastLoc: TiogaOps.Location;
IF ps.errorViewer =
NIL
THEN {
name: ROPE ¬ NIL;
IF ps.fileName #
NIL
THEN {
i: INT ¬ MAX[Rope.FindBackward[ps.fileName,"/"], Rope.FindBackward[ps.fileName,">"]];
n: INT ¬ Rope.FindBackward[ps.fileName, "."];
IF n > i THEN name ¬ Rope.Cat[Rope.Substr[ps.fileName, 0, n], ".sceneErrors"];
IF i = -1 THEN name ¬ Rope.Cat[ps.directory, name];
};
IF name = NIL THEN name ¬ Rope.Cat[ps.directory, "List.sceneErrors"];
ps.errorViewer
¬
ViewerTools.MakeNewTextViewer[[name: name, file: name, column: right]];
ViewerOps.OpenIcon[ps.errorViewer];
};
lastLoc ¬ TiogaOps.LastLocWithin[TiogaOps.ViewerDoc[ps.errorViewer]];
TiogaOps.SetSelection[ps.errorViewer, lastLoc, lastLoc];
TiogaOps.SetLooks["b"];
TiogaOps.InsertRope[
IO.PutFR["Error on line %g, file %g, position %g",
IO.int[lineNumber], IO.rope[ps.fileName], IO.int[ps.index]]];
TiogaOps.SetLooks[" "];
TiogaOps.Break[];
TiogaOps.Nest[];
IO.SetIndex[in, ps.index];
TiogaOps.InsertRope[GetRope[in ! IO.EndOfStream => CONTINUE]];
TiogaOps.InsertRope[IO.GetLineRope[in ! IO.EndOfStream => CONTINUE]];
IF errMsg # NIL THEN {TiogaOps.Break[]; TiogaOps.InsertRope[errMsg]};
TiogaOps.Break[];
TiogaOps.UnNest[];
nErrors ¬ nErrors+1;
};
lineNumber: INT ¬ 0;
in: STREAM ¬ IO.RIS[operation];
DO
args: ROPE;
CedarProcess.CheckAbort[];
ps.index ¬ IO.GetIndex[in];
ps.command ¬ GetRope[in];
args ¬ IO.GetLineRope[in ! IO.EndOfStream => CONTINUE];
IF
NOT Eq[ps.command, "Set"]
THEN {
-- variable substitution before interpretation:
s: STREAM ¬ IO.RIS[SubstituteVariables[ps.variables, Rope.Cat[ps.command, " ", args], ps.randomStream]];
ps.command ¬ IO.GetTokenRope[s, IO.IDProc].token;
args ¬ IO.GetLineRope[s ! IO.EndOfStream => CONTINUE];
IF Rope.Find[args, ";"] # -1
THEN {
-- treat as several commands:
Translate: Rope.TranslatorType ~ {RETURN[IF old = '; THEN '\n ELSE old]};
args ¬ Rope.Translate[args,,, Translate];
InnerParse[Rope.Cat[ps.command, " ", args]];
ps.command ¬ "";
};
};
SELECT
TRUE
FROM
DoRegisteredOp[args] => NULL;
Eq[ps.command, "
ReadScene"] => {
save: ROPE ¬ ps.fileName;
name: ROPE ¬ GetRope[IO.RIS[args]];
WriteLog[ps, Rope.Cat["Reading scene: ", name]];
ps.nesting ¬ ps.nesting+1;
ps.fileName ¬ name;
InnerParse[PFS.RopeOpen[PFS.PathFromRope[name]].rope];
ps.fileName ¬ save;
ps.nesting ¬ ps.nesting-1;
};
Eq[ps.command, ""] => {
ps.command ¬ " ";
};
ENDCASE => LogError[Rope.Cat["Unknown command: ", ps.command]];
lineNumber ¬ lineNumber+1;
ENDLOOP;
};
nErrors: INT ¬ 0;
ps: ParseState ¬ IF parseState # NIL THEN parseState ELSE NEW[ParseStateRep];
ps.errorMessage ¬ NIL;
ps.fileName ¬ fileName;
ps.context3d ¬ context3d;
ps.logProc ¬ logProc;
ps.clientData ¬ clientData;
IF ps.timeTree =
NIL
THEN {
ps.timeTree ¬ G3dTimeTrees.CreateTimeTree[];
G3dTimeTrees.InitializeGraphicsTimeTree[ps.timeTree];
};
IF ps.context3d # NIL AND ps.context3d.shapes # NIL
THEN ps.currentShape ¬ ps.context3d.shapes[ps.context3d.shapes.length-1];
ps.cmdHandle ¬ NEW[Commander.CommandObject];
ps.directory ¬ PFS.RopeFromPath[PFS.GetWDir[]];
CommanderOps.AddSearchRule[ps.cmdHandle, ps.directory];
CommanderOps.AddSearchRule[ps.cmdHandle, "///7.0/Commands/"];
CommanderOps.AddSearchRule[ps.cmdHandle, "///7.0/System/"];
ps.cmdHandle.err ¬ ps.cmdHandle.out ¬ cmdOut;
ps.ctm ¬ G3dMatrix.Identity[];
InnerParse[operation];
RETURN[ps];
};
Support for Shape Creation
InheritMaterial:
PUBLIC PROC [ps: ParseState] ~ {
SetTexture:
PROC [name:
ROPE, style: TextureStyle] ~ {
IF name #
NIL
THEN
error ¬ Rope.Cat[error, G3dRender.SetTextureMap[shape, name, style]];
G3dRender.OffsetTextureCoords[shape, material.textureOffset];
G3dRender.SetTextureScale[shape, name, material.textureScale];
G3dRender.SetBumpHeight[shape, name, material.bumpScale];
G3dRender.SetTextureFiltering[shape, name, material.textureFiltering];
};
error: ROPE ¬ NIL;
shape: Shape ¬ ps.currentShape;
material: Material ¬ ps.currentMaterial;
firstChar: ROPE ¬ Rope.Substr[ps.fileName, 0, 1];
fullName:
ROPE ¬
IF Rope.Equal[firstChar, "["]
OR Rope.Equal[firstChar, "/"]
THEN ps.fileName
ELSE Rope.Cat[ps.directory, ps.fileName];
G3dRender.SetRenderStyle[shape, material.renderStyle];
G3dRender.SetColor[shape, [material.color.x, material.color.y, material.color.z]];
G3dRender.SetShininess[shape, material.shininess];
G3dRender.SetTransmittance[shape, material.transmittance];
IF material.backFaces
THEN G3dRender.ShowBackfaces[shape]
ELSE G3dRender.HideBackfaces[shape];
IF material.visible
THEN G3dRender.SetVisible[shape]
ELSE G3dRender.SetInvisible[shape];
SetTexture[material.textureIntensityName, intensity];
SetTexture[material.textureColorName, color];
SetTexture[material.textureBumpName, bump];
G3dTimeTrees.SetLocalTransform[ps.timeTree, ps.ctm];
shape.hierarchyData ¬ G3dTimeTrees.SetNodeObject[ps.timeTree, shape];
ps.timeTree.activeNode.clientData ¬ NEW[SourceTextRep ¬ [fullName, ps.index]];
IF error # NIL THEN ParseError[error];
};
Registering Procedures Callable from a Scene File
registry: Registry ¬
NIL;
RegisterProc:
PUBLIC
PROC [key:
ROPE, proc: SceneProc, data:
REF
ANY ¬
NIL] ~ {
UnregisterProc[key];
registry ¬ CONS[[key, proc, data], registry];
};
UnregisterProc:
PUBLIC
PROC [key:
ROPE] ~ {
IF registry = NIL THEN RETURN;
IF Rope.Equal[registry.first.key, key]
THEN registry ¬ registry.rest
ELSE FOR r: Registry ¬ registry, r.rest
WHILE r.rest #
NIL
DO
IF Rope.Equal[r.rest.first.key, key] THEN {r.rest ¬ r.rest.rest; EXIT};
ENDLOOP;
};
GetRegistry:
PUBLIC
PROC
RETURNS [Registry] ~ {
RETURN[registry]};
ResetRegistry: PUBLIC PROC ~ {registry ¬ NIL};
Registered SceneProcs
Echo: SceneProc ~ {WriteLog[ps, args]};
Command: SceneProc ~ {
result: REF;
name: ROPE ¬ GetRope[IO.RIS[args]];
WriteLog[ps, IO.PutFR["Command: %g\n%l", IO.rope[name], IO.rope["i"]]];
WriteLog[ps, IO.PutFR["%l", IO.rope["I"]]];
result ¬ CommanderOps.DoCommand[args, ps.cmdHandle];
IF result # NIL THEN WriteLog[ps, IO.PutFR["(%g)", IO.refAny[result]]];
};
TimeTree Node Construction
MakeTransformNode:
PUBLIC
PROC [ps: ParseState] ~ {
IF Atom.GetPropFromList[ps.changes, $transform] = NIL THEN RETURN;
G3dTimeTrees.SetLocalTransform[ps.timeTree, ps.ctm];
G3dTimeTrees.PushTimeTree[ps.timeTree];
ps.changes ¬ Atom.RemPropFromList[ps.changes, $transform];
ps.ctm ¬ G3dMatrix.Identity[];
};
MakeKeysNode:
PUBLIC
PROC [ps: ParseState] ~ {
G3dTimeTrees.PushTimeTree[ps.timeTree];
ps.changes ¬ Atom.RemPropFromList[ps.changes, $transform];
ps.ctm ¬ G3dMatrix.Identity[];
};
MakeShapeNode:
PUBLIC
PROC [ps: ParseState] ~ {
shape: Shape ¬ ps.currentShape;
IF Atom.GetPropFromList[ps.changes, $transform] # NIL THEN {
MakeTransformNode[ps];
ps.changes ← Atom.RemPropFromList[ps.changes, $transform];
};
workaround for rendering bug which doesn't support shear and differential scale
save the shape in the tree
G3dTimeTrees.SetLocalTransform[ps.timeTree, ps.ctm];
shape.hierarchyData ¬ G3dTimeTrees.SetNodeObject[ps.timeTree, shape];
G3dTimeTrees.PushTimeTree[ps.timeTree];
G3dTimeTrees.PopTimeTree[ps.timeTree];
};
Utility Procs for Registered SceneProcs
AssignCtmToShape:
PUBLIC PROC [ps: ParseState, shape: Shape] ~ {
shape.matrix ¬ G3dMatrix.CopyMatrix[ps.ctm];
shape.renderValid ¬ FALSE;
};
GetArgs:
PUBLIC PROC [ps: ParseState, input, format:
ROPE]
RETURNS
[Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg,Arg]
~ {
newInput: ROPE ← SubstituteVariables[ps.variables, input]; -- done now by InnerParse
a: ARRAY [0..18) OF Arg;
[a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], a[16], a[17]] ¬ Args.ArgsGetFromRope[input, format, FALSE ! Args.Error => ParseError[IO.PutFR["line actually interpreted: %g %g", [rope[ps.command]], [rope[input]]]]];
RETURN[a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], a[16], a[17]];
};
SubstituteVariables:
PUBLIC
PROC
[variables:
VariableSequence, input:
ROPE, rs: RandomStream ¬
NIL]
RETURNS [out: ROPE]
~ {
IDProc:
IO.BreakProc ~ {
RETURN[SELECT char FROM IN [IO.NUL..IO.SP], ',, ':, '), '( => sepr, ENDCASE => other];
};
OnePass:
PROC ~ {
in: STREAM ¬ IO.RIS[input];
out ¬ NIL;
DO
r: ROPE ¬ IO.GetTokenRope[in, IDProc ! IO.EndOfStream => EXIT].token;
out ¬ Rope.Cat[out, ExpandRope[in, variables, r, rs], " "];
ENDLOOP;
};
len: INT ¬ Rope.Length[input];
new: ROPE ¬ NIL;
start: INT ¬ 0;
DO
skipTo: INT ¬ Rope.SkipTo[input, start, ";"];
new ¬ Rope.Cat[new, Rope.Substr[input, start, skipTo-start]];
IF skipTo < len THEN new ¬ Rope.Cat[new, " ; "];
IF skipTo = len THEN EXIT;
start ¬ skipTo+1;
ENDLOOP;
DO
OnePass[];
IF Rope.Equal[input, out] THEN EXIT ELSE input ¬ out;
ENDLOOP;
};
ExpandRope:
PUBLIC
PROC [in:
STREAM, variables: VariableSequence, name:
ROPE, rs: RandomStream ¬
NIL]
RETURNS [
ROPE]
~ {
Eq: PROC [r1, r2: ROPE] RETURNS [b: BOOL] ~ {b ¬ Rope.Equal[r1, r2, FALSE]};
RandomUnit: PROC [] RETURNS [REAL] ~ { RETURN[REAL[Random.NextInt[rs]]/REAL[LAST[INT]]]; };
name ¬ LookupVariable[variables, name];
SELECT
TRUE
FROM
Eq[name, "RandomInt"] => {
lo: INT ¬ IO.GetInt[in];
hi: INT ¬ IO.GetInt[in];
v: INT ¬ Real.Round[lo + RandomUnit[] * (hi - lo)];
RETURN[IO.PutFR["%g", IO.int[v]]];
};
Eq[name, "RandomReal"] => {
lo: REAL ¬ IO.GetReal[in];
hi: REAL ¬ IO.GetReal[in];
v: REAL ¬ lo + RandomUnit[] * (hi - lo);
RETURN[IO.PutFR["%g", IO.real[v]]];
};
Eq[name, "RandomTriple"] => {
lo: REAL ¬ IO.GetReal[in];
hi: REAL ¬ IO.GetReal[in];
x: REAL ¬ 2.0 * (RandomUnit[] - 0.5);
y: REAL ¬ 2.0 * (RandomUnit[] - 0.5);
z: REAL ¬ 2.0 * (RandomUnit[] - 0.5);
len: REAL ¬ RealFns.SqRt[(x*x)+(y*y)+(z*z)];
newlen: REAL ¬ lo + RandomUnit[] * (hi - lo);
scl: REAL ¬ IF len # 0.0 THEN newlen / len ELSE 1.0;
RETURN[IO.PutFR["%g %g %g", IO.real[x*scl], IO.real[y*scl], IO.real[z*scl]]];
};
ENDCASE;
RETURN[name];
};
LookupVariable:
PUBLIC PROC [variables: VariableSequence, name:
ROPE]
RETURNS [
ROPE]
~ {
Eq: PROC [r1, r2: ROPE] RETURNS [b: BOOL] ~ {b ¬ Rope.Equal[r1, r2, FALSE]};
IF variables #
NIL
THEN
FOR i:
INT
IN [0..variables.length)
DO
IF Eq[variables[i].name, name] THEN RETURN[variables[i].value];
ENDLOOP;
RETURN[name];
};
Start Code
RegisterProc["--", Comment];
RegisterProc["Echo", Echo];
RegisterProc["Command", Command];
END.
..
By the Wayside
ViewerSelection:
TYPE ~
RECORD [viewer: Viewer, selection: SelPos];
ReadParameters:
PUBLIC
PROC [context3d:
Context3d,
fileName:
ROPE
¬
NIL, cmdOut:
STREAM ¬
NIL]
RETURNS [shouldRepaint: BOOL] ~ {
i: ReadInfo ¬
IF fileName =
NIL
THEN GetReaderInfo[]
ELSE [PFS.RopeOpen[fileName], NIL, NIL, fileName].rope;
RETURN[Parse[context3d, i.operations, cmdOut,,
TRUE, i.fileName, [i.v, i.sel]
! ParseError => CONTINUE]];
};
Parse:
PROC [
context3d: Context3d,
operation: ROPE,
cmdOut: STREAM ¬ NIL,
log: LogProc ¬ NIL,
showErrors: BOOL ¬ TRUE,
fileName: ROPE ¬ NIL,
viewerSelection: ViewerSelection ¬ [NIL, NIL]]
RETURNS [shouldRepaint: BOOL];
ParseError:
PUBLIC
ERROR [position:
NAT, fileName:
ROPE, viewer: Viewer] =
CODE;
This is a nice example of opening a Tioga file and setting a selection:
PositionAndRaiseError:
PROC [file,
out:
STREAM,
fileName,
err:
ROPE,
vs:
ViewerSelection]
~ {
position: INT ¬ IO.GetIndex[file];
IF vs.viewer #
NIL
AND vs.selection #
NIL
THEN {
position ¬ position+vs.selection.start;
vs.selection ¬ [position, 10];
ViewerTools.SetSelection[vs.viewer, vs.selection];
IO.PutRope[out, "Error during read"];
}
ELSE {
IF out # NIL THEN IO.PutF[out, "%g, file position = %g\n", IO.rope[err], IO.int[position]];
IF fileName #
NIL
THEN {
name: ROPE ¬ PFSNames.StripVersionNumber[PFS.FileInfo[fileName].fullFName];
v: Viewer ¬ ViewerOps.FindViewer[name];
IF v =
NIL
THEN {
v ¬ ViewerOps.CreateViewer[flavor: $Text, paint: FALSE, info: [iconic: TRUE, column: right, openHeight: 140]];
TiogaMenuOps.Load[v, fileName];
};
ViewerOps.OpenIcon[icon: v, bottom: FALSE, paint: FALSE];
ViewerTools.SetSelection[v,NEW[ViewerTools.SelPosRec¬[MAX[0,position-4],4,FALSE]]];
};
};
ParseError[position, fileName, vs.viewer];
};
GetRope:
PROC
RETURNS [r:
ROPE] ~ {
BreakProc: IO.BreakProc ~ { -- as default IO.TokenProc except '-, '/, '], '[ aren't separators
RETURN[SELECT char FROM
IN [Ascii.NUL..Ascii.SP], ',, ':, '; => sepr,
'[, '], '(, '), '{, '}, '", '+, '*, '@, '← => break,
ENDCASE => other];
};
CedarProcess.CheckAbort[];
Skip[];
r ¬ IO.GetTokenRope[in, IO.IDProc].token;
};
The following procs replaced by world coordinate transformations:
ScaleShape: SceneProc ~ {
IF ShapeOk[ps]
THEN {
s: REAL ¬ GetReal[IO.RIS[args]];
WriteLog[ps, IO.PutFR["Set %g scale to %g", [rope[ps.currentShape.name]], [real[s]]]];
G3dShape.TransformShape[shape: ps.currentShape, scale: s, concat: TRUE];
};
};
TranslateShape: SceneProc ~ {
IF ShapeOk[ps]
THEN {
t: Triple ¬ GetTriple[IO.RIS[args]];
WriteLog[ps,
IO.PutFR["%g trans:
(%g,
%g,
%g)",
[rope[ps.currentShape.name]], [real[t.x]], [real[t.y]], [real[t.z]]]];
G3dShape.TransformShape[shape: ps.currentShape, translate: t, concat: TRUE];
};
};
RotateShape: SceneProc ~ {
IF ShapeOk[ps]
THEN {
in: STREAM ¬ IO.RIS[args];
p0: Triple ¬ GetTriple[in];
p1: Triple ¬ GetTriple[in];
axis: G3dBasic.Ray ¬ [p0, G3dVector.Sub[p1, p0]];
r: REAL ¬ GetReal[in];
WriteLog[ps, IO.PutFR["%g rotate: %g", IO.rope[ps.currentShape.name], IO.real[r]]];
WriteLog[ps, Rope.Cat["Rotate ", ps.currentShape.name]];
G3dShape.TransformShape[shape: ps.currentShape, axis: axis, rotation: r, concat: TRUE];
};
};