ThreeDDemoImpl.mesa
Last Edited by: Crow, September 21, 1989 9:44:50 am PDT
Bloomenthal, September 26, 1988 12:04:57 pm PDT
DIRECTORY Atom, BasicTime, CedarProcess, Commander, FS, G3dAnimationSupport, G3dColorDisplaySupport, G3dRender, G3dRenderWithImager, G3dRenderWithPixels, G3dShape, G3dSortandDisplay, Imager, ImageTwiddle, List, Process, Random, Real, Rope, Terminal, ThreeDViewer, ViewerTools;
ThreeDDemoImpl: CEDAR MONITOR
IMPORTS Atom, BasicTime, CedarProcess, Commander, FS, G3dAnimationSupport, G3dColorDisplaySupport, G3dRender, G3dRenderWithImager, G3dRenderWithPixels, G3dShape, G3dSortandDisplay, ImageTwiddle, List, Process, Random, Real, Rope, Terminal, ThreeDViewer, ViewerTools
~ BEGIN
Types
PropList:   TYPE ~ Atom.PropList;
ROPE:    TYPE ~ Rope.ROPE;
Context:   TYPE ~ G3dRender.Context;
IntegerPair:  TYPE ~ G3dRender.IntegerPair;
Pair:    TYPE ~ G3dRender.Pair;
Pixel:    TYPE ~ G3dRender.Pixel;
Triple:   TYPE ~ G3dRender.Triple;
Rectangle:  TYPE ~ G3dRender.Rectangle;
RGB:    TYPE ~ G3dRender.RGB;
Patch:    TYPE ~ G3dRender.Patch;
ShadingClass: TYPE ~ G3dRender.ShadingClass;
Shape:   TYPE ~ G3dRender.Shape;
ShapeRep:  TYPE ~ G3dShape.ShapeRep;
ShapeSequence: TYPE ~ G3dRender.ShapeSequence;
ShapeSequenceRep: TYPE ~ G3dShape.ShapeSequenceRep;
RenderData:  TYPE ~ G3dRender.RenderData;
ImagerProcRec: TYPE ~ G3dRender.ImagerProcRec;
LORA:    TYPE ~ LIST OF REF ANY;
Global Variables
context3d, offScreenCtx: Context;  -- , onScreenCtx
currentScene, scene1, scene2, scene3: ShapeSequence ← NIL;
lastDemoCalled: PROC[makeFrame: BOOLEANFALSE];
frames: REF G3dAnimationSupport.FrameSequence;
xNow, yNow, zNow: REAL ← 3.0;
xPosition, yPosition: REAL ← 0.0;
cursorRange: REAL ← 64.0;
quadTime: REAL;
animation: BOOLEANFALSE;
movieProcess: CedarProcess.Process ← NIL;
cachedViewPort: Rectangle ← [0.0, 0.0, 0.0, 0.0];
windowStack, viewPortStack: LIST OF Rectangle ← NIL;
mouseWaiting: ATOMNIL;
mouseRectangle: REF Rectangle ← NIL;
Renamed Procedures
FindShape: PROC [context: Context, shapeName: ROPE] RETURNS [Shape]
~ G3dRender.FindShape;
GetAIS: PROC[ context: Context, fileRoot: ROPE, xOffset, yOffset: INTEGER ← 0,
     center: BOOLEANTRUE, labeled: BOOLEANFALSE ]
   RETURNS[ xSize, ySize: INTEGER]
~ G3dColorDisplaySupport.GetAIS;
GetProp: PROC [propList: PropList, prop: REF ANY] RETURNS [REF ANY]
~ Atom.GetPropFromList;
PutProp: PROC [propList: PropList, prop: REF ANY, val: REF ANY] RETURNS [PropList]
~ Atom.PutPropOnList;
Menu and Button Procs
demoMenu: LIST OF ThreeDViewer.ButtonDesc ~ LIST [
[ proc: SetScene,            -- Select Scene
choices: LIST[
[$Scene1, "Polyhedra on Checkerboard"],       -- left mouse button
[$Scene2, "Glass, Banana and Egg"],        -- middle mouse button
[$Scene3, "Bezier Teapot"],          -- right mouse button
[$OneShape, "Show One Shape (name from selection)"],  -- shift left
[NIL, NIL],               -- shift middle
[NIL, NIL],               -- shift right
[$Window, "2 Clicks in image define part of scene to display"],   -- control left
[$ViewPort, "2 Clicks in image define new screen area"],    -- control middle
[NIL, NIL],                 -- control right
[$RestoreW, "Restore previous window"],       -- control shift left
[$RestoreVP, "Restore previous viewport"]       -- control shift middle
],
label: "Select Shapes",
purpose: "Chooses scene to manipulate"
],
[ proc: SetShading,           -- Shading Style
choices: LIST[
[$Lines, "Line Drawing"],        -- left mouse button
[$Facets, "Faceted Shading"],        -- middle mouse button
[$Smooth, "Smooth Shading"],       -- right mouse button
[$Hidden, "Hidden Line Drawing"],     -- shift left
[$Shiny, "Faceted Shading with Highlights"],   -- shift middle
[$Hilights, "Smooth Shading with Highlights"],  -- shift right
[$SmoLines, "Smooth Shading on Lines"],    -- control left
[$Normals, "Line drawing with normal vectors"]  -- control middle
],
label: "Shading Style",
purpose: "Chooses shading for surfaces in current scene"
],
[ proc: SetRenderingStyle,         -- Rendering Style
choices: LIST[
[$Jaggy, "Aliased"],           -- left mouse button
[$NoJaggy, "AntiAliased"],        -- middle mouse button
[NIL, NIL],             -- right mouse button
[$Visible, "Build Image On Display"],      -- shift left
[$Buffered, "Double Buffer Image"],       -- shift middle
[NIL, NIL],             -- shift right
[$Imager, "Use Imager (faceted only)"],      -- control left
[$Pixels, "Use fancier tiler/shaders"],      -- control middle
[NIL, NIL],             -- control right
[$ZBuffer, "Use depth buffer for hidden surfaces"],  -- control shift left
[$Sorted, "Use sorting for hidden surfaces"],    -- control shift middle
[NIL, NIL],             -- control shift right
[$PseudoClr, "8-bit mapped color"],
[$Gray, "8-bit grayscale"],
[$FullClr, "24-bit full color"]
],
label: "Rendering Style",
purpose: "Chooses display type, antialising, and double buffering"
],
[ proc: DoCommands,           -- Commands
choices: LIST[
[$Display, "Make a frame with the current scene"],    -- left mouse button
[$StoreImage, "Store image in file (name from selection)"],  -- middle mouse button
[$Interpress, "Interpress file (name from selection)"]    -- right mouse button
],
label: "Do It",
purpose: "Make/Store images"
],
[ proc: ShowMotion,           -- Animation
choices: LIST[
[$Orbit, "Orbit about scene computing frames on the fly"],  -- left mouse button
[$SelectDthr, "Take 8-bit dithered sequence name from selection"], -- middle button
[$SelectGray, "Take 8-bit grayscale sequence name from selection"], -- right button
[$Shapes, "Play back Banana, Glass, Egg scene"],     -- shift left
[$Terrain, "Play back Terrain flyover"],       -- shift middle
[NIL, NIL],               -- shift right
[$Web1, "Play back front view Paper web"],     -- control left
[$Web2, "Play back top view Paper web"],       -- control middle
],
label: "Animation",
purpose: "Chooses various animations"
],
[ proc: ShowPicture,           -- Gallery
choices: LIST[ 
[$SelectColor, "Take rgb file name from selection"],     -- left mouse button
[$SelectDthr, "Take 8-bit dithered file name from selection"], -- middle mouse button
[$SelectGray, "Take 8-bit grayscale file name from selection"], -- right mouse button
[$Spoon, "Newell's Bezier patch spoon"],    -- shift left
[$Bananas, "A field of bananas"],      -- shift middle
[$Compare, "Shading Comparison on Eggs"],   -- shift right
[$Bowl, "Sugar bowl with spoon"],      -- control left
[$TeaPot, "Teapot with Perlin texture"],     -- control middle
[$Eggtxtr, "Multi-textured Egg"],      -- control right
[$Heart, "Heart from two Bezier patches"],    -- control shift left
[$Objects, "Five objects on a checkerboard"],   -- control shift middle
[$Extents, "Five objects with bounding boxes"],  -- control shift right
[$HexTiles, "Objects on hex-tiled plane"],
[$MikHead, "Mik leers over the curvaceous glass"],
[$MikTrees, "Mik leers over scanned-in trees"],
[$BallPage, "A sphere with a page of text"],
[$BallPages, "A sphere with many pages of text"],
[$TeaPages, "Teapot with pages of text"],
[$SolidLines, "Drawing with fat lines"]
],
label: "Gallery",
purpose: "Chooses a pretty picture to display"
],
[ proc: StopEverything,          -- STOP! button
choices: LIST[],      -- No Choices, just calls proc
label: "STOP!",
purpose: "Panic button, stops all 3D activity"
],
[ proc: DoReset,            -- Reset button
choices: LIST[],      -- No Choices, just calls proc
label: "Reset",
purpose: "Reset button, can often get things unwedged"
]
];
SetScene: ENTRY PROC[context: Context, key: ATOM] ~ {
context.stopMe^ ← FALSE;      -- unstick stop button
{ ENABLE UNWIND => NULL; -- get out and release lock if error, etc.
SELECT key FROM
$Scene1 => SetScene1[];
$Scene2 => SetScene2[];
$Scene3 => SetScene3[];
$OneShape => OneShape[];
$Window => GetWindow[];
$ViewPort => GetViewPort[];
$RestoreW => RestoreWindow[];
$RestoreVP => RestoreViewPort[];
ENDCASE;
};
};
SetShading: ENTRY PROC[context: Context, key: ATOM] ~ {
context.stopMe^ ← FALSE;      -- unstick stop button
{ ENABLE UNWIND => NULL;
SELECT key FROM
$Lines => LinesDemo[];
$Facets => { SetDull[]; FacetedDemo[]; };
$Smooth => { SetDull[]; SmoothDemo[]; };
$Hidden => HiddenLinesDemo[];
$Shiny => { SetShiny[]; FacetedDemo[]; };
$Hilights => { SetShiny[]; SmoothDemo[]; };
$SmoLines => IF context.class.displayType # $PseudoColor
OR ThreeDViewer.SwitchDisplayTo[context, $FullColor]
THEN {       -- only for $Gray and $FullColor
context.preferredRenderMode ← $Pixels;
ShadedLinesDemo[];
};
$Normals => NormaledLinesDemo[];
ENDCASE;
};
};
SetRenderingStyle: ENTRY PROC[context: Context, key: ATOM] ~ {
context.stopMe^ ← FALSE;      -- unstick stop button
SELECT key FROM
$Jaggy  => G3dRenderWithPixels.AntiAliasing[context, FALSE];
$Visible  => G3dRenderWithPixels.BufferRendering[context, FALSE];
$Imager  => context.preferredRenderMode ← $Imager;
$NoJaggy  => IF context.class.displayType # $PseudoColor
OR ThreeDViewer.SwitchDisplayTo[context, $FullColor]
THEN {
context.preferredRenderMode ← $Pixels;
G3dRenderWithPixels.AntiAliasing[context, TRUE];
};
$Buffered => G3dRenderWithPixels.BufferRendering[context, TRUE];
$Pixels  => context.preferredRenderMode ← $Pixels;
$ZBuffer => G3dRenderWithPixels.DepthBuffering[context, TRUE];
$Sorted  => G3dRenderWithPixels.DepthBuffering[context, FALSE];
$PseudoClr => [] ← ThreeDViewer.SwitchDisplayTo[context, $PseudoColor];
$Gray   => [] ← ThreeDViewer.SwitchDisplayTo[context, $Gray];
$FullClr  => [] ← ThreeDViewer.SwitchDisplayTo[context, $FullColor];
ENDCASE;
{ ENABLE UNWIND => NULL;
ThreeDViewer.DrawInViewer[context, NIL];
};
};
DoCommands: ENTRY PROC[context: Context, key: ATOM] ~ {
context.stopMe^ ← FALSE;      -- unstick stop button
{ ENABLE UNWIND => NULL;
SELECT key FROM
$Display => {
IF DemoErrorMessages[] THEN RETURN;
SELECT lastDemoCalled FROM
LinesDemo   => MakeFrame[];
ShadedLinesDemo => MakeFrame[];
HiddenLinesDemo => HiddenLinesDemo[TRUE];
NormaledLinesDemo => NormaledLinesDemo[TRUE];
FacetedDemo => MakeFrame[];
SmoothDemo => MakeFrame[];
ENDCASE  => MakeFrame[];
};
$StoreImage => G3dColorDisplaySupport.PutAIS[
           context3d, ViewerTools.GetSelectionContents[] ];
$Interpress => InterpressDemo[];
ENDCASE;
};
};
ShowMotion: ENTRY PROC[context: Context, key: ATOM] ~ {
SetSingleBuffer: PROC[] ~ {
G3dRenderWithPixels.AntiAliasing[context, FALSE];
G3dRenderWithPixels.DepthBuffering[context, FALSE];
};
context.stopMe^ ← FALSE;      -- unstick stop button
{ ENABLE UNWIND => NULL;
IF key = $Orbit OR context.class.displayType # $FullColor
OR ThreeDViewer.SwitchDisplayTo[context, $PseudoColor] -- ensure 8-bit display
THEN SELECT key FROM
$Orbit => Orbit[];
$SelectDthr => {
[] ← ThreeDViewer.SwitchDisplayTo[context, $PseudoColor]; SetSingleBuffer[];
SetUpMovie[ fileRoot: ViewerTools.GetSelectionContents[] ];
};
$SelectGray => {
[] ← ThreeDViewer.SwitchDisplayTo[context, $Gray]; SetSingleBuffer[];
SetUpMovie[ fileRoot: ViewerTools.GetSelectionContents[] ];
};
$Shapes => {
[] ← ThreeDViewer.SwitchDisplayTo[context, $PseudoColor]; SetSingleBuffer[];
SetUpMovie[
fileRoot: "/Pixel/Crow/Animation/StillCloser.ais", numFiles: 40, start: 1
];
};
$Terrain => {
[] ← ThreeDViewer.SwitchDisplayTo[context, $PseudoColor]; SetSingleBuffer[];
SetUpMovie[ fileRoot: "/Cyan/AIS/Animation/LakeSceneTest.ais", numFiles: 64 ];
ImageTwiddle.SetUpTerrainColors[context.terminal];
};
$Web1 => {
[] ← ThreeDViewer.SwitchDisplayTo[context, $Gray]; SetSingleBuffer[];
SetUpMovie[ fileRoot: "/Pixel/Crow/Animation/Paper4Web.ais", numFiles: 40 ];
};
$Web2 => {
[] ← ThreeDViewer.SwitchDisplayTo[context, $Gray]; SetSingleBuffer[];
SetUpMovie[ fileRoot: "/Pixel/Crow/Animation/Paper5Web.ais", numFiles: 40 ];
};
ENDCASE;
IF key # $Orbit THEN movieProcess ← CedarProcess.Fork[PlayBackbyFrame];
};
};
ShowPicture: ENTRY PROC[context: Context, key: ATOM] ~ {
context.stopMe^ ← FALSE;      -- unstick stop button
{ ENABLE UNWIND => NULL;
SELECT key FROM
$SelectDthr => IF context.class.displayType # $PseudoColor THEN
IF ThreeDViewer.SwitchDisplayTo[context, $PseudoColor] THEN -- switch to dithered
{ [] ← CedarProcess.Fork[ WaitThenShowPicture, LIST[context, key] ]; RETURN; };
$SelectGray => IF context.class.displayType # $Gray THEN
IF ThreeDViewer.SwitchDisplayTo[context, $Gray] THEN -- switch to 8-bit gray
{ [] ← CedarProcess.Fork[ WaitThenShowPicture, LIST[context, key] ]; RETURN; };
ENDCASE => IF context.class.displayType # $FullColor THEN
IF ThreeDViewer.SwitchDisplayTo[context, $FullColor] THEN-- switch to full color
{ [] ← CedarProcess.Fork[ WaitThenShowPicture, LIST[context, key] ]; RETURN; };
SELECT key FROM
$SelectDthr, $SelectGray, $SelectColor =>
     [] ← GetAIS[context3d, ViewerTools.GetSelectionContents[] ];
$Spoon  => [] ← GetAIS[context3d, "/cyan/AIS/Crow/Spoon.ais" ];
$Bananas  => [] ← GetAIS[context3d, "/cyan/AIS/Crow/FieldOfBanana.ais" ];
$Compare  => [] ← GetAIS[context3d, "/cyan/AIS/Crow/ComparisonEggs.ais" ]; 
$Bowl   => [] ← GetAIS[context3d, "/cyan/AIS/Crow/BowlAndSpoon.ais" ];
$TeaPot   => [] ← GetAIS[context3d, "/cyan/AIS/Crow/ZebraBurlTeaPot.ais" ];
$Eggtxtr  => [] ← GetAIS[context3d, "/cyan/AIS/Crow/MultiTxtrdEgg2.ais" ];
$Heart  => [] ← GetAIS[context3d, "/cyan/AIS/Crow/Heart.ais" ];
$Objects  => [] ← GetAIS[context3d, "/cyan/AIS/Crow/TestScene2.ais" ];
$Extents  => [] ← GetAIS[context3d, "/cyan/AIS/Crow/FiveObjWBoxes.ais" ];
$HexTiles  => [] ← GetAIS[context3d, "/cyan/AIS/Crow/HexTiles.ais" ];
$MikHead => [] ← GetAIS[context3d, "/cyan/AIS/Crow/Lamming6Scene.ais" ];
$MikTrees => [] ← GetAIS[context3d, "/cyan/AIS/Crow/Lamming7Scene.ais" ];
$BallPage  => [] ← GetAIS[context3d, "/cyan/AIS/Crow/SphereWithPage.ais" ];
$BallPages  => [] ← GetAIS[context3d, "/cyan/AIS/Crow/SphereWithPages.ais" ];
$TeaPages => [] ← GetAIS[context3d, "/cyan/AIS/Crow/PagedTeaPot.ais" ];
$SolidLines => [] ← GetAIS[context3d, "/cyan/AIS/Crow/StillLines.ais" ];
ENDCASE
};
};
WaitThenShowPicture: CedarProcess.ForkableProc ~ {
PROC [data: REF] RETURNS [results: REFNIL];
Forking this process frees button, reducing likelihood of deadlocks, at least I think that's why this is done this way
list: LORANARROW[data];
context: ContextNARROW[list.first];
key: ATOMNARROW[list.rest.first];
WHILE GetProp[ context.displayProps, $ViewerAdjusted ] # NIL DO
Process.PauseMsec[ 500 ]; ENDLOOP;    -- wait for viewer paint proc to complete
ShowPicture[context, key];
};
StopEverything: PROC[context: Context, key: ATOM] ~ {    -- panic stop
bkgrdContext: ContextNIL;
WITH GetProp[context.props, $BackGround] SELECT FROM
bkGrdCtx: Context => bkgrdContext ← bkGrdCtx;
ENDCASE;
context.stopMe^ ← TRUE;
WHILE bkgrdContext # NIL DO    -- stop chain of background contexts
tmpCtx: Context ← bkgrdContext;
tmpCtx.stopMe^ ← TRUE;
bkgrdContext ← NARROW[GetProp[tmpCtx.props, $BackGround]];
ENDLOOP;
IF movieProcess # NIL THEN {
Process.Yield[];
CedarProcess.Abort[movieProcess];
movieProcess ← NIL;
Process.Yield[];
G3dRender.SetViewPort[ context, cachedViewPort ];
};
};
DoReset: PROC [context: Context, key: ATOM] ~ { -- get out of stuckness, start over
StopEverything[context, NIL];
Process.PauseMsec[ 3000 ];       -- wait 3 seconds for processes to stop
context3d.stopMe^ ← FALSE;       -- get stop flag unstuck, if necessary
context3d.window ← NIL; 
context3d.shapes ← context3d.visibleShapes ← NIL; 
context3d.lightSources ← NIL;
G3dRender.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
G3dRender.AddLight[context3d, "Light0", [-100., -200., 50.] ];
G3dRender.SetViewPort[context3d, [0.0, 0.0, 65536.0, 65536.0] ];
windowStack ← viewPortStack ← NIL;
mouseWaiting ← NIL;
mouseRectangle ← NIL;
currentScene ← NIL;
frames ← NIL;
context3d.props ← Atom.RemPropFromList[ context3d.props, $SinglePatch ];
context3d.props ← Atom.RemPropFromList[ context3d.props, $SingleVtx ];
};
Context and Interactive Control
ThreeDDemoCommand: Commander.CommandProc ~ {
context3d ← G3dRender.Create[];
context3d.preferredRenderMode ← $Pixels;  -- $Pixels, Imager;
ThreeDViewer.MakeViewer[
context: context3d,
displayType: $PseudoColor,    -- $Gray, ImagerDithered
bannerName: "ThreeDWorld Demonstration",
menu: demoMenu,
mouseAction: MouseControl,
verticalMenu: FALSE
];
G3dRender.NameBackgroundColor[ context3d, "Darkish Blue" ]; -- set background color
G3dRender.NameAmbientLight[ context3d, "Darkish Gray" ]; -- set ambient light
G3dRender.AddLight[context3d, "Light0", [-100., -200., 50.] ];
G3dRender.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
};
InitNoDisplay: PROC[] ~ {
context3d ← G3dRender.Create[];
G3dRender.NameBackgroundColor[ context3d, "Darkish Blue" ]; -- set background color
G3dRender.NameAmbientLight[ context3d, "Darkish Gray" ]; -- set ambient light
G3dRender.AddLight[context3d, "Light0", [-100., -200., 50.] ];
G3dRender.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
};
InitNoViewer: PROC[] ~ {
context3d ← G3dRender.Create[];
G3dRender.LoadDisplayClass[context3d, $PseudoColor];
G3dRender.NameBackgroundColor[ context3d, "Darkish Blue" ]; -- set background color
G3dRender.NameAmbientLight[ context3d, "Darkish Gray" ]; -- set ambient light
G3dRender.AddLight[context3d, "Light0", [-100., -200., 50.] ];
G3dRender.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
};
InitNoViewerFullColor: PROC[] ~ {
context3d ← G3dRender.Create[];
G3dRender.LoadDisplayClass[context3d, $FullColor];
G3dRender.NameBackgroundColor[ context3d, "Darkish Blue" ]; -- set background color
G3dRender.NameAmbientLight[ context3d, "Darkish Gray" ]; -- set ambient light
G3dRender.AddLight[context3d, "Light0", [-100., -200., 50.] ];
G3dRender.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
};
Reset: PROCEDURE [] ~ {
DoReset[context3d, NIL];
};
MouseControl: ThreeDViewer.MouseProc ~ {
PROC[context: Context, bttn, ctlShft: ATOM, x, y: REAL]
bttn in { $LeftButton, $LeftHeld, $MiddleButton, $MiddleHeld, $RightButton, $RightHeld }
ctlShft in { NIL, $Shift, $Control, $ControlShift }
This will be called whenever the mouse is moved or a mouse button is pushed.
width, height: INTEGER;
divisor: REAL;
IF mouseWaiting # NIL
THEN { IF bttn = $LeftButton THEN GetMouseRectangle[x, y]; RETURN; };
IF context.viewer # NIL
THEN { width ← context.viewer.ww; height ← context.viewer.wh; }
ELSE { width ← context.pixels.box.max.f - context.pixels.box.min.f;
   height ← context.pixels.box.max.s - context.pixels.box.min.s; };
divisor ← ((width/2.0) * (width/2.0)) / cursorRange;
x ← x - width / 2.0;    -- center mouse coords
y ← y - height / 2.0;
SELECT bttn FROM
$LeftButton, $LeftHeld => IF movieProcess # NIL
THEN frameRate ← INTEGER[Real.Fix[x / 10.]]
ELSE {
xNow ← (x * ABS[x] / divisor) + context3d.lookAt.x;
yNow ← (y * ABS[y] / divisor) + context3d.lookAt.y;
G3dRender.SetView[ context, [xNow, yNow, zNow], context.lookAt, context.fieldOfView, context.rollAngle, context.upDirection, context.hitherLimit, context.yonLimit ];
DoCommands[context, $Display];
};
$MiddleButton, $MiddleHeld => {
xLight: REAL ← x * 1.0;   -- roughly plus or minus 300-500
yLight: REAL ← y * 1.0;
[] ← G3dRender.AddLight[ context, "Light0", [xLight, yLight, 50.0] ];
DoCommands[context, $Display];
};
$RightButton, $RightHeld => {
zNow ← (y * ABS[y] / divisor) + context3d.lookAt.z;
G3dRender.SetView[ context, [xNow, yNow, zNow], context.lookAt, context.fieldOfView, context.rollAngle, context.upDirection, context.hitherLimit, context.yonLimit ];
DoCommands[context, $Display];
};
ENDCASE;
};
MoveToCursor: PROC[] ~ {
width, height, offset: INTEGER;
sPos: IntegerPair;
IF context3d.viewer # NIL
THEN {
width ← context3d.viewer.ww; height ← context3d.viewer.wh; offset ← 28;
}
ELSE {
width ← context3d.pixels.box.max.f - context3d.pixels.box.min.f; 
height ← context3d.pixels.box.max.s - context3d.pixels.box.min.s;
offset ← context3d.pixels.box.min.s
};
[[sPos.x, sPos.y]] ← Terminal.GetColorCursorPosition[context3d.terminal];
xPosition ← 1.0 * sPos.x / width;
yPosition ← 1.0 * (height - sPos.y + offset) / height;
};
DrawToCursor: PROC[ color: RGB ← [1.0,1.0,1.0] ] ~ {
width, height, offset: INTEGER;
sPos: IntegerPair;
newPos: Pair;
clrPxl: Pixel ← [Real.Fix[color.R*255], Real.Fix[color.G*255], Real.Fix[color.B*255], 0, 0];
IF context3d.viewer # NIL
THEN {
width ← context3d.viewer.ww; height ← context3d.viewer.wh; offset ← 28;
}
ELSE {
width ← context3d.pixels.box.max.f - context3d.pixels.box.min.f; 
height ← context3d.pixels.box.max.s - context3d.pixels.box.min.s;
offset ← context3d.pixels.box.min.s
};
[[sPos.x, sPos.y]] ← Terminal.GetColorCursorPosition[context3d.terminal];
newPos ← [1.0 * sPos.x / width, 1.0 * (height - sPos.y + offset) / height];
context3d.class.draw2DLine[ context3d, [xPosition, yPosition], newPos, clrPxl ];
xPosition ← newPos.x;
yPosition ← newPos.y;
};
TextAtCursor: PROC[ rope: ROPE, color: RGB ← [1.0,1.0,1.0], size: REAL ← 20.0,
       font: ROPENIL ] ~ {
sPos: IntegerPair;
pos: Pair;
width, height, offset: INTEGER;
clrPxl: Pixel ← [Real.Fix[color.R*255], Real.Fix[color.G*255], Real.Fix[color.B*255], 0, 0];
IF context3d.viewer # NIL
THEN {
width ← context3d.viewer.ww; height ← context3d.viewer.wh; offset ← 28;
}
ELSE {
width ← context3d.pixels.box.max.f - context3d.pixels.box.min.f; 
height ← context3d.pixels.box.max.s - context3d.pixels.box.min.s;
offset ← context3d.pixels.box.min.s
};
[[sPos.x, sPos.y]] ← Terminal.GetColorCursorPosition[context3d.terminal];
pos ← [1.0 * sPos.x / width, 1.0 * (height - sPos.y + offset) / height];
IF font = NIL THEN font ← "Xerox/Pressfonts/TimesRoman-MRR";
context3d.class.draw2DRope[context3d, rope, pos, clrPxl, size, font];
};
GetOffScreenCtx: PROC[context: Context, width, height: NAT] ~ {
offScreenCtx ← G3dRender.GetTmpContext[context];
offScreenCtx.viewer ← NIL;
offScreenCtx.viewPort ← NEW[     -- set viewport directly to define pixelMap size
Rectangle ← [ x: 0.0, y: 0.0, w: Real.Float[width], h: Real.Float[height] ]
];
G3dRenderWithPixels.AllocatePixelMemory[offScreenCtx];     -- get display memory
G3dRender.SetViewPort[ offScreenCtx, [0.0, 0.0, Real.Float[width], Real.Float[height]] ];
IF context.antiAliasing THEN G3dRenderWithPixels.AntiAliasing[offScreenCtx];-- get state straight
IF context.depthBuffering THEN G3dRenderWithPixels.DepthBuffering[offScreenCtx];
[] ← G3dRender.StartLog[offScreenCtx];
offScreenCtx.changed ← TRUE;
G3dSortandDisplay.ValidateContext[offScreenCtx];
G3dSortandDisplay.ValidateContext[context];   -- just in case it hasn't been used yet
};
GetOnScreenCtx: PROC[context: Context, width, height: NAT] ~ {
Makes a new view of the supplied context, useful for using two portions of the screen differently, etc.
onScreenCtx: Context ← G3dRender.GetTmpContext[context];
G3dRender.SetViewPort[ onScreenCtx, [0.0, 0.0, Real.Float[width], Real.Float[height]] ];
onScreenCtx.class.render ← G3dRenderWithPixels.MakeFrame;  -- load direct memory procs
onScreenCtx.class.displayPolygon ← G3dRenderWithPixels.PolygonTiler;
IF context.antiAliasing THEN G3dRenderWithPixels.AntiAliasing[onScreenCtx];
IF context.depthBuffering THEN G3dRenderWithPixels.DepthBuffering[onScreenCtx];
onScreenCtx.changed ← TRUE;
G3dSortandDisplay.ValidateContext[onScreenCtx];
G3dSortandDisplay.ValidateContext[context];   -- just in case it hasn't been used yet
};
Demos
DemoErrorMessages: PROCEDURE [] RETURNS[ error: BOOLEANFALSE ] ~ {
NoSurfaces: PROCEDURE [shapes: ShapeSequence] RETURNS[BOOLEAN]~{
FOR i: NAT IN [0..shapes.length) DO
IF shapes[i].vertices # NIL AND shapes[i].surfaces # NIL THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
};
IF context3d.shapes = NIL OR NoSurfaces[context3d.shapes] THEN SetScene1[];
IF context3d.shapes = NIL OR NoSurfaces[context3d.shapes] THEN {
context3d.class.draw2DRope[context3d, "No objects, nothing to display", [60, 340] ];
error ← TRUE;
};
IF context3d.antiAliasing
AND NOT List.Memb[ context3d.class.displayType, LIST[$Gray, $FullColor] ]
THEN {
context3d.class.draw2DRope[context3d,
        "NoJaggy needs GrayScale or 24-bit Color", [60, 300] ];
error ← TRUE;
};
};
LinesDemo: PROCEDURE [makeFrame: BOOLEANFALSE] ~ {
IF DemoErrorMessages[] THEN RETURN;
FOR i: NAT IN [0..context3d.shapes.length) DO
G3dRender.SetRenderStyle[ FindShape[context3d, context3d.shapes[i].name], lines ];
ENDLOOP;
context3d.changed ← TRUE;
IF makeFrame THEN context3d.class.render[context3d];
lastDemoCalled ← LinesDemo;
};
ShadedLinesDemo: PROCEDURE [makeFrame: BOOLEANFALSE] ~ {
IF DemoErrorMessages[] THEN RETURN;
FOR i: NAT IN [0..context3d.shapes.length) DO
G3dRender.SetRenderStyle[ FindShape[context3d, context3d.shapes[i].name], shadedLines ];
ENDLOOP;
context3d.changed ← TRUE;
IF makeFrame THEN context3d.class.render[context3d];
lastDemoCalled ← ShadedLinesDemo;
};
HiddenLinesDemo: PROCEDURE [makeFrame: BOOLEANFALSE] ~ {
tmpContext: Context ← G3dRender.GetTmpContext[context3d]; -- get modifiable context
IF DemoErrorMessages[] THEN RETURN;
G3dRender.NameBackgroundColor[ tmpContext, "White" ];
G3dRender.CopyContextShapes[tmpContext, context3d]; -- insulate shapes from changes
FOR i: NAT IN [0..tmpContext.shapes.length) DO
G3dRender.SetRenderStyle[
FindShape[tmpContext, tmpContext.shapes[i].name], hiddenLines
];
ENDLOOP;
context3d.changed ← TRUE;
IF makeFrame THEN tmpContext.class.render[tmpContext];
lastDemoCalled ← HiddenLinesDemo;
};
NormaledLinesDemo: PROCEDURE [makeFrame: BOOLEANFALSE] ~ {
tmpContext: Context ← G3dRender.GetTmpContext[context3d]; -- get modifiable context
IF DemoErrorMessages[] THEN RETURN;
G3dRender.NameBackgroundColor[ tmpContext, "White" ];
G3dRender.CopyContextShapes[tmpContext, context3d]; -- insulate shapes from changes
FOR i: NAT IN [0..tmpContext.shapes.length) DO
G3dRender.SetColor[ FindShape[tmpContext, tmpContext.shapes[i].name], [1.0, 1.0, 1.0] ];
G3dRender.SetRenderStyle[
FindShape[tmpContext, tmpContext.shapes[i].name], hiddenLines
];
ENDLOOP;
context3d.changed ← TRUE;
IF makeFrame THEN tmpContext.class.render[tmpContext];
lastDemoCalled ← NormaledLinesDemo;
};
FacetedDemo: PROCEDURE [makeFrame: BOOLEANFALSE] ~ {
IF DemoErrorMessages[] THEN RETURN;
FOR i: NAT IN [0..context3d.shapes.length) DO
G3dRender.SetRenderStyle[ FindShape[context3d, context3d.shapes[i].name], faceted ];
ENDLOOP;
context3d.changed ← TRUE;
IF makeFrame THEN context3d.class.render[context3d];
lastDemoCalled ← FacetedDemo;
};
SmoothDemo: PROCEDURE [makeFrame: BOOLEANFALSE] ~ {
IF DemoErrorMessages[] THEN RETURN;
FOR i: NAT IN [0..context3d.shapes.length) DO
G3dRender.SetRenderStyle[ FindShape[context3d, context3d.shapes[i].name], smooth ];
ENDLOOP;
context3d.changed ← TRUE;
lastDemoCalled ← SmoothDemo;
IF makeFrame THEN context3d.class.render[context3d];
};
SetShiny: PROCEDURE [] ~ {
FOR i: NAT IN [0..context3d.shapes.length) DO
G3dRender.SetShininess[ FindShape[context3d, context3d.shapes[i].name], 50.0 ];
ENDLOOP;
context3d.changed ← TRUE;
};
SetDull: PROCEDURE [] ~ {
Remove Highlights
FOR i: NAT IN [0..context3d.shapes.length) DO
G3dRender.SetShininess[ FindShape[context3d, context3d.shapes[i].name], 0.0 ];
ENDLOOP;
context3d.changed ← TRUE;
};
InterpressDemo: PROCEDURE [] ~ {
fileName: Rope.ROPE ← ViewerTools.GetSelectionContents[];
IF Rope.Length[fileName] < 1 THEN {
SIGNAL G3dRender.Error[$MisMatch, "No selection for file name"];
RETURN;
};
IF DemoErrorMessages[] THEN RETURN;
IF lastDemoCalled # HiddenLinesDemo AND lastDemoCalled # NormaledLinesDemo
THEN
G3dRenderWithImager.MakeInterpressPage[context3d, fileName ]
ELSE {
tmpContext: Context ← G3dRender.GetTmpContext[context3d]; -- get mod. context
context3d.changed ← TRUE;
G3dRender.NameBackgroundColor[ tmpContext, "White" ];
G3dRender.CopyContextShapes[tmpContext, context3d]; -- insulate shapes from change
FOR i: NAT IN [0..context3d.shapes.length) DO
G3dRender.SetColor[ FindShape[tmpContext, tmpContext.shapes[i].name], [1.0,1.0,1.0] ];
IF G3dRender.ShapeClassFrom[tmpContext.shapes[i]].type # $Light  -- avoid lights
THEN IF lastDemoCalled # NormaledLinesDemo
THEN G3dRender.SetRenderStyle[
   FindShape[tmpContext, tmpContext.shapes[i].name], hiddenLines ]
ELSE G3dRender.SetRenderStyle[
   FindShape[tmpContext, tmpContext.shapes[i].name], linesWnormals ];
ENDLOOP;
G3dRenderWithImager.MakeInterpressPage[tmpContext, fileName ]
};
};
RandomQuads: PROCEDURE [context: Context] ~ {
x, y: REAL;
time: REAL ← BasicTime.PulsesToSeconds[BasicTime.GetClockPulses[]];
width: REAL ← context.viewPort.w; height: REAL ← context.viewPort.h;
aspectRatio: REAL ← height / width;
rs: Random.RandomStream ← Random.Create[Real.Round[width]]; -- limit to display width
p: REF Patch ← NEW[ Patch[4] ];
shape: Shape ← NEW[ShapeRep];
renderData: REF RenderData ← G3dRender.RenderDataFrom[shape];
renderData.shadingClass ← NEW[ShadingClass];
renderData.shadingClass.renderMethod ← $Smooth;
p.props ← PutProp[ p.props, $Shape, shape ];
p.nVtces ← 4;
FOR i: NAT IN [0..1000) DO
IF context.stopMe^ THEN EXIT;
x ← Random.NextInt[rs]; y ← Random.NextInt[rs] * aspectRatio;
FOR j: NAT IN [0..4) DO
xSgn, ySgn: INTEGER;
xSgn ← IF j < 1 OR j > 3 THEN -1 ELSE 1; ySgn ← IF j < 2 THEN 1 ELSE -1;
p[j].coord.sx ← MAX[0, MIN[ width-1, x + xSgn * Random.NextInt[rs] * 0.1]];
p[j].coord.sy ← MAX[0, MIN[ height-1, y+ySgn * Random.NextInt[rs] *0.1 *aspectRatio]];
p[j].shade.er ← Random.NextInt[rs] / width;
p[j].shade.eg ← Random.NextInt[rs] / width;
p[j].shade.eb ← Random.NextInt[rs] / width;
ENDLOOP;
[] ← context.class.displayPolygon[context, p];
ENDLOOP;
quadTime ← BasicTime.PulsesToSeconds[BasicTime.GetClockPulses[]] - time;
};
Scene Definition
SetScene1: PROCEDURE [] ~ {
IF scene1 = NIL THEN {
Reset[];
G3dRender.AddShapeFromFile[ context3d,
"CheckerBoard",        -- internal name
"CheckerBoard.shape",       -- file name
[-8.0, -8.0, -2.0]        -- position
];
G3dRender.SetColor[ FindShape[context3d, "CheckerBoard"], [.8, .4, .2] ]; -- add shading
G3dRender.AddShapeFromFile[ context3d,
           "Icosahedron", "Icosahedron.shape", [2.0, 0.0, 0.0] ];
G3dRender.SetColor[ FindShape[context3d, "Icosahedron"], [1., 1., 1.] ];
G3dRender.AddShapeFromFile[ context3d, "SoccerBall", "SoccerBall.shape", [-2.0, 0.0, 0.0] ];
G3dRender.SetColor[ FindShape[context3d, "SoccerBall"], [1., 1., 1.] ];
G3dRender.AddShapeFromFile[ context3d, "CutCube", "CutCube.shape", [0.0, 3.0, 0.0] ];
G3dRender.SetColor[ FindShape[context3d, "CutCube"], [.9, .8, .5] ];
scene1 ← NEW[ShapeSequenceRep[context3d.shapes.length]];
FOR i: NAT IN [0..context3d.shapes.length) DO
scene1[i] ← context3d.shapes[i];
ENDLOOP;
scene1.length ← context3d.shapes.length;
}
ELSE {
context3d.shapes ← NEW[ShapeSequenceRep[scene1.length]];
FOR i: NAT IN [0..scene1.length) DO
context3d.shapes[i] ← scene1[i];
ENDLOOP;
context3d.shapes.length ← scene1.length;
};
G3dRenderWithPixels.DepthBuffering[context3d, FALSE];
currentScene ← scene1;
};
SetScene2: PROCEDURE [] ~ {
IF scene2 = NIL THEN {
Reset[];
G3dRender.AddShapeFromFile[context3d, "ChampagneGlass", "ChampagneGlass.shape",
                      [2.0, 0.0, 0.0] ];
G3dRender.SetColor[ FindShape[context3d, "ChampagneGlass"], [1.,0.,1.] ];
G3dRender.SetTransmittance[ FindShape[context3d, "ChampagneGlass"], 0.8 ];
G3dRender.AddShapeFromFile[context3d, "UtahEgg", "UtahEgg.shape" ];
G3dShape.TransformShape[ shape: FindShape[context3d, "UtahEgg"], translate: [-2.,.0,.0],
        axis: [[0.0, 0.0, 0.0], [1.0, 1.0, 0.0]], rotation: 90.0 ];
G3dRender.SetColor[ FindShape[context3d, "UtahEgg"], [1.0, .8, .5] ];
G3dRender.AddShapeFromFile[context3d, "Banana", "Banana.shape" ];
G3dRender.SetColor[ FindShape[context3d, "Banana"], [1.0, .9, .1] ];
scene2 ← NEW[ShapeSequenceRep[context3d.shapes.length]];
FOR i: NAT IN [0..context3d.shapes.length) DO
scene2[i] ← context3d.shapes[i];
ENDLOOP;
scene2.length ← context3d.shapes.length;
}
ELSE {
context3d.shapes ← NEW[ShapeSequenceRep[scene2.length]];
FOR i: NAT IN [0..scene2.length) DO
context3d.shapes[i] ← scene2[i];
ENDLOOP;
context3d.shapes.length ← scene2.length;
};
G3dRenderWithPixels.DepthBuffering[context3d, FALSE];
currentScene ← scene2;
};
SetScene3: PROCEDURE [] ~ {
IF scene3 = NIL THEN {
Reset[];
G3dRender.AddShapeFromFile[context3d, "TeaPot", "TeaPotWithBot.shape", [0.0, 0.0, -1.25]];
scene3 ← NEW[ShapeSequenceRep[context3d.shapes.length]];
FOR i: NAT IN [0..context3d.shapes.length) DO
scene3[i] ← context3d.shapes[i];
ENDLOOP;
scene3.length ← context3d.shapes.length;
G3dRender.SetView[context3d, [3.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
}
ELSE {
context3d.shapes ← NEW[ShapeSequenceRep[scene3.length]];
FOR i: NAT IN [0..scene3.length) DO
context3d.shapes[i] ← scene3[i];
ENDLOOP;
context3d.shapes.length ← scene3.length;
};
currentScene ← scene3;
};
OneShape: PROCEDURE [] ~ {
Reset[];
G3dRender.AddShapeFromFile[context3d, "Shape0", ViewerTools.GetSelectionContents[] ];
G3dRender.SetView[context3d, [0.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
};
GetMouseRectangle: PROCEDURE[x, y: REAL] ~ {
IF mouseRectangle # NIL
THEN {
mouseRectangle.w ← x - mouseRectangle.x;
IF mouseRectangle.w < 0.0 THEN {
mouseRectangle.x ← mouseRectangle.x + mouseRectangle.w;
mouseRectangle.w ← -mouseRectangle.w;
};
mouseRectangle.h ← y - mouseRectangle.y;
IF mouseRectangle.h < 0.0 THEN {
mouseRectangle.y ← mouseRectangle.y + mouseRectangle.h;
mouseRectangle.h ← -mouseRectangle.h;
};
SELECT mouseWaiting FROM
$Window => {
mouseRectangle.x ← mouseRectangle.x - context3d.viewPort.x;
mouseRectangle.y ← mouseRectangle.y - context3d.viewPort.y;
mouseRectangle.w ← MIN[mouseRectangle.w, context3d.viewPort.w];
mouseRectangle.x ← 2.0 * (mouseRectangle.x / context3d.viewPort.w - 0.5); -- center
mouseRectangle.y ←
2.0 * (mouseRectangle.y - context3d.viewPort.h/2.0) / context3d.viewPort.w;
mouseRectangle.w ← 2.0 * mouseRectangle.w / context3d.viewPort.w;
mouseRectangle.h ← mouseRectangle.w * context3d.viewPort.h/context3d.viewPort.w;
windowStack ← CONS[ context3d.window^, windowStack ];
G3dRender.SetWindow[ context3d, mouseRectangle^ ];
mouseRectangle ← NIL;
mouseWaiting ← NIL;
};
$ViewPort => {
viewPortStack ← CONS[ context3d.viewPort^, viewPortStack ];
G3dRender.SetViewPort[ context3d, mouseRectangle^ ];
mouseRectangle ← NIL;
mouseWaiting ← NIL;
};
ENDCASE => SIGNAL G3dRender.Error[$MisMatch, "Unknown Mouse Atom"];
}
ELSE mouseRectangle ← NEW[ Rectangle ← [x, y, 0.0, 0.0] ];
};
GetWindow: PROCEDURE[] ~ {
mouseWaiting ← $Window
};
RestoreWindow: PROCEDURE[] ~ {
IF windowStack # NIL
THEN {
G3dRender.SetWindow[ context3d, windowStack.first ];
windowStack ← windowStack.rest;
}
ELSE SIGNAL G3dRender.Error[$MisMatch, "No previous window"];
};
GetViewPort: PROCEDURE [] ~ {
mouseWaiting ← $ViewPort
};
RestoreViewPort: PROCEDURE[] ~ {
IF viewPortStack # NIL
THEN {
G3dRender.SetViewPort[ context3d, viewPortStack.first ];
viewPortStack ← viewPortStack.rest;
}
ELSE SIGNAL G3dRender.Error[$MisMatch, "No previous viewport"];
};
ShowSinglePatch: PROCEDURE[ shapeNumber, patchNumber: NAT, keep: BOOLFALSE ] ~ {
context3d.props ← PutProp[
context3d.props, $SinglePatch, NEW[ IntegerPair ← [shapeNumber, patchNumber] ]
];
DoCommands[context3d, $Display];
IF NOT keep THEN context3d.props ← Atom.RemPropFromList[ context3d.props, $SinglePatch ];
};
ShowPatchesOnVtx: PROCEDURE[ shapeNumber, vtxNumber: NAT, keep: BOOLFALSE ] ~ {
context3d.props ← PutProp[
context3d.props, $SingleVtx, NEW[ IntegerPair ← [shapeNumber, vtxNumber] ]
];
DoCommands[context3d, $Display];
IF NOT keep THEN context3d.props ← Atom.RemPropFromList[ context3d.props, $SingleVtx ];
};
Frame Generation and Animation
Orbit: PROCEDURE [] ~ {
IF DemoErrorMessages[] THEN RETURN;
animation ← TRUE;
G3dAnimationSupport.Orbit[
context: context3d,
lookingFrom: context3d.eyePoint,
lookingAt: context3d.lookAt,
axis: [0.3, 0.0, 1.0],
base: context3d.lookAt,
framesPerRev: 53
! UNWIND => animation ← FALSE
];
animation ← FALSE;
};
MakeFrame: PROC ~ {
context3d.class.render[context3d ];      -- clear and make new frame
};
ShowPixels: PROC ~ {
context3d.class.drawInViewer[ context3d,
        NEW
[ImagerProcRec ← [G3dColorDisplaySupport.StuffBuf, NIL]]
        ];
};
ShowShapes: PROC ~ {
G3dSortandDisplay.ShowShapes[context3d ];     -- show shapes without clearing
};
movieFileRoot: Rope.ROPE;
movieFileLog: Rope.ROPE;
movieFileLogTime: BasicTime.GMT;
movieNumFiles: NAT;
movieStart: NAT;
movieLabeledFrames: BOOLEAN;
SetUpMovie: PROC[fileRoot: Rope.ROPE, numFiles, start: NAT ← 0, labels: BOOLEANFALSE] ~ {
fileUpdated: BOOLEANFALSE;
IF frames # NIL AND Rope.Equal[movieFileRoot, fileRoot]
THEN {
latestTime: BasicTime.GMT;
[created: latestTime] ← FS.FileInfo[ G3dRender.GetLogFileName[fileRoot] ];
IF BasicTime.Period[from: movieFileLogTime, to: latestTime] > 0
THEN fileUpdated ← TRUE;
}
ELSE frames ← NIL;
IF frames = NIL OR fileUpdated THEN {
movieFileRoot ← fileRoot;
[fullFName: movieFileLog, created: movieFileLogTime] ← FS.FileInfo[
G3dRender.GetLogFileName[fileRoot]
];
movieNumFiles ← numFiles;
movieStart ← start;
movieLabeledFrames ← labels;
frames ← NIL;
};
};
frameRate: INTEGER ← 6;  -- playback rate in frames/second, negative plays backwards
PlayBackbyFrame: CedarProcess.ForkableProc ~ {
CleanUp: PROC ~ {
G3dRender.SetViewPort[ context3d, cachedViewPort ];
IF context3d.class.displayType = $PseudoColor
THEN G3dColorDisplaySupport.LoadStd8BitClrMap[context3d.terminal];
};
size, posn: Pair;
G3dSortandDisplay.ValidateContext[context3d];
cachedViewPort ← context3d.viewPort^;
context3d.class.loadBackground[context3d];
IF frames = NIL THEN {
frames ← G3dAnimationSupport.CacheNumberedAISFiles[
context3d, movieFileRoot, movieNumFiles, movieStart, movieLabeledFrames
];
};
Construct viewPort from cached frame size
IF context3d.stopMe^ THEN { CleanUp[]; RETURN[]; };
size ← [frames[0].box.max.f - frames[0].box.min.f, frames[0].box.max.s - frames[0].box.min.s];
posn ← IF context3d.viewer # NIL
THEN [ context3d.viewer.cx + (context3d.viewer.cw/2 - size.x/2),
context3d.viewer.cy + (context3d.viewer.ch/2 - size.y/2) ]
ELSE [ context3d.pixels.box.max.f/2 - size.x/2, context3d.pixels.box.max.s/2 - size.y/2 ];
G3dRender.SetViewPort[ context3d, [ posn.x, posn.y, size.x, size.y ] ];
IF context3d.viewer # NIL THEN ThreeDViewer.ViewerUpdate[context3d]; -- install viewport
context3d.class.draw2DRope[context3d, "Control frame rate using left button", [140, 50] ];
WHILE TRUE DO
ENABLE UNWIND => CleanUp[];
IF context3d.stopMe^ THEN { CleanUp[]; RETURN[]; };
G3dAnimationSupport.ShowNextAISCacheFrame[context3d, frames, frameRate];
CedarProcess.CheckAbort[];
ENDLOOP;
};
Commander.Register["ThreeDDemo", ThreeDDemoCommand, "\nDemonstrate ThreeDWorld"];
END.