<> <> 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 ]; ThreeDDemoImpl: CEDAR MONITOR IMPORTS Atom, List, AISAnimation, CedarProcess, ImagerColorMap, Process, QuickViewer, Real, Terminal, ThreeDMisc, ThreeDScenes, ViewerOps ~ BEGIN <> ThreeDDemoError: PUBLIC SIGNAL [reason: ATOM] = CODE; Context: TYPE ~ ThreeDScenes.Context; Triple: TYPE ~ Vector3d.Triple; RGB: TYPE ~ ImagerColor.RGB; <> 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; <> 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]; }; <> 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 }; <> 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; }; <> 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] ~ { <> 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] ~ { <> 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. < 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 ]; }; <> 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]; }; <> 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; }; Init[]; END.