ThreeDDemoImpl.mesa
Last Edited by: Crow, June 3, 1986 3:32:29 pm PDT
DIRECTORY
Atom     USING [ GetPropFromList, PutPropOnList, RemPropFromList ],
List     USING [ Memb ],
CedarProcess   USING [ ForkableProc, Process, Fork, Join, Abort, CheckAbort ],
Terminal    USING [ Virtual, Current ],
Commander   USING [ PrependWorkingDir ],
Rope     USING [ ROPE, Substr, Length ],
Real     USING [ FixI, Float ],
Imager    USING [ Context, Rectangle ],
ImagerColor   USING [ RGB ],
ImagerColorMap  USING [SetStandardColorMap],
QuickViewer   USING [ BuildViewer, Reset, QuickView ],
ViewerOps   USING [ ChangeColumn ],
ViewerClasses  USING [ Viewer ],
Vector3d    USING [ Triple ],
Pixels     USING [ PixelBuffer ],
ThreeDScenes  USING [ Context, Create, FindShape, SetLight, SetView,
         FillInBackGround, SetViewPort, ShapeInstance, ShapeSequence,
         GetFromImagerContext, GetShading, PutShading,
         SetWindow, WindowFromViewPort ],
ThreeDSurfaces  USING [ EnableDisplay, StopDisplay ],
ThreeDMisc   USING [ AddShapeAt, CloseLog, CopyContextData, GetImagerContext,
         GetPolygonColors, LoadColorRamp, LoadStd8BitClrMap,
         MakeFrame, OrbitEye, PutPixel, SetBackground,
         SetFacetedColor, SetNamedColor, SetShininess, SetSmoothColor,
         SetTransmittance, SetViewPortFromImager, ShowRope,
         ShowShapes, StartLog ],
AISAnimation  USING [ FrameSequence, ShowNextAISCacheFrame,
         CacheAISFiles, CacheNumberedAISFiles, StopDisplay ];
ThreeDDemoImpl: CEDAR MONITOR
IMPORTS Atom, List, AISAnimation, CedarProcess, ImagerColorMap, QuickViewer, Real, Terminal, ThreeDMisc, ThreeDSurfaces, ThreeDScenes, Commander, Rope, ViewerOps
~ BEGIN
Types
ThreeDDemoError: PUBLIC SIGNAL [reason: ATOM] = CODE;
Triple: TYPE ~ Vector3d.Triple;
RGB: TYPE ~ ImagerColor.RGB;
Global Variables
context3d, offScreenCtx, onScreenCtx: REF ThreeDScenes.Context;
currentScene, scene1, scene2, scene3: REF ThreeDScenes.ShapeSequence ← NIL;
frames: REF AISAnimation.FrameSequence;
xNow, yNow, zNow: REAL ← 3.0;
xOrigin, yOrigin: REAL ← 100.0;
movie, gallery, noReDraw, buttonsEnabled: BOOLEANFALSE;
cachedViewPort: Imager.Rectangle;
currentProcess: CedarProcess.Process ← NIL;
Context and Interactive Control
Init: PROC[] ~ {
viewer: ViewerClasses.Viewer ← NIL;
imagerCtx: Imager.Context;
wDir: Rope.ROPE ← Commander.PrependWorkingDir[" "]; -- working dir plus (needed) space
wDir ← Rope.Substr[ base: wDir, len: Rope.Length[wDir] - 1 ];     -- drop space
buttonsEnabled ← FALSE;
imagerCtx ← QuickViewer.BuildViewer[
LIST[$Help, $NewScene, $Lines, $Facets, $Smooth, $Shiny, $Orbit, $STOP, $Movie, $Gallery, $Jaggy, $NoJaggy, $Reset],
ReDraw,
ShutDown,
MenuHit,
"ThreeDDemo",
TRUE    -- shut off scrolling and extra buttons
];
viewer ← NARROW[imagerCtx.data, QuickViewer.QuickView].viewer.parent;
IF viewer.column # color THEN ViewerOps.ChangeColumn[viewer, color];
noReDraw ← TRUE;     -- stop recursion through ReDraw
context3d ← ThreeDScenes.GetFromImagerContext[ imagerCtx ];
context3d.props ← Atom.PutPropOnList[context3d.props, $WDir, wDir]; -- keep directory
ThreeDMisc.SetBackground[ context3d, "Darkish Blue" ];  -- set background color
[] ← ThreeDScenes.SetLight[context3d, "Initial", [-100., -200., 50.] ];
SetScene1[];
ThreeDScenes.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
noReDraw ← FALSE;
buttonsEnabled ← TRUE;
};
This resets the clipper, transformation, scene, etc. to the unused state
Reset: PROCEDURE [] ~ { 
imagerCtx: Imager.Context ← ThreeDMisc.GetImagerContext[context3d];
buttonsEnabled ← FALSE;
QuickViewer.Reset[ imagerCtx ];
ThreeDSurfaces.EnableDisplay[];       -- get stop flag unstuck, if necessary
ThreeDMisc.SetViewPortFromImager[ context3d, imagerCtx ];
ThreeDScenes.SetWindow[context3d,ThreeDScenes.WindowFromViewPort[context3d.viewPort]];
context3d.shapes ← context3d.lights;   -- delete all the objects
ThreeDScenes.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
movie ← FALSE;
buttonsEnabled ← TRUE;
currentScene ← NIL;
};
This will be called when the viewer is changed
ReDraw:  PROCEDURE [imagerCtx: Imager.Context] ~ { 
newContext: REF ThreeDScenes.Context;
oldCtx: Imager.Context;
IF noReDraw THEN RETURN;      -- Don't do anything during Init sequence
buttonsEnabled ← FALSE;
noReDraw ← TRUE;         -- stop recursion from viewers
oldCtx ← ThreeDMisc.GetImagerContext[context3d];
IF oldCtx # NIL THEN imagerCtx ← oldCtx;     -- use original context
newContext ← ThreeDScenes.GetFromImagerContext[
imagerCtx, context3d.alphaBuffer, context3d.depthBuffer
];
context3d.display ← newContext.display;
context3d.alphaBuffer ← newContext.alphaBuffer;
context3d.depthBuffer ← newContext.depthBuffer;
context3d.renderMode ← newContext.renderMode;
context3d.viewPort ← newContext.viewPort;
SetViewPort[ context3d.viewPort ];
noReDraw ← FALSE;
buttonsEnabled ← TRUE;
};
This will be called when the viewer is destroyed
ShutDown: PROCEDURE [] ~ {
buf: Pixels.PixelBuffer;  -- zeroed buffer
context3d.display ← buf;
IF offScreenCtx # NIL THEN offScreenCtx.display ← buf;
buttonsEnabled ← FALSE;
frames ← NIL;   -- free frame storage for garbage colection
pictures ← NIL;   -- free image storage for garbage collection
ThreeDMisc.CloseLog[context3d];
};
GetOffScreenCtx: PUBLIC PROC[context: REF ThreeDScenes.Context, width, height: NAT] ~ {
offScreenCtx ← ThreeDScenes.Create[
width,
height,
context.renderMode,
context.alphaBuffer
];
ThreeDMisc.CopyContextData[dstCtx: offScreenCtx, srcCtx: context];
offScreenCtx.viewPort ← [ x: 0.0, y: 0.0, w: Real.Float[width], h: Real.Float[height] ];
[] ← ThreeDMisc.StartLog[offScreenCtx];
FOR i: NAT IN [0..offScreenCtx.shapes.length) DO
offScreenCtx.shapes[i].vtcesInValid ← TRUE;
ENDLOOP;
};
GetOnScreenCtx: PUBLIC PROC[context: REF ThreeDScenes.Context, width, height: NAT] ~ {
onScreenCtx ← ThreeDScenes.GetFromImagerContext[
ThreeDMisc.GetImagerContext[context],
context.depthBuffer,
context.alphaBuffer,
context.renderMode = $Grey
];
ThreeDMisc.CopyContextData[dstCtx: onScreenCtx, srcCtx: context];
onScreenCtx.viewPort ← [ x: 0.0, y: 0.0, w: Real.Float[width], h: Real.Float[height] ];
FOR i: NAT IN [0..onScreenCtx.shapes.length) DO
onScreenCtx.shapes[i].vtcesInValid ← TRUE;
ENDLOOP;
};
This will be called whenever the mouse is moved or a button is pushed.
All atomic operations must occur in here.
MenuHit: ENTRY PROCEDURE[bttn: ATOM, x, y: REAL, ctrl, shift: BOOL] ~ { 
DoIt: CedarProcess.ForkableProc ~ { [] ← NARROW[ data, REF PROC]^[]; };  -- call the procedure
ForkAndWait: PROC [ proc: PROC ] ~ {
IF currentProcess # NIL AND currentProcess.status = busy OR gallery THEN RETURN;
fork process for safe remote aborting
currentProcess ← CedarProcess.Fork[DoIt, NEW[ PROC ← proc] ];
[] ← CedarProcess.Join[currentProcess];      -- await termination
currentProcess ← NIL;
};
ForkAndGo: PROC [ proc: PROC ] ~ {
IF currentProcess # NIL AND currentProcess.status = busy OR gallery THEN RETURN;
fork process for safe remote aborting, then proceeed so buttons may be caught
currentProcess ← CedarProcess.Fork[DoIt, NEW[ PROC ← proc] ];
};
StopEverything: PROC ~ {
ThreeDSurfaces.StopDisplay[];
AISAnimation.StopDisplay[];
IF currentProcess # NIL THEN CedarProcess.Abort[currentProcess]; -- kill it
[] ← CedarProcess.Join[currentProcess];      -- await termination
currentProcess ← NIL;
IF gallery THEN ClearGallery[];
IF movie THEN {
movie ← FALSE;
SetViewPort[ cachedViewPort ];   -- restore viewport
};
};
IF buttonsEnabled THEN SELECT bttn FROM
$Help    => ForkAndGo[ HelpMessage ];
$NewScene  => IF currentScene = scene1
       THEN ForkAndGo[ SetScene2 ]
      ELSE IF currentScene = scene2
      THEN ForkAndGo[ SetScene3 ]
      ELSE ForkAndGo[ SetScene1 ]; 
$Lines   => ForkAndGo[ LinesDemo ];
$Facets   => ForkAndGo[ FacetedDemo ];
$Smooth   => ForkAndGo[ SmoothDemo ];
$Shiny   => ForkAndGo[ ShinyDemo ];
$Orbit   => ForkAndGo[ Orbit ];
$STOP   => StopEverything[];
$Movie   => ForkAndGo[ PlayBackbyFrame ];
$Gallery   => ForkAndGo[ LoadGallery ];
$Jaggy   => ForkAndGo[ SetAliased ];
$NoJaggy   => ForkAndGo[ SetAntiAliased ];
$Reset   => { StopEverything[]; Reset[]; };
$LeftButton, $LeftHeld => IF movie
THEN frameRate ← Real.FixI[x / 10.]
ELSE IF gallery THEN ShowNextPicture[]
ELSE IF currentProcess = NIL OR currentProcess.status # busy THEN {
xNow ← x / 15.0;
yNow ← y / 15.0;
ThreeDScenes.SetView[ context3d, [xNow, yNow, zNow], context3d.ptOfInterest ];
ForkAndGo[MakeFrame];
};
$MiddleButton, $MiddleHeld => 
IF currentProcess = NIL OR currentProcess.status # busy THEN {
xLight: REAL ← x * 1.0;
yLight: REAL ← y * 1.0;
[] ← ThreeDScenes.SetLight[ context3d, "Initial", [xLight, yLight, 50.0] ];
IF NOT context3d.lineDrawing THEN ForkAndGo[MakeFrame];
};
$RightButton, $RightHeld => 
IF gallery THEN ShowLastPicture[]
ELSE IF currentProcess = NIL OR currentProcess.status # busy THEN {
zNow ← y / 15.0;
ThreeDScenes.SetView[ context3d, [xNow, yNow, zNow], context3d.ptOfInterest ];
ForkAndGo[MakeFrame];
};
ENDCASE;
};
HelpMessage: PROCEDURE [] ~ {
ThreeDMisc.SetNamedColor[context3d, "Vivid Green" ];
ThreeDMisc.ShowRope[context3d, 60.0, 360.0,
       "Scene1 - Select Scene with Polyhedra on Checkerboard." ];
ThreeDMisc.ShowRope[context3d, 60.0, 330.0,
       "Scene2 - Select Scene with Egg, banana, and Glass." ];
ThreeDMisc.ShowRope[context3d, 60.0, 300.0,
       "Lines, Facets, Smooth, Shiny - Make image as specified." ];
ThreeDMisc.ShowRope[context3d, 60.0, 270.0,
       "Orbit - Make images following elliptical path around scene." ];
ThreeDMisc.ShowRope[context3d, 60.0, 240.0,
       "Movie - Play back a precalculated sequence."];
ThreeDMisc.ShowRope[context3d, 60.0, 210.0,
       "Jaggy/NoJaggy - turn antialiasing off/on (NoJaggy is slow!)." ];
ThreeDMisc.ShowRope[context3d, 60.0, 180.0,
       "Buttoning in the image makes a new image with:" ];
ThreeDMisc.ShowRope[context3d, 60.0, 150.0,
       " Left Button determining eyepoint position in x-y plane," ];
ThreeDMisc.ShowRope[context3d, 60.0, 120.0,
       " Right Button determining eyepoint height," ];
ThreeDMisc.ShowRope[context3d, 60.0, 90.0,
       " Middle Button determining light source x-y." ];
ThreeDMisc.ShowRope[context3d, 60.0, 60.0,
       "STOP - stops long operations, Reset - recovers from trouble."];
};
SetViewPort: PROC [ size: Imager.Rectangle ] ~ {
ThreeDScenes.SetViewPort[ context3d, size ];   -- set clippers etc.
! ThreeDScenes.ThreeDScenesError => IF reason = $NullClipper THEN RESUME
FOR i: NAT IN [0..context3d.shapes.length) DO
context3d.shapes[i].vtcesInValid ← TRUE;
context3d.shapes[i].shadingInValid ← TRUE;
ENDLOOP;
ThreeDScenes.SetView[      -- get new screen dimensions into transformations
context:  context3d,
eyePoint:  context3d.eyePoint,
ptOfInterest: context3d.ptOfInterest,
fieldOfView: context3d.fieldOfView,
rollAngle: context3d.rollAngle,
upDirection: context3d.upDirection,
hitherLimit: context3d.hitherLimit,
yonLimit:  context3d.yonLimit
];
};
ChangeRenderMode: PROC [ mode: ATOM ] ~ {  -- for {$Dithered, $PseudoClr, $Grey}
SELECT mode FROM
$Dithered =>  ImagerColorMap.SetStandardColorMap[ Terminal.Current[] ];
$PseudoClr => ThreeDMisc.LoadStd8BitClrMap[ Terminal.Current[] ];
$Grey   =>
ThreeDMisc.LoadColorRamp[ Terminal.Current[], [0.,0.,0.], [1.,1.,1.], [.43, .43, .43] ];
ENDCASE  => SIGNAL ThreeDDemoError[$CantHackThatMode];
context3d.renderMode ← mode;
};
Demos
SetDepthBuffer: PROCEDURE [] ~ {
context: REF ThreeDScenes.Context;
IF context3d.depthBuffer = TRUE THEN RETURN[];
context ← ThreeDScenes.GetFromImagerContext[
NARROW[Atom.GetPropFromList[ context3d.display.props, $ImagerContext], Imager.Context],
FALSE, TRUE
];
context3d.display ← context.display;
context3d.alphaBuffer ← FALSE; context3d.depthBuffer ← TRUE;
context3d.renderMode ← context.renderMode;
SetViewPort[context.viewPort];
};
NoDepthBuffer: PROCEDURE [] ~ {
context: REF ThreeDScenes.Context;
IF context3d.depthBuffer = FALSE THEN RETURN[];
context ← ThreeDScenes.GetFromImagerContext[
NARROW[Atom.GetPropFromList[ context3d.display.props, $ImagerContext], Imager.Context],
context3d.alphaBuffer
];
context3d.display ← context.display;
context3d.depthBuffer ← FALSE;
context3d.renderMode ← context.renderMode;
SetViewPort[context.viewPort];
};
SetAliased: PROCEDURE [] ~ {
context: REF ThreeDScenes.Context ← ThreeDScenes.GetFromImagerContext[
NARROW[Atom.GetPropFromList[ context3d.display.props, $ImagerContext], Imager.Context],
FALSE, context3d.depthBuffer
];
context3d.display ← context.display;
context3d.alphaBuffer ← FALSE;
context3d.renderMode ← context.renderMode;
SetViewPort[context.viewPort];
};
SetAntiAliased: PROCEDURE [] ~ {
context: REF ThreeDScenes.Context ← ThreeDScenes.GetFromImagerContext[
NARROW[Atom.GetPropFromList[ context3d.display.props, $ImagerContext], Imager.Context],
TRUE
];
context3d.display ← context.display;
context3d.alphaBuffer ← TRUE; context3d.depthBuffer ← FALSE;
context3d.renderMode ← context.renderMode;
SetViewPort[context.viewPort];
};
DemoErrorMessages: PROCEDURE [] RETURNS[ error: BOOLEANFALSE] ~ {
NoSurfaces: PROCEDURE [shapes: REF ThreeDScenes.ShapeSequence] RETURNS[BOOLEAN]~{
FOR i: NAT IN [0..shapes.length) DO
IF shapes[i].surface # NIL THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
};
IF context3d.shapes = NIL OR NoSurfaces[context3d.shapes] THEN {
ThreeDMisc.SetNamedColor[context3d, "Vivid Green"];
ThreeDMisc.ShowRope[context3d, 60.0, 340.0, "No objects, nothing to display"];
error ← TRUE;
};
IF context3d.alphaBuffer
AND NOT List.Memb[ context3d.renderMode, LIST[$Grey, $FullClr, $Dorado24] ]
THEN {
ThreeDMisc.SetNamedColor[context3d, "Vivid Green"];
ThreeDMisc.ShowRope[context3d, 60.0, 300.0, "NoJaggy needs GreyScale or 24-bit Color"];
error ← TRUE;
};
};
LinesDemo: PROCEDURE [] ~ {
context3d.lineDrawing ← TRUE;
IF DemoErrorMessages[] THEN RETURN;
IF NOT context3d.alphaBuffer
THEN MakeFrame[]
ELSE {
FOR i: NAT IN [0..context3d.shapes.length) DO
IF context3d.shapes[i].surface # NIL
THEN ThreeDScenes.PutShading[context3d.shapes[i], $Type, $Lines ];
ENDLOOP;
MakeFrame[];
};
};
FacetedDemo: PROCEDURE [] ~ {
context3d.lineDrawing ← FALSE;
IF DemoErrorMessages[] THEN RETURN;
FOR i: NAT IN [0..context3d.shapes.length) DO IF context3d.shapes[i].surface # NIL THEN {
context3d.shapes[i].shadingProps ← Atom.RemPropFromList[  -- no highlights
context3d.shapes[i].shadingProps,
$Shininess
];
ThreeDScenes.PutShading[context3d.shapes[i], $Type, $Faceted ];
IF ThreeDScenes.GetShading[context3d.shapes[i], $PatchColors ] = NIL THEN {
rgb: RGBNARROW[ThreeDScenes.GetShading[context3d.shapes[i], $Color], REF RGB ]^;
ThreeDMisc.SetFacetedColor[context3d, context3d.shapes[i].name, rgb];
};
};
ENDLOOP;
MakeFrame[];
};
SmoothDemo: PROCEDURE [] ~ {
context3d.lineDrawing ← FALSE;
IF DemoErrorMessages[] THEN RETURN;
FOR i: NAT IN [0..context3d.shapes.length) DO IF context3d.shapes[i].surface # NIL THEN {
context3d.shapes[i].shadingProps ← Atom.RemPropFromList[  -- no highlights
context3d.shapes[i].shadingProps,
$Shininess
];
ThreeDScenes.PutShading[context3d.shapes[i], $Type, $Smooth ];
};
ENDLOOP;
MakeFrame[];
};
ShinyDemo: PROCEDURE [] ~ {
context3d.lineDrawing ← FALSE;
IF DemoErrorMessages[] THEN RETURN;
FOR i: NAT IN [0..context3d.shapes.length) DO IF context3d.shapes[i].surface # NIL THEN {
shininess: REF REALNARROW[
ThreeDScenes.GetShading[context3d.shapes[i], $Shininess]
];
IF shininess = NIL THEN {
shininess ← NEW[REAL ← 50.0];
ThreeDMisc.SetShininess[ context3d, context3d.shapes[i].name, shininess^ ];
};
IF NARROW[ ThreeDScenes.GetShading[context3d.shapes[i], $Type], ATOM ] = $Lines
THEN ThreeDScenes.PutShading[context3d.shapes[i], $Type, $Smooth ];
};
ENDLOOP;
MakeFrame[];
};
Orbit: PROCEDURE [] ~ {
IF DemoErrorMessages[] THEN RETURN;
ThreeDSurfaces.EnableDisplay[];       -- get stop flag unstuck, if necessary
ThreeDMisc.OrbitEye[
context: context3d,
lookingFrom: context3d.eyePoint,
lookingAt: context3d.ptOfInterest,
axis: [0.3, 0.0, 1.0],
framesPerRev: 53
];
};
SetScene1: PROCEDURE [] ~ {
IF scene1 = NIL THEN {
context3d.shapes ← context3d.lights;   -- don't change the lighting
ThreeDMisc.AddShapeAt[ context3d,
"CheckerBoard",        -- internal name
"CheckerBoard.shape",       -- file name
[-8.0, -8.0, -2.0],        -- position
$ConvexPolygon, TRUE    -- not a closed shape (TRUE allows backfacing polys)
];
ThreeDMisc.AddShapeAt[ context3d, "Icosahedron", "Icosahedron.shape", [2.0, 0.0, 0.0] ];
ThreeDMisc.AddShapeAt[ context3d, "SoccerBall", "SoccerBall.shape", [-2.0, 0.0, 0.0] ];
ThreeDMisc.AddShapeAt[ context3d, "CutCube", "CutCube.shape", [0.0, 3.0, 0.0] ];
ThreeDMisc.SetFacetedColor[ context3d, "CheckerBoard", [.8, .4, .2] ]; -- add shading
ThreeDMisc.SetSmoothColor[ context3d, "CheckerBoard", [.8, .4, .2] ];
ThreeDMisc.SetFacetedColor[ context3d, "SoccerBall", [.7, .7, .6] ];
ThreeDMisc.SetSmoothColor[ context3d, "SoccerBall", [1.0, .8, .8] ];
ThreeDMisc.SetFacetedColor[ context3d, "Icosahedron", [.1, .9, .1] ];
ThreeDMisc.SetSmoothColor[ context3d, "Icosahedron", [.8, 1.0, .8] ];
ThreeDMisc.SetFacetedColor[ context3d, "CutCube", [.9, .3, .1] ];
ThreeDMisc.SetSmoothColor[ context3d, "CutCube", [.9, .3, .1] ];
ThreeDMisc.GetPolygonColors[ context3d, "Icosahedron", "IcosahedronPolyColors.vtces" ];
ThreeDMisc.GetPolygonColors[ context3d, "SoccerBall", "SoccerBallPolyColors.vtces" ];
scene1 ← NEW[ThreeDScenes.ShapeSequence[context3d.shapes.length] ← context3d.shapes^];
}
ELSE context3d.shapes ← scene1;
NoDepthBuffer[];
currentScene ← scene1;
};
SetScene2: PROCEDURE [] ~ {
IF scene2 = NIL THEN {
context3d.shapes ← context3d.lights;   -- don't change the lighting;
ThreeDMisc.AddShapeAt[context3d, "ChampagneGlass",
    "ChampagneGlass.shape",
    [2.0, 0.0, 0.0],
    $ConvexPolygon, TRUE ];    -- TRUE allows the inside to be seen
ThreeDMisc.AddShapeAt[context3d, "UtahEgg", "UtahEgg.shape", [-2.0, .0, .0] ];
ThreeDMisc.AddShapeAt[context3d, "Banana", "Banana.shape", [0.0, .0, .0] ];
ThreeDScenes.FindShape[ context3d.shapes, "UtahEgg" ].orientation ← [-1., 1., 0.];
ThreeDScenes.FindShape[ context3d.shapes, "UtahEgg" ].rotation ← 90.0;
ThreeDScenes.FindShape[ context3d.shapes, "ChampagneGlass" ].orientation ← [0., 0., 1.];
ThreeDScenes.FindShape[ context3d.shapes, "ChampagneGlass" ].rotation ← 45.0;
ThreeDMisc.SetFacetedColor[context3d, "ChampagneGlass", [1.,0.,1.] ];
ThreeDMisc.SetSmoothColor[context3d, "ChampagneGlass", [1.,0.,1.] ];
ThreeDMisc.SetTransmittance[context3d, "ChampagneGlass", .8 ];
ThreeDMisc.SetFacetedColor[context3d, "UtahEgg", [1.0, .8, .5] ];
ThreeDMisc.SetSmoothColor[context3d, "UtahEgg", [1.0, .8, .5] ];
ThreeDMisc.SetFacetedColor[context3d, "Banana", [1.0, .9, .1] ];
ThreeDMisc.SetSmoothColor[context3d, "Banana", [1.0, .9, .1] ];
scene2 ← NEW[ThreeDScenes.ShapeSequence[context3d.shapes.length] ← context3d.shapes^];
}
ELSE context3d.shapes ← scene2;
NoDepthBuffer[];
currentScene ← scene2;
};
SetScene3: PROCEDURE [] ~ {
IF scene3 = NIL THEN {
context3d.shapes ← context3d.lights;   -- don't change the lighting;
ThreeDMisc.AddShapeAt[context3d, "TeaPot", "TeaPotWithBot.bezier",
          [0.0, 0.0, -1.25], $Bezier ];
scene3 ← NEW[ThreeDScenes.ShapeSequence[context3d.shapes.length] ← context3d.shapes^];
ThreeDScenes.SetView[context3d, [3.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
}
ELSE context3d.shapes ← scene3;
SetDepthBuffer[];
currentScene ← scene3;
};
PutPixel: PROCEDURE[x, y: NAT, clr: RGB] ~ {
ThreeDMisc.PutPixel[context3d, x, y, clr];
};
Frame Generation and Animation
MakeFrame: PROC ~ {
ThreeDSurfaces.EnableDisplay[];       -- get stop flag unstuck, if necessary
ThreeDMisc.MakeFrame[context3d ];      -- clear and make new frame
};
ShowShapes: PROC ~ {
ThreeDSurfaces.EnableDisplay[];       -- get stop flag unstuck, if necessary
ThreeDMisc.ShowShapes[context3d ];     -- show shapes without clearing
};
movieFileRoot: Rope.ROPE ← "/Pixel/Crow/Animation/StillCloser.ais";
movieViewPort: Imager.Rectangle ← [120.0, 90.0, 400.0, 300.0];
movieNumFiles: NAT ← 40;
movieStart: NAT ← 1;
SetUpMovie: PROC[fileRoot: Rope.ROPE, viewPort: Imager.Rectangle, numFiles, start: NAT] ~ {
ChangeRenderMode[$PseudoClr];
movieFileRoot ← fileRoot;
movieViewPort ← viewPort;
movieNumFiles ← numFiles;
movieStart ← start;
frames ← NIL;
};
frameRate: INTEGER ← 6;  -- playback rate in frames/second, negative plays backwards
PlayBackbyFrame: PROC ~ {
cachedViewPort ← context3d.viewPort;
ThreeDMisc.SetNamedColor[context3d, "Vivid Green" ];
IF context3d.renderMode # $PseudoClr THEN {
ThreeDMisc.ShowRope[context3d, 140.0, 50.0, "Change to 8-bit pseudocolor, please!"];
RETURN[];
};
ThreeDScenes.FillInBackGround[context3d];
ThreeDMisc.ShowRope[context3d, 140.0, 50.0, "Control frame rate using left button"];
movie ← TRUE;
SetViewPort[ movieViewPort ];
IF frames = NIL THEN {
pictures ← NIL;        -- avoid using up all the VM
frames ← AISAnimation.CacheNumberedAISFiles[
context3d, movieFileRoot, movieNumFiles, movieStart
];
};
WHILE movie DO
AISAnimation.ShowNextAISCacheFrame[context3d, frames, frameRate];
CedarProcess.CheckAbort[];
ENDLOOP;
SetViewPort[ cachedViewPort ];
};
pictureFile: Rope.ROPE ← "/Cyan/AIS/Crow/PrettyPictures.txt";
pictures: REF AISAnimation.FrameSequence; 
LoadGallery: PROC ~ {
ThreeDMisc.SetNamedColor[context3d, "Vivid Green" ];
IF context3d.renderMode # $Dorado24 THEN {
ThreeDMisc.ShowRope[context3d, 140.0, 50.0, "Change to 24-bit color, please!"];
RETURN[];
};
ThreeDScenes.FillInBackGround[context3d];
ThreeDMisc.ShowRope[context3d, 140.0, 50.0, "Left button, ahead - Right button, back"];
gallery ← TRUE;
SetAliased[];
IF pictures = NIL THEN pictures ← NEW[AISAnimation.FrameSequence[16]];
pictures ← AISAnimation.CacheAISFiles[context3d, pictureFile, pictures];
};
ShowNextPicture: PROC ~ {   -- display next picture in sequence
IF pictures # NIL AND pictures.length > 0 -- make sure frame ready and no wraparound
AND ((pictures.currentFrame + 1) MOD pictures.length) >= pictures.currentFrame
THEN AISAnimation.ShowNextAISCacheFrame[context3d, pictures, 10];
};
ShowLastPicture: PROC ~ {   -- display previous picture in sequence
IF pictures # NIL AND pictures.currentFrame > 0
THEN AISAnimation.ShowNextAISCacheFrame[context3d, pictures, -10];
};
ClearGallery: PROC ~ {
IF pictures # NIL
THEN FOR i: NAT IN [0..pictures.length) DO pictures[i].pixels ← NIL; ENDLOOP;
gallery ← FALSE;
};
Init[];
END.