DIRECTORY Atom, CedarProcess, Convert, FS, G3dAnimationSupport, G3dBasic, G3dColorDisplaySupport, G3dMatrix, G3dRender, G3dRenderWithPixels, G3dSortandDisplay, G3dSpline, G3dVector, ImagerBackdoor, ImagerSample, IO, Real, Rope, Terminal; G3dAnimationSupportImpl: CEDAR MONITOR IMPORTS Atom, CedarProcess, Convert, FS, G3dColorDisplaySupport, G3dMatrix, G3dRender, G3dRenderWithPixels, G3dSortandDisplay, G3dSpline, G3dVector, ImagerSample, IO, Real, Rope, Terminal EXPORTS G3dAnimationSupport ~ BEGIN ROPE: TYPE ~ Rope.ROPE; RopeSequence: TYPE ~ RECORD[ SEQUENCE length: NAT OF Rope.ROPE ]; Context: TYPE ~ G3dRender.Context; ContextClass: TYPE ~ G3dRender.ContextClass; Pair: TYPE ~ G3dRender.Pair; PairSequence: TYPE ~ G3dRender.PairSequence; Triple: TYPE ~ G3dRender.Triple; -- RECORD [ x, y, z: REAL]; TripleSequence: TYPE ~ G3dRender.TripleSequence; TripleSequenceRep: TYPE ~ G3dBasic.TripleSequenceRep; Rectangle: TYPE ~ G3dRender.Rectangle; Shape: TYPE ~ G3dRender.Shape; RenderData: TYPE ~ G3dRender.RenderData; ImagerProc: TYPE ~ G3dRender.ImagerProc; ImagerProcRec: TYPE ~ G3dRender.ImagerProcRec; ViewProc: TYPE ~ G3dAnimationSupport.ViewProc; FrameSequence: TYPE ~ G3dAnimationSupport.FrameSequence; SampleMap: TYPE ~ ImagerSample.SampleMap; LORA: TYPE ~ LIST OF REF ANY; GetProp: PROC [propList: Atom.PropList, prop: REF ANY] RETURNS [REF ANY] ~ Atom.GetPropFromList; PutProp: PROC [propList: Atom.PropList, prop: REF ANY, val: REF ANY] RETURNS [Atom.PropList] ~ Atom.PutPropOnList; bitsPer75thSecond: INT ~ 314573; -- roughly what bitBlt can move in one LF field time labelsEnabled: BOOLEAN _ FALSE; labelPosn: Pair _ [0.02, 0.02]; Ceiling: PROC[number: REAL] RETURNS[result: INTEGER] ~ { result _ Real.Round[number]; IF result < number THEN result _ result + 1; }; GetWaitCount: PROC[context: Context, framesPerSec: NAT] RETURNS[waitCount: NAT, transferTime: NAT] ~ { width: INT _ Ceiling[context.viewPort.w]; height: INT _ Ceiling[context.viewPort.h]; addOn: NAT _ IF context.antiAliasing THEN 1 ELSE 0; bitsPerPixel: NAT _ IF context.class.displayType = $FullColor THEN 24 ELSE 8; transferTime _ width * height * bitsPerPixel / bitsPer75thSecond; IF framesPerSec = 0 THEN waitCount _ 150 ELSE waitCount _ 75 / framesPerSec; -- LF display VerticalRetrace comes 75/sec. IF transferTime > waitCount -- take bitBlt overhead into account THEN waitCount _ 0 ELSE waitCount _ waitCount - transferTime; waitCount _ MAX[waitCount, 1]; }; ShowAISCacheFrame: PUBLIC PROC[ context: Context, frames: REF FrameSequence ] ~ { IF frames.currentFrame < 0 OR frames.currentFrame >= frames.length THEN SIGNAL G3dRender.Error[$MisMatch, "Frame number out of range"]; IF frames[frames.currentFrame] # NIL THEN IF context.viewer # NIL THEN { context.class.updateViewer[context]; context.pixels _ frames[frames.currentFrame]; context.class.drawInViewer[ context, NEW[ImagerProcRec _ [G3dColorDisplaySupport.StuffBuf, context]] ]; } ELSE { samplesPerColor: NAT _ IF context.class.displayType = $FullColor THEN 3 ELSE 1; FOR i: NAT IN [0..samplesPerColor) DO ImagerSample.Transfer[dst: context.pixels[i], src: frames[frames.currentFrame][i]]; ENDLOOP; }; }; PlayBackNumberedAISFiles: PUBLIC PROC[context: Context, fileRoot : Rope.ROPE, startNum, numFiles, framesPerSec, secondsPlayingTime : NAT] ~ { frames: REF FrameSequence _ CacheNumberedAISFiles[context, fileRoot, numFiles, startNum]; PlayBackAISCache[context, frames, framesPerSec, secondsPlayingTime]; }; CacheAISFiles: PUBLIC PROC[ context: Context, fileOfNames: Rope.ROPE, labeled: BOOL _ FALSE ] RETURNS[REF FrameSequence] ~ { pictureNames: REF RopeSequence; GetName: PROC[number: NAT] RETURNS[Rope.ROPE] ~ { RETURN[ pictureNames[number] ]; }; Extend: PROC[names: REF RopeSequence] ~ { newNames: REF RopeSequence _ NEW[ RopeSequence[names.length*2] ]; FOR i: NAT IN [0..names.length) DO newNames[i] _ names[i]; ENDLOOP; names _ newNames; }; input: IO.STREAM _ FS.StreamOpen[ fileName: fileOfNames ]; numFiles: NAT _ IO.GetInt[input]; -- first thing in file is number of pictures pictureNames _ NEW[ RopeSequence[numFiles] ]; numFiles _ 0; WHILE NOT IO.EndOf[input] DO -- read robustly in case number is wrong IF numFiles >= pictureNames.length THEN Extend[pictureNames]; pictureNames[numFiles] _ IO.GetRopeLiteral[input ! IO.EndOfStream => LOOP]; IF pictureNames[numFiles] # NIL THEN numFiles _ numFiles + 1; ENDLOOP; RETURN[ CacheFiles[context, numFiles, 0, GetName] ]; }; CacheNumberedAISFiles: PUBLIC PROC[context: Context, fileRoot: Rope.ROPE, numFiles: NAT, start: NAT _ 0, labeled: BOOL _ FALSE] RETURNS[REF FrameSequence] ~ { GetName: PROC[number: NAT] RETURNS[Rope.ROPE] ~ { RETURN[ G3dRender.PasteInSequenceNo[ G3dRender.PrependWorkingDirectory[context, fileRoot], number ] ]; }; RETURN[ CacheFiles[context, numFiles, start, GetName] ]; }; CacheFiles: PROC[context: Context, numFiles, start: NAT _ 0, getname: PROC[number: NAT] RETURNS[Rope.ROPE], labeled: BOOL _ FALSE ] RETURNS[REF FrameSequence] ~ { Action: PROC ~ { frameFailed, firstFrameFound: BOOLEAN _ FALSE; firstFrame, i: NAT _ start; tmpSamples: ARRAY[0..5) OF SampleMap _ ALL[NIL]; WHILE TRUE DO IF context.stopMe^ THEN RETURN[]; -- shut down if stop signal received CedarProcess.CheckAbort[]; -- respond to Stop button G3dRenderWithPixels.AllocatePixelMemory[ context ];-- get new set of pixels each frame [] _ G3dColorDisplaySupport.GetAIS[ context, getname[i] ! G3dRender.Error => IF code = $NotFound THEN { frameFailed _ TRUE; CONTINUE; } ]; IF NOT frameFailed THEN firstFrameFound _ TRUE -- found frame, legit sequence started ELSE IF NOT firstFrameFound -- frame failed, has a sequence been started? THEN IF i < start + 16 -- try for 16 consecutive frames, just in case THEN { firstFrame _ i _ i + 1; frameFailed _ FALSE; LOOP; } ELSE EXIT -- frame not found after 16 tries, assume bad name ELSE EXIT; -- must be end of sequence, earlier frames found but not this one IF frames.maxLength <= i - firstFrame THEN { -- extend FrameSequence if necessary newFrames: REF FrameSequence _ NEW[ FrameSequence[frames.maxLength+16] ]; FOR j: NAT IN [0..frames.maxLength) DO newFrames[j] _ frames[j]; ENDLOOP; frames _ newFrames; }; frames[i - firstFrame] _ NARROW[ Atom.GetPropFromList[context.props, $TempPixels] ]; IF frames.labels AND context.viewer # NIL THEN G3dColorDisplaySupport.GetPixelsFromViewer[context, frames[i - firstFrame]]; context.props _ Atom.RemPropFromList[context.props, $TempPixels]; -- save VM frames.length _ i - firstFrame + 1; i _ i + 1; IF numFiles > 0 AND i - start >= numFiles THEN EXIT; -- found requested no. of files ENDLOOP; IF NOT firstFrameFound THEN { frames _ NIL; -- no frames found, prevent further mistakes SIGNAL G3dRender.Error[$NotFound, "File not found by that name"]; }; }; frames: REF FrameSequence _ NEW[ FrameSequence[numFiles] ]; IF labeled THEN frames.labels _ TRUE; CedarProcess.DoWithPriority[background, Action]; IF context.stopMe^ THEN RETURN[NIL] ELSE RETURN[frames]; }; PlayBackAISCache: PUBLIC PROC[context: Context, frames: REF FrameSequence, framesPerSec, secondsPlayingTime: NAT, bothWays: BOOLEAN _ FALSE, startNum: NAT _ 0] ~ { vt: Terminal.Virtual _ context.terminal; waitCount, transferTime: NAT; repetitions: INT _ MAX[1, INT[secondsPlayingTime] * framesPerSec / (frames.length-startNum)]; IF frames = NIL THEN { SIGNAL G3dRender.Error[$MisMatch, "NoCachedFrames"]; RETURN; }; context.stopMe^ _ FALSE; -- get stop flag unstuck, if necessary context.imageReady _ FALSE; -- discourage use of bits by other processes context.class.validateDisplay[context]; -- update viewport, etc. [waitCount, transferTime] _ GetWaitCount[context, framesPerSec]; IF waitCount = 0 THEN repetitions _ (secondsPlayingTime * 75 / transferTime) / (frames.length - startNum); IF bothWays THEN repetitions _ repetitions/2; FOR i: INT IN [0..repetitions) DO -- play back FOR j: NAT IN [startNum..frames.length) DO FOR k: NAT IN [0..waitCount) DO vt.WaitForBWVerticalRetrace[]; ENDLOOP; frames.currentFrame _ j; IF context.stopMe^ THEN RETURN[]; -- shut down if stop signal received CedarProcess.CheckAbort[]; -- respond to Stop button ShowAISCacheFrame[context, frames]; ENDLOOP; IF bothWays THEN FOR j: NAT DECREASING IN [startNum..frames.length) DO FOR k: NAT IN [0..waitCount) DO vt.WaitForBWVerticalRetrace[]; ENDLOOP; frames.currentFrame _ j; IF context.stopMe^ THEN RETURN[]; -- shut down if stop signal received CedarProcess.CheckAbort[]; -- respond to Stop button ShowAISCacheFrame[context, frames]; ENDLOOP; ENDLOOP; context.imageReady _ TRUE; -- encourage use of bits by other processes }; ShowNextAISCacheFrame: PUBLIC PROC[context: Context, frames: REF FrameSequence, framesPerSec: INTEGER] ~ { waitCount: NAT; vt: Terminal.Virtual _ Terminal.Current[]; IF framesPerSec < 0 THEN frames.currentFrame _ (frames.currentFrame + frames.length-1) MOD frames.length ELSE frames.currentFrame _ (frames.currentFrame + 1) MOD frames.length; [waitCount: waitCount] _ GetWaitCount[context, ABS[framesPerSec]]; FOR k: NAT IN [0..waitCount) DO vt.WaitForBWVerticalRetrace[]; ENDLOOP; IF context.stopMe^ THEN RETURN[]; -- shut down if stop signal received CedarProcess.CheckAbort[]; -- respond to Stop button ShowAISCacheFrame[context, frames]; }; NewFrame: ENTRY ViewProc ~ { ENABLE UNWIND => NULL; filename: ROPE _ NARROW[Atom.GetPropFromList[context.props, $OutputFile]]; G3dRender.SetView[ context, lookingFrom, lookingAt, context.fieldOfView, context.rollAngle, context.upDirection, context.hitherLimit, context.yonLimit ]; context.class.render[context]; -- make frame IF context.stopMe^ THEN RETURN[]; CedarProcess.CheckAbort[]; -- check for external abort request before proceeding IF filename # NIL THEN -- if storing frames IF context.class.displayType = $FullColor AND NotRGB[filename] THEN { -- dither to 8 bits per pixel for storage savings ditherContext: Context _ NARROW[ Atom.GetPropFromList[context.props, $DitherContext] ]; G3dColorDisplaySupport.DitherImage[ditherContext, context]; G3dRender.StoreImage[ditherContext, filename, frameNo]; } ELSE G3dRender.StoreImage[context, filename, frameNo]; }; NotRGB: PROC[fileRoot: Rope.ROPE] RETURNS[BOOLEAN] ~ { -- not an RGB file name? cp: FS.ComponentPositions; fullFName, ext: Rope.ROPE; [fullFName, cp, ] _ FS.ExpandName[fileRoot]; ext _ Rope.Substr[ fullFName, cp.ext.start, cp.ext.length]; IF Rope.Equal[ext, "rgb", FALSE] THEN RETURN[FALSE] ELSE RETURN[TRUE]; }; SetUpAnimation: PROC[ context: Context, filename: ROPE _ NIL ] ~ { context.stopMe^ _ FALSE; -- clear stop flag IF context.preferredRenderMode = $Imager OR filename # NIL -- using imager / writing files THEN G3dRenderWithPixels.BufferRendering[context, FALSE]; -- then draw on screen IF filename # NIL THEN { context.props _ Atom.PutPropOnList[context.props, $OutputFile, filename]; IF context.class.displayType = $FullColor THEN IF NotRGB[filename] THEN { ditherContext: Context _ G3dRender.Create[]; ditherContext.class _ NEW[ ContextClass _ G3dRender.GetDisplayClass[$PseudoColor] ]; G3dSortandDisplay.ValidateContext[context]; -- get everything straight ditherContext.viewPort _ context.viewPort; G3dRenderWithPixels.AllocatePixelMemory[ditherContext]; context.props _ Atom.PutPropOnList[context.props, $DitherContext, ditherContext]; } ELSE { -- file being prepared for Abekas context.viewPort _ NEW[Rectangle _ [ 0.0, 0.0, 720.0, 486.0 ]]; G3dRenderWithPixels.AllocatePixelMemory[context]; -- get buffer memory of right size G3dRender.SetViewPort[ context, [0.0, 0.0, 720.0, 486.0] ]; G3dRender.SetWindow[context, [x: -1.0, y: -0.75, w: 2.0, h: 1.5] ]; context.pixelAspectRatio _ (4.0/3.0) / (720.0/486.0); -- pixel width/height }; }; }; FinishAnimation: PROC[ context: Context ] ~ { context.props _ Atom.RemPropFromList[context.props, $OutputFile]; context.props _ Atom.RemPropFromList[context.props, $DitherContext]; }; Orbit: PUBLIC PROC[ context: Context, lookingFrom, lookingAt, axis, base: Triple, moveEPNotCI: BOOLEAN _ TRUE, framesPerRev: NAT _ 16, startAt: NAT _ 0, endAt: NAT _ 32767, filename: ROPE _ NIL ] ~ { ENABLE UNWIND => NULL; SetUpAnimation[context, filename]; IF moveEPNotCI THEN MoveEyePointInOrbit[ context, lookingFrom, lookingAt, axis, base, NewFrame, framesPerRev, startAt, endAt ] ELSE MoveCtrOfInterestInOrbit[ context, lookingFrom, lookingAt, axis, base, NewFrame, framesPerRev, startAt, endAt ]; FinishAnimation[context]; }; MakeFramesFromTo: PUBLIC PROC[context: Context, lookingFrom, lookingAt, toLookingFrom, toLookingAt: Triple, framesOnLine: NAT, startAt, endAt: NAT _ 0, filename: ROPE _ NIL] ~ { ENABLE UNWIND => NULL; SetUpAnimation[context, filename]; IF endAt = 0 THEN endAt _ framesOnLine; -- default is full number of frames MoveOnLine[ context, lookingFrom, lookingAt, toLookingFrom, toLookingAt, NewFrame, framesOnLine, startAt, endAt ]; FinishAnimation[context]; }; TripletoRope: PROC[ r: Triple] RETURNS[ROPE] ~ { rope: ROPE; rope _ Rope.Cat[ " ", Convert.RopeFromReal[r.x], " ", Convert.RopeFromReal[r.y] ]; rope _ Rope.Cat[ rope, " ", Convert.RopeFromReal[r.z], " " ]; RETURN[ rope ]; }; MakeFramesOnPath: PUBLIC PROC[ context: Context, lookingFrom, lookingAt: LIST OF Triple, framesOnPath: NAT, startAt, endAt: NAT _ 0, filename: ROPE _ NIL, closed: BOOLEAN _ TRUE ] ~ { ENABLE UNWIND => NULL; ExpandSequence: PROC[seq: TripleSequence] ~ { newSeq: TripleSequence _ NEW[TripleSequenceRep[seq.length + 64]]; FOR i: NAT IN [0..seq.length) DO newSeq[i] _ seq[i]; ENDLOOP; seq _ newSeq; }; lookingFroms: TripleSequence _ NEW[TripleSequenceRep[16]]; lookingAts: TripleSequence _ NEW[TripleSequenceRep[16]]; i: NAT _ 0; SetUpAnimation[context, filename]; FOR list: LIST OF Triple _ lookingFrom, list.rest UNTIL list = NIL DO lookingFroms[i] _ list.first; i _ i + 1; IF i >= lookingFroms.maxLength THEN ExpandSequence[lookingFroms]; ENDLOOP; lookingFroms.length _ i; i _ 0; FOR list: LIST OF Triple _ lookingAt, list.rest UNTIL list = NIL DO lookingAts[i] _ list.first; i _ i + 1; IF i >= lookingAts.maxLength THEN ExpandSequence[lookingAts]; ENDLOOP; lookingAts.length _ i; IF endAt = 0 THEN endAt _ framesOnPath; IF closed THEN MoveOnClosedCurve[ context, lookingFroms, lookingAts, NewFrame, framesOnPath, startAt, endAt ] ELSE MoveOnOpenCurve[ context, lookingFroms, lookingAts, NewFrame, framesOnPath, startAt, endAt ]; FinishAnimation[context]; }; MoveEyePointInOrbit: PUBLIC PROC[ context: Context, eyePt, lookingAt, axis, base: Triple, displayProc: ViewProc, framesPerRev: NAT, startAt, endAt: NAT _ 0 ] ~ { theta: REAL _ 360. / framesPerRev; currentEyePt: Triple; FOR i: NAT IN [startAt .. endAt] DO IF context.stopMe^ THEN RETURN[]; currentEyePt _ G3dMatrix.Transform[ eyePt, G3dMatrix.MakeRotateAbout[ axis: axis, theta: theta * i, base: base ] ]; context.frameNumber _ i; displayProc[ context, currentEyePt, lookingAt, context.frameNumber ]; ENDLOOP; context.frameNumber _ 0; }; MoveCtrOfInterestInOrbit: PUBLIC PROC[ context: Context, eyePt, lookingAt, axis, base: Triple, displayProc: ViewProc, framesPerRev: NAT, startAt, endAt: NAT _ 0 ] ~ { theta: REAL _ 360. / framesPerRev; currentLookingAt: Triple; FOR i: NAT IN [startAt .. endAt] DO IF context.stopMe^ THEN RETURN[]; currentLookingAt _ G3dMatrix.Transform[ lookingAt, G3dMatrix.MakeRotateAbout[ axis: axis, theta: theta * i, base: base ] ]; context.frameNumber _ i; displayProc[ context, eyePt, currentLookingAt, context.frameNumber ]; ENDLOOP; context.frameNumber _ 0; }; MoveOnLine: PUBLIC PROC[ context: Context, eyePt, lookingAt, toEyePt, toLookingAt: Triple, displayProc: ViewProc, framesOnLine: NAT, startAt, endAt: NAT _ 0 ] ~ { currentEyePt, currentLookingAt: Triple; FOR i: NAT IN [startAt .. endAt] DO IF context.stopMe^ THEN RETURN[]; currentEyePt.x _ eyePt.x + (toEyePt.x - eyePt.x) * i / (framesOnLine-1); currentEyePt.y _ eyePt.y + (toEyePt.y - eyePt.y) * i / (framesOnLine-1); currentEyePt.z _ eyePt.z + (toEyePt.z - eyePt.z) * i / (framesOnLine-1); currentLookingAt.x _ lookingAt.x + (toLookingAt.x - lookingAt.x) * i / (framesOnLine-1); currentLookingAt.y _ lookingAt.y + (toLookingAt.y - lookingAt.y) * i /(framesOnLine-1); currentLookingAt.z _ lookingAt.z + (toLookingAt.z - lookingAt.z) * i / (framesOnLine-1); context.frameNumber _ i; displayProc[ context, currentEyePt, currentLookingAt, context.frameNumber ]; ENDLOOP; context.frameNumber _ 0; }; MoveOnOpenCurve: PUBLIC PROC[ context: Context, eyePts, lookingAts: TripleSequence, displayProc: ViewProc, framesOnCurve: NAT, startAt, endAt: NAT _ 0 ] ~ { currentEyePt, currentLookingAt: TripleSequence; epCoeffs: G3dSpline.SplineSequence _ G3dSpline.Interpolate[eyePts]; ciCoeffs: G3dSpline.SplineSequence _ G3dSpline.Interpolate[lookingAts]; currentEyePt _ ExpandCurve[epCoeffs, framesOnCurve]; currentLookingAt _ ExpandCurve[ciCoeffs, framesOnCurve]; IF context.stopMe^ THEN RETURN[]; FOR i: NAT IN [startAt .. endAt] DO j: NAT _ i MOD framesOnCurve; context.frameNumber _ i; displayProc[ context, currentEyePt[j], currentLookingAt[j], context.frameNumber ]; ENDLOOP; context.frameNumber _ 0; }; MoveOnClosedCurve: PUBLIC PROC[ context: Context, eyePts, lookingAts: TripleSequence, displayProc: ViewProc, framesOnCurve: NAT, startAt, endAt: NAT _ 0 ] ~ { currentEyePt, currentLookingAt: TripleSequence; epCoeffs: G3dSpline.SplineSequence _ G3dSpline.InterpolateCyclic[eyePts]; ciCoeffs: G3dSpline.SplineSequence _ G3dSpline.InterpolateCyclic[lookingAts]; currentEyePt _ ExpandCurve[epCoeffs, framesOnCurve]; currentLookingAt _ ExpandCurve[ciCoeffs, framesOnCurve]; FOR i: NAT IN [startAt .. endAt] DO j: NAT _ i MOD framesOnCurve; IF context.stopMe^ THEN RETURN[]; context.frameNumber _ i; displayProc[ context, currentEyePt[j], currentLookingAt[j], context.frameNumber ]; ENDLOOP; context.frameNumber _ 0; }; ExpandCurve: PROC[coeffs: G3dSpline.SplineSequence, numFrames: NAT] RETURNS[TripleSequence] ~ { points: TripleSequence _ NEW[TripleSequenceRep[numFrames]]; framesPerSeg: REAL _ Real.Float[numFrames - 1] / (coeffs.length); FOR i: NAT IN [0..numFrames - 1) DO t: REAL _ i / framesPerSeg; -- distance, in segments, along curve seg: NAT _ Real.Fix[t]; -- segment t _ t - seg; -- position in segment points[i] _ G3dSpline.Position[coeffs[seg], t]; ENDLOOP; points[numFrames - 1] _ G3dSpline.Position[coeffs[coeffs.length - 1], 1.0]; points.length _ numFrames; RETURN[points]; }; ShowOpenCurve: PUBLIC PROC[ context: Context, eyePts, lookingAts: TripleSequence, numFrames: NAT, startAt: NAT _ 0 ] ~ { SIGNAL G3dRender.Error[$Unimplemented, "Sorry, ask Frank for an implementation"]; }; ShowClosedCurve: PUBLIC PROC[ context: Context, eyePts, lookingAts: TripleSequence, numFrames: NAT, startAt: NAT _ 0 ] ~ { SIGNAL G3dRender.Error[$Unimplemented, "Sorry, ask Frank for an implementation"]; }; ShowOrbit: PUBLIC PROC[ context: Context, startPt, axis, base: Triple, framesPerRev: NAT ] ~ { SIGNAL G3dRender.Error[$Unimplemented, "Sorry, ask Frank for an implementation"]; }; SetTxtrTranslation: PUBLIC PROC[ context: Context, shapeName: Rope.ROPE, translation: Pair, frames: NAT ] ~{ shape: Shape _ G3dRender.FindShape[context, shapeName]; translation.x _ translation.x / frames; translation.y _ translation.y / frames; }; LoadTranslatedTxtrCoords: G3dRender.CtlPtInfoProc ~ { -- load aux field in vtx frameNumber: NAT _ context.frameNumber; RETURN[ vtx ]; }; SetShapeInterpolation: PUBLIC PROC[ context: Context, shapeName: Rope.ROPE, begin, end: REF Shape, frames: NAT ] ~{ shape: Shape _ G3dRender.FindShape[context, shapeName]; renderData: REF RenderData _ G3dRender.RenderDataFrom[shape]; renderData.shadingProps _ PutProp[ renderData.shadingProps, $ShapeLerp, NEW[LORA _ LIST[ begin, end, NEW[ NAT _ frames] ] ] ]; renderData.class.doBeforeFrame _ CONS[LerpShape, renderData.class.doBeforeFrame]; }; LerpShape: G3dRender.ShapeProc ~ { frameNumber: NAT _ context.frameNumber; renderData: REF RenderData _ G3dRender.RenderDataFrom[shape]; list: LORA _ NARROW[GetProp[renderData.shadingProps, $ShapeLerp] ]; begin: REF Shape _ NARROW[list.first]; end: REF Shape _ NARROW[list.rest.first]; lastFrame: NAT _ NARROW[list.rest.rest.first, REF NAT]^; alpha: REAL _ 1.0 * frameNumber / lastFrame; FOR i: NAT IN [0..shape.vertices.length) DO shape.vertices[i].point _ G3dVector.Add[ begin.vertices[i].point, G3dVector.Mul[ G3dVector.Sub[end.vertices[i].point, begin.vertices[i].point], alpha ] ]; shape.vertices[i].normal _ G3dVector.Add[ begin.vertices[i].normal, G3dVector.Mul[ G3dVector.Sub[end.vertices[i].normal, begin.vertices[i].normal], alpha ] ]; ENDLOOP; renderData.patch _ NIL; -- must rebuild patches shape.renderValid _ FALSE; RETURN[shape]; }; SetShapeManipulation: PUBLIC PROC[ context: Context, shapeName: Rope.ROPE, prop, propVal: REF ANY, proc: G3dRender.ShapeProc ] ~ { shape: Shape _ G3dRender.FindShape[context, shapeName]; renderData: REF RenderData _ G3dRender.RenderDataFrom[shape]; renderData.shadingProps _ PutProp[ renderData.shadingProps, prop, propVal ]; renderData.class.doBeforeFrame _ CONS[ proc, NIL ]; }; AddShapeManipulation: PUBLIC PROC[ context: Context, shapeName: Rope.ROPE, prop, propVal: REF ANY, proc: G3dRender.ShapeProc ] ~ { shape: Shape _ G3dRender.FindShape[context, shapeName]; renderData: REF RenderData _ G3dRender.RenderDataFrom[shape]; renderData.shadingProps _ PutProp[ renderData.shadingProps, prop, propVal ]; renderData.class.doBeforeFrame _ CONS[ proc, renderData.class.doBeforeFrame ]; }; END. G3dAnimationSupportImpl.mesa Copyright c 1984 by Xerox Corporation. All rights reserved. Last Edited by: Crow, August 8, 1989 1:09:13 pm PDT Bloomenthal, September 14, 1988 1:05:33 pm PDT Basic Types Renamed Procedures Procedures for Storage and Playback of Image Sequences from Files Get pixels left in proplist by GetAIS Copy pixels from viewer to stored frame to get label in stored frame Procedures for Making Simple Animations PROC[context: Context, lookingFrom, lookingAt: Triple, frameNo: NAT] Animate along curved path of typed in points Procedures for Eyepoint-Ctr of Interest Trajectories Call display procedure Call display procedure Procedures for Viewing Trajectories Adds description of path to data for current scene, will be displayed just like any other shape. "Trajectory" is name given to resulting Shape. Procedures for Texture Animation Sets up translation by [translation.x, translation.y] over "frames" frames shape.shadingProps _ PutProp[ shape.shadingProps, $TxtrTranslation, NEW[Pair _ translation] ]; shape.shadingClass.loadVtxAux _ LoadTranslatedTxtrCoords; PROC[ context: Context, vtx: REF CtlPtInfo, data: REF ANY _ NIL ] RETURNS[REF CtlPtInfo]; shape: Shape _ NARROW[ Atom.GetPropFromList[vtx.props, $Shape] ]; txtrIncr: REF Pair _ NARROW[GetProp[shape.shadingProps, $TxtrTranslation] ]; vtxAux: REF Pair _ NEW[Pair]; WITH data SELECT FROM input: LORA => { auxInfo: PairSequence _ NARROW[ input.first ]; index: INTEGER _ NARROW[ input.rest.first, REF INTEGER ]^; vtxAux.x _ auxInfo[index].x + txtrIncr.x * frameNumber; -- texture coords vtxAux.y _ auxInfo[index].y + txtrIncr.y * frameNumber; }; txtr: REF Pair => { vtxAux.x _ txtr.x + txtrIncr.x * frameNumber; vtxAux.y _ txtr.y + txtrIncr.y * frameNumber; }; ENDCASE => SIGNAL G3dRender.Error[$Unimplemented, "Unrecognized type"]; vtx.aux _ vtxAux; Procedures for Shape Interpolation Sets up translation by [translation.x, translation.y] over "frames" frames PROC[context: Context, shape: REF Shape, data: REF ANY _ NIL] RETURNS[REF Shape] Translates texture coordinates over sequence of frames Procedure for Registering Shape Manipulation Procedures for Animation Way of registering procedure to be called to modify shape before each frame Way of registering procedure to be called to modify shape before each frame Κ/˜Iheadšœ™šœ Οmœ1™žœžœžœ žœ ˜ΗJšœ(˜(Nšœžœ˜Nšœ žœžœ žœ@˜fJ˜šžœ žœžœ˜Nšžœ0žœ˜?N˜—Jšœžœ‘&˜EJšœžœ‘,˜KIdefaultšœ,‘˜DN˜Ašžœ˜JšžœU˜Y—Nšžœ žœ˜-š žœžœžœžœ‘ ˜5šžœžœžœž˜*Nš žœžœžœžœ žœ˜GN˜Jšžœžœžœ‘$˜GNšœ‘˜6N˜#Nšžœ˜—š žœ žœžœžœž œžœž˜FNš žœžœžœžœ žœ˜GN˜Jšžœžœžœ‘$˜GNšœ‘˜6N˜#Nšžœ˜—Nšžœ˜—Jšœžœ‘+˜IN˜—š Ÿœžœžœ'žœžœ˜vNšœ žœ˜Jšœ*˜*šžœ˜Nšžœ@žœ˜VNšžœ2žœ˜H—Nšœ/žœ˜BNš žœžœžœžœ žœ˜GJšžœžœžœ‘$˜GNšœ‘˜6N˜#N˜—M™—šœ'™'šŸœžœ ˜Jšžœ<žœ™DJšžœžœžœ˜Nšœ žœžœ4˜KJ˜«Nšœ ‘ ˜-Nšžœžœžœ˜!Nšœ‘5œ˜Sšžœ žœžœ‘˜/šžœ(žœ˜>šžœ ‘1˜ANšœžœ˜ N˜3N˜J˜;J˜7N˜—Jšžœ2˜6——N˜—š Ÿœžœžœžœžœ‘˜ONšœžœ+žœ˜6Jšœžœ˜.N˜;Nšžœžœžœžœžœžœžœžœ˜FN˜—šŸœžœžœžœ˜BNšœžœ ‘˜6šžœ'žœ žœ‘˜ZNšžœ.žœ‘˜Q—šžœ žœžœ˜J˜Išžœ'˜)šžœžœ˜šžœ˜Nšœ,˜,šœžœ˜J˜6J˜—Nšœ.‘˜HN˜*N˜7N˜QN˜—šžœ‘!˜,Nšœžœ+˜ANšœ2‘"˜TN˜;J˜CNšœ7‘˜MN˜———N˜—N˜—šŸœžœ˜-J˜AJ˜DN˜—šŸœžœžœWžœžœžœžœ žœžœžœ˜δJšžœžœžœ˜N˜#šžœ ˜šžœ˜N˜-N˜'N˜—šžœ˜N˜.N˜'N˜——N˜J˜—šŸœžœžœpžœžœžœžœ˜ΝJšžœžœžœ˜N˜#Nšžœ žœ‘#˜MN˜„N˜J˜J˜—šŸ œžœ žœžœ˜0Jšœžœ˜ J˜SJ˜=Jšžœ ˜J˜—J˜šŸœžœžœ:žœžœ&žœžœžœžœ žœžœ˜βN™,Jšžœžœžœ˜šŸœžœ˜-Jšœžœ%˜AJš žœžœžœžœžœ˜>J˜ J˜—Nšœžœ˜:Nšœžœ˜8Nšœžœ˜ N˜#š žœžœžœ!žœžœž˜EN˜+Nšžœžœ˜ANšžœ˜—N˜N˜š žœžœžœžœžœž˜CN˜)Nšžœžœ˜=Nšžœ˜—N˜Nšžœ žœ˜'šžœ˜ Nšžœl˜pNšžœl˜p—N˜J˜J˜—M™—šœ4™4š Ÿœžœžœ‹žœžœ ˜ΝJšœžœ˜#J˜šžœžœžœž˜#Nšžœžœžœ˜!˜#J˜˜J˜ J˜J˜ J˜—J˜Jšœ™—J˜J˜EJšžœ˜—J˜J˜—š Ÿœžœžœ”žœ%žœ ˜ξJšœžœ˜#J˜šžœžœžœž˜#Nšžœžœžœ˜!˜'J˜ ˜J˜ J˜J˜ J˜—J˜Jšœ™—J˜J˜EJšžœ˜—J˜J˜—š Ÿ œžœžœˆžœžœ ˜ΑJ˜'šžœžœžœž˜#Nšžœžœžœ˜!J˜HJ˜HJ˜HJ˜XJ˜WJ˜XJ˜J˜LJšžœ˜—J˜J˜J˜—š Ÿœžœžœžœžœ ˜ΛJšœ/˜/JšœC˜CJšœG˜GJ˜4J˜8Nšžœžœžœ˜!šžœžœžœž˜#Jšœžœžœ˜J˜J˜RJšžœ˜—J˜N˜N˜—š ŸœžœžœŠžœžœ ˜ΚJšœ/˜/JšœI˜IJšœM˜MJ˜4J˜8šžœžœžœž˜#Jšœžœžœ˜Nšžœžœžœ˜!J˜J˜RJšžœ˜—J˜N˜N˜—šŸ œžœ.žœ žœ˜gJšœžœ˜;Jšœžœ/˜Ašžœžœžœž˜#Jšœžœ‘%˜EJšœžœ‘ ˜(Jšœ‘˜,J˜/Jšžœ˜—J˜KJ˜Jšžœ ˜J˜——™#š Ÿ œžœžœMžœ žœ ˜‚NšžœK˜QN˜—š ŸœžœžœNžœ žœ ˜…NšžœK˜QN˜—šŸ œžœžœ?žœ˜^J™NšžœK˜QJ˜—N˜—™ š Ÿœžœžœ$žœ)žœ˜xJ™JJšœ7˜7J˜'J˜'™Jšœ&žœ™>J™—J™9J˜—šŸœ‘˜NJšžœžœžœžœžœžœžœ ™YJšœ žœ˜'Jšœžœ,™BJšœ žœžœ1™LNšœžœžœ™šžœžœž™šœžœ™Jšœžœ™/Nš œžœžœžœžœ™:Nšœ=‘™NN™7N™—šœžœ ™N™1N™/N™—Nšžœžœ6™G—O™Ošžœ˜O˜——™"š Ÿœžœžœ$žœžœžœ˜€J™JJšœ7˜7Jšœ žœ.˜=šœ"˜"Jšœ%˜%Jš žœžœžœžœžœ˜4J˜—Jšœ!žœ,˜QJ˜—šŸ œ˜"Jšžœžœžœžœžœžœžœ™PJ™6Jšœ žœ˜'Jšœ žœ.˜=Jšœžœžœ0˜CJšœžœ žœ ˜&Jšœžœ žœ˜)Jš œ žœžœžœžœ˜8Jšœžœ!˜,šžœžœžœž˜+˜(J˜J˜UJ˜—˜)J˜J˜WJ˜—Jšžœ˜—Jšœžœ‘˜2Jšœžœ˜Jšžœ˜J˜——™Eš Ÿœžœžœ$žœžœžœ/˜žJ™KJšœ7˜7Jšœ žœ.˜=JšœL˜LJšœ!žœžœ˜3J˜—š Ÿœžœžœ$žœžœžœ/˜žJ™KJšœ7˜7Jšœ žœ.˜=JšœL˜LJšœ!žœ)˜NJ˜——Jšžœ˜—…—Wug