ThreeDDemoImpl.mesa
Last Edited by: Crow, December 16, 1986 5:22:56 pm PST
DIRECTORY
Atom USING [ GetPropFromList, RemPropFromList, PutPropOnList ],
List USING [ Memb ],
CedarProcess USING [ Abort, CheckAbort, Fork, ForkableProc, Process],
Process USING [ Pause, SecondsToTicks ],
Terminal USING [ Virtual, Current ],
Rope USING [ ROPE ],
Real USING [ FixI, Float ],
Imager USING [ Context, Rectangle ],
ImagerColor USING [ RGB ],
ImagerColorMap USING [SetStandardColorMap],
QuickViewer USING [ BuildViewer, DrawInViewer, QuickView ],
ViewerOps USING [ ChangeColumn, OpenIcon ],
Vector3d USING [ Triple ],
Pixels USING [ PixelBuffer ],
ThreeDScenes USING [ AddAlphaBuffer, AddDepthBuffer, Context, Create,
DisplayFromImagerContext, DisplayFromVM, FillInBackGround,
FindShape, GetShading, PutShading, SetLight, SetView,
SetViewPort, SetWindow, ShapeSequence, WindowFromViewPort
],
ThreeDMisc USING [ AddShapeAt, CloseLog, CopyContextData, LoadColorRamp,
LoadStd8BitClrMap, MakeFrame, OrbitEye, PutPixel,
SetBackgroundColor, SetFacetedColor, SetShininess, SetSmoothColor,
SetTransmittance, ShowRope, ShowShapes, StartLog,
UpdateDisplay ],
AISAnimation USING [ FrameSequence, ShowNextAISCacheFrame,
CacheAISFiles, CacheNumberedAISFiles ];
Global Variables
window3d: REF QuickViewer.QuickView;
context3d, offScreenCtx, onScreenCtx: REF Context;
currentScene, scene1, scene2, scene3: REF ThreeDScenes.ShapeSequence ← NIL;
frames: REF AISAnimation.FrameSequence;
xNow, yNow, zNow: REAL ← 3.0;
xOrigin, yOrigin: REAL ← 100.0;
animation, gallery: BOOLEAN ← FALSE;
movieProcess: CedarProcess.Process ← NIL;
cachedViewPort: Imager.Rectangle;
Context and Interactive Control
Init:
PROC[] ~ {
context3d ← ThreeDScenes.Create[];
context3d.preferredRenderMode ← $PseudoColor;
context3d.viewer ← window3d ← QuickViewer.BuildViewer[
"ThreeDDemo",
LIST["Help", "NewScene", "Lines", "Facets", "Smooth", "Shiny", "Orbit", "STOP!", "Movie", "Gallery", "Jaggy", "NoJaggy", "Reset"],
ReDraw,
MenuHit,
ShutDown
];
ThreeDMisc.SetBackgroundColor[ context3d, "Darkish Blue" ]; -- set background color
[] ← ThreeDScenes.SetLight[context3d, "Initial", [-100., -200., 50.] ];
ThreeDScenes.SetView[ context3d, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
SetScene1[];
IF window3d.outer.column # color THEN ViewerOps.ChangeColumn[window3d.outer, color];
IF window3d.outer.iconic THEN ViewerOps.OpenIcon[icon: window3d.outer];
};
This resets the clipper, transformation, scene, etc. to the unused state
Reset:
PROCEDURE [] ~ {
DoReDraw: PROCEDURE [imagerCtx: Imager.Context] ~ { ReDraw[imagerCtx, NIL]; };
context3d.stopMe ← FALSE; -- get stop flag unstuck, if necessary
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] ];
gallery ← FALSE;
currentScene ← NIL;
frames ← NIL;
QuickViewer.DrawInViewer[window3d, DoReDraw]; -- gets a new imager context
};
This will be called when the viewer is changed, grabs Imager context and rebuilds context3d.display
ReDraw:
PROCEDURE [imagerCtx: Imager.Context, toDo:
REF
ANY] ~ {
ENABLE UNWIND => NULL;
ThreeDScenes.DisplayFromImagerContext[context3d, imagerCtx];
IF context3d.depthBuffer THEN ThreeDScenes.AddDepthBuffer[context3d];
IF context3d.alphaBuffer THEN ThreeDScenes.AddAlphaBuffer[context3d];
};
Notify:
ENTRY
PROCEDURE [] ~ {
NOTIFY context3d.halted;
};
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;
frames ← NIL; -- free frame storage for garbage colection
pictures ← NIL; -- free image storage for garbage collection
ThreeDMisc.CloseLog[context3d];
};
GetOffScreenCtx:
PROC[context:
REF Context, width, height:
NAT] ~ {
offScreenCtx ← ThreeDScenes.Create[];
ThreeDScenes.DisplayFromVM[offScreenCtx, width, height, context.renderMode];
IF context.alphaBuffer THEN ThreeDScenes.AddAlphaBuffer[offScreenCtx];
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:
PROC[context:
REF Context, width, height:
NAT] ~ {
Makes a new view of the supplied context, useful for using two portions of the screen differently, etc.
GetDisplay:
PROC[ imagerCtx: Imager.Context] ~ {
ThreeDScenes.DisplayFromImagerContext[ onScreenCtx, imagerCtx ];
IF context.depthBuffer THEN ThreeDScenes.AddDepthBuffer[context3d];
IF context.alphaBuffer THEN ThreeDScenes.AddAlphaBuffer[context3d];
};
onScreenCtx ← ThreeDScenes.Create[];
QuickViewer.DrawInViewer[ NARROW[context.viewer], GetDisplay ];
IF context.alphaBuffer THEN ThreeDScenes.AddAlphaBuffer[onScreenCtx];
IF context.depthBuffer THEN ThreeDScenes.AddDepthBuffer[onScreenCtx];
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;
};
StopEverything:
PROCEDURE[] ~ {
-- panic stop
bufContext:
REF Context ←
NARROW[
Atom.GetPropFromList[context3d.props, $BufferContext]
];
context3d.stopMe ← TRUE;
IF bufContext #
NIL
THEN {
bufContext.stopMe ← TRUE;
context3d.props ← Atom.RemPropFromList[context3d.props, $BufferContext];
};
IF gallery THEN ClearGallery[];
IF movieProcess #
NIL
THEN {
CedarProcess.Abort[movieProcess];
movieProcess ← NIL;
SetViewPort[ cachedViewPort ];
};
};
MenuHit:
PROCEDURE[bttn, choice:
ATOM, x, y:
REAL] ~ {
This will be called whenever the mouse is moved or a button is pushed.
x ← x - context3d.display.width/2; -- center mouse coords
y ← y - context3d.display.height/2;
SELECT bttn
FROM
$Help => HelpMessage;
$NewScene =>
IF currentScene =
NIL
THEN SetScene1[]
ELSE IF currentScene = scene1 THEN SetScene2[]
ELSE IF currentScene = scene2 THEN SetScene3[] ELSE SetScene1[];
$Lines => LinesDemo[];
$Facets => FacetedDemo[];
$Smooth => SmoothDemo[];
$Shiny => ShinyDemo[];
$Orbit => Orbit[];
$STOP => StopEverything[];
$Movie => movieProcess ← CedarProcess.Fork[PlayBackbyFrame];
$Gallery => [] ← CedarProcess.Fork[LoadGallery];
$Jaggy => SetAliased[];
$NoJaggy => SetAntiAliased[];
$Reset => { StopEverything[]; Reset[]; };
$LeftButton, $LeftHeld =>
IF movieProcess #
NIL
THEN frameRate ← Real.FixI[x / 10.]
ELSE IF gallery THEN ShowNextPicture[]
ELSE {
xNow ← x / 15.0;
yNow ← y / 15.0;
ThreeDScenes.SetView[ context3d, [xNow, yNow, zNow], context3d.ptOfInterest ];
MakeFrame[];
};
$MiddleButton, $MiddleHeld =>
IF
NOT context3d.lineDrawing
THEN {
xLight: REAL ← x * 1.0;
yLight: REAL ← y * 1.0;
[] ← ThreeDScenes.SetLight[ context3d, "Initial", [xLight, yLight, 50.0] ];
MakeFrame[];
};
$RightButton, $RightHeld =>
IF gallery THEN ShowLastPicture[]
ELSE {
zNow ← y / 15.0;
ThreeDScenes.SetView[ context3d, [xNow, yNow, zNow], context3d.ptOfInterest ];
MakeFrame[];
};
ENDCASE;
};
HelpMessage:
PROCEDURE [] ~ {
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, $PseudoColor, $Grey}
SELECT mode
FROM
$Dithered => ImagerColorMap.SetStandardColorMap[ Terminal.Current[] ];
$PseudoColor => 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;
context3d.preferredRenderMode ← mode;
context3d.display.props ← Atom.PutPropOnList[ context3d.display.props, $RenderMode, mode ];
};
Demos
SetDepthBuffer:
PROCEDURE [] ~ {
IF context3d.depthBuffer = TRUE THEN RETURN[];
ThreeDScenes.AddDepthBuffer[context3d];
};
NoDepthBuffer:
PROCEDURE [] ~ {
GetDisplay:
PROC[ imagerCtx: Imager.Context] ~ {
ThreeDScenes.DisplayFromImagerContext[ context3d, imagerCtx ];
};
IF context3d.depthBuffer = FALSE THEN RETURN[];
QuickViewer.DrawInViewer[ NARROW[context3d.viewer], GetDisplay ];
IF context3d.alphaBuffer THEN ThreeDScenes.AddAlphaBuffer[context3d];
context3d.depthBuffer ← FALSE;
};
SetAliased:
PROCEDURE [] ~ {
GetDisplay:
PROC[ imagerCtx: Imager.Context] ~ {
ThreeDScenes.DisplayFromImagerContext[ context3d, imagerCtx ];
};
IF context3d.alphaBuffer = FALSE THEN RETURN[];
QuickViewer.DrawInViewer[ NARROW[context3d.viewer], GetDisplay ];
IF context3d.depthBuffer THEN ThreeDScenes.AddDepthBuffer[context3d];
context3d.alphaBuffer ← FALSE;
};
SetAntiAliased:
PROCEDURE [] ~ {
IF context3d.alphaBuffer = TRUE THEN RETURN[];
ThreeDScenes.AddAlphaBuffer[context3d];
};
DemoErrorMessages:
PROCEDURE []
RETURNS[ error:
BOOLEAN ←
FALSE] ~ {
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.ShowRope[context3d, 60.0, 340.0, "No objects, nothing to display"];
error ← TRUE;
};
IF context3d.alphaBuffer
AND NOT List.Memb[ context3d.renderMode, LIST[$Grey, $FullColor, $Dorado24] ]
THEN {
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;
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 {
color: REF ← ThreeDScenes.GetShading[context3d.shapes[i], $Color];
rgb: RGB ← IF color # NIL THEN NARROW[color, REF RGB ]^ ELSE [0.7, 0.7, 0.7];
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
REAL ←
NARROW[
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;
context3d.stopMe ← FALSE; -- get stop flag unstuck, if necessary
context3d.suspendMe ← FALSE;
animation ← TRUE;
ThreeDMisc.OrbitEye[
context: context3d,
lookingFrom: context3d.eyePoint,
lookingAt: context3d.ptOfInterest,
axis: [0.3, 0.0, 1.0],
framesPerRev: 53
! UNWIND => animation ← FALSE
];
animation ← FALSE;
};
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
];
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, "CutCube", [.9, .3, .1] ];
scene1 ← NEW[ThreeDScenes.ShapeSequence[context3d.shapes.length] ← context3d.shapes^];
context3d.lineDrawing ← TRUE;
}
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] ];
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.shape", [0.0, 0.0, -1.25] ];
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 ~ {
context3d.stopMe ← FALSE; -- get stop flag unstuck, if necessary
ThreeDMisc.MakeFrame[context3d ]; -- clear and make new frame
};
ShowShapes:
PROC ~ {
context3d.stopMe ← FALSE; -- 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[$PseudoColor];
movieFileRoot ← fileRoot;
movieViewPort ← viewPort;
movieNumFiles ← numFiles;
movieStart ← start;
frames ← NIL;
};
frameRate: INTEGER ← 6; -- playback rate in frames/second, negative plays backwards
PlayBackbyFrame:
ENTRY CedarProcess.ForkableProc ~ {
ENABLE UNWIND => NULL;
context3d.stopMe ← FALSE; -- get stop flag unstuck, if necessary
cachedViewPort ← context3d.viewPort;
IF context3d.renderMode # $PseudoColor
THEN {
ThreeDMisc.ShowRope[context3d, 140.0, 50.0, "Change to 8-bit pseudocolor, please!"];
RETURN[];
};
ThreeDScenes.FillInBackGround[context3d];
SetViewPort[ movieViewPort ];
IF frames =
NIL
THEN {
pictures ← NIL; -- avoid using up all the VM
frames ← AISAnimation.CacheNumberedAISFiles[
context3d, movieFileRoot, movieNumFiles, movieStart
];
};
ThreeDMisc.ShowRope[context3d, 140.0, 50.0, "Control frame rate using left button"];
WHILE
TRUE
DO
ENABLE UNWIND => SetViewPort[ cachedViewPort ];
changed: BOOLEAN ← ThreeDMisc.UpdateDisplay[context3d];
IF changed
THEN {
Process.Pause[Process.SecondsToTicks[2]]; -- hang on moment to allow things to settle
SetViewPort[ movieViewPort ];
};
AISAnimation.ShowNextAISCacheFrame[context3d, frames, frameRate];
CedarProcess.CheckAbort[];
ENDLOOP;
};
pictureFile: Rope.ROPE ← "/Cyan/AIS/Crow/PrettyPictures.txt";
pictures: REF AISAnimation.FrameSequence;
LoadGallery: CedarProcess.ForkableProc ~ {
IF context3d.renderMode # $Dorado24
THEN {
ThreeDMisc.ShowRope[context3d, 140.0, 50.0, "Change to 24-bit color, please!"];
RETURN[];
};
context3d.stopMe ← FALSE; -- get stop flag unstuck, if necessary
ThreeDScenes.FillInBackGround[context3d];
ThreeDMisc.ShowRope[context3d, 140.0, 50.0, "Pictures will appear as they are read in"];
ThreeDMisc.ShowRope[context3d, 140.0, 20.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;
};