G3dColorDisplayRenderImpl.mesa
Last Edited by: Crow, April 4, 1989 9:56:19 am PDT
Glassner, March 14, 1989 2:18:27 pm PST
Bloomenthal, December 29, 1988 9:06:41 pm PST
DIRECTORY
Basics     USING [ BITAND, BITSHIFT ],
Atom     USING [ GetPropFromList, PutPropOnList, RemPropFromList ],
Process    USING [ Pause, SecondsToTicks ],
CedarProcess   USING [ Abort, CheckAbort, DoWithPriority, Fork, ForkableProc, Join, Process
        ],
ViewerClasses  USING [ Viewer ],
Terminal    USING [ ColorMode, Current, GetColorFrameBufferA,
         GetColorFrameBufferB, GetColorMode, SetColor, SetBlueMap,
         SetGreenMap, SetRedMap, Virtual ],
FS      USING [ ComponentPositions, ExpandName ],
IO      USING [ STREAM ],
Convert    USING [ RopeFromReal ],
Rope     USING [ Cat, Equal, ROPE, Substr ] ,
Real     USING [ Float, Fix, Round ],
RealFns    USING [ Power ],
ColorDisplayManager USING [ Start ],
Imager    USING [ Context, Rectangle ],
ImagerBackdoor  USING [ AccessBufferRectangle ],
ImagerSample  USING [ Box, GetBase, GetBitsPerLine, GetBox, GetRef, GetSamples,
         MapFromFrameBuffer, NewSamples,
         ObtainScratchSamples, PutSamples, RasterSampleMap,
         ReleaseScratchSamples, SampleBuffer, Transfer,
         UnsafeNewSampleMap, WordsForLines ],
ImagerPixel   USING [ GetPixels, MakePixelMap, NewPixels, PixelBuffer, PixelMap,
         PutPixels ],
G3dRender  USING [ Box, ClipState, Context, ContextClass, ContextProc, Create, Error,
         GetDisplayType, ImagerProc, ImagerProcRec,
         NatSequence, Pair, PairSequence, Patch, PatchProc, Pixel, Quad,
         Rectangle, RegisterDisplayType, RGB, SetView,
         ShadingValue, ShapeInstance, ShapeSequence, Triple,
         TripleSequence, Vertex ],
SceneUtilities  USING [ GetTmpContext, SetViewPort, SetWindow, StartLog ],
G3dSurfaceRender  USING [ ValidateContext ],
G3dRenderWithPixels USING [ AntiAliasing, BufferRendering, DepthBuffering, DoRope,
         DrawRope, AllocatePixelMemory, Draw2DPoly, Draw2DLine,
         FillInBackGround, MakeFrame, PolygonTiler, RopeDesc ],
Animation3D   USING [ MoveCtrOfInterestInOrbit, MoveEyePointInOrbit, MoveOnLine,
         MoveOnClosedCurve, MoveOnOpenCurve, ViewProc ],
AISAnimation  USING [ StoreFiles ],
G3dColorDisplayRender USING [ ];
G3dColorDisplayRenderImpl: CEDAR MONITOR
IMPORTS AISAnimation, Animation3D, Atom, Basics, CedarProcess, ColorDisplayManager, Convert, FS, ImagerBackdoor, ImagerPixel, ImagerSample, Process, Real, RealFns, G3dRenderWithPixels, Rope, SceneUtilities, G3dSurfaceRender, Terminal, G3dRender
EXPORTS G3dColorDisplayRender
~ BEGIN
Types
ROPE:    TYPE ~ Rope.ROPE;
RopeDesc:   TYPE ~ G3dRenderWithPixels.RopeDesc;
Context:   TYPE ~ G3dRender.Context;
ContextProc:  TYPE ~ G3dRender.ContextProc;
ImagerProc:  TYPE ~ G3dRender.ImagerProc;
ContextClass:  TYPE ~ G3dRender.ContextClass;
Box:    TYPE ~ G3dRender.Box;
Rectangle:  TYPE ~ G3dRender.Rectangle;
Triple:   TYPE ~ G3dRender.Triple;
TripleSequence: TYPE ~ G3dRender.TripleSequence;
RGB:     TYPE ~ G3dRender.RGB;
Pixel:    TYPE ~ G3dRender.Pixel;
Pair:     TYPE ~ G3dRender.Pair;
PairSequence:  TYPE ~ G3dRender.PairSequence;
IntRGB:    TYPE ~ RECORD [r, g, b: CARDINAL];
IntRGBSequence: TYPE ~ RECORD [SEQUENCE length: NAT OF IntRGB];
NatSequence:  TYPE ~ G3dRender.NatSequence;
Patch:    TYPE ~ G3dRender.Patch;
PatchProc:   TYPE ~ G3dRender.PatchProc;
ClipState:   TYPE ~ G3dRender.ClipState;
PixelBuffer:   TYPE ~ ImagerPixel.PixelBuffer;
PixelMap:   TYPE ~ ImagerPixel.PixelMap;
ImagerProcRec:  TYPE ~ G3dRender.ImagerProcRec;
Globals
timeResolution: CARD16 ← 3;
Utility Procedures
Ceiling: PROC[number: REAL] RETURNS[result: INTEGER] ~ {
result ← Real.Round[number];
IF result < number THEN result ← result + 1;
};
Floor: PROC[ in: REAL ] RETURNS[ out: INTEGER ] ~ {
out ← Real.Round[in];
IF Real.Float[out] > in THEN out ← out - 1;
};
UpdateFullColorDisplay: PROC [context: REF Context] ~ {
screenPixels: PixelMap NARROW[Atom.GetPropFromList[context.displayProps, $ScreenPixels]];
xMin: NAT ← context.pixels.box.min.f;
width: NAT ← context.pixels.box.max.f-xMin;
buf1: ImagerSample.SampleBuffer ← ImagerSample.ObtainScratchSamples[width];
buf2: ImagerSample.SampleBuffer ← ImagerSample.ObtainScratchSamples[width];
DoUpdateFullColorDisplay[context, screenPixels, buf1, buf2, xMin, width];
ImagerSample.ReleaseScratchSamples[buf1];
ImagerSample.ReleaseScratchSamples[buf2];
};
PeriodicUpdateFullColorDisplay: CedarProcess.ForkableProc ~ {
Updates from buffered pixel map
context: REF Context ← NARROW[data];
screenPixels: PixelMap NARROW[Atom.GetPropFromList[context.displayProps, $ScreenPixels]];
height: NAT ← screenPixels.box.max.s; width: NAT ← screenPixels.box.max.f/2;
xMin: NAT ← context.pixels.box.min.f;
width: NAT ← context.pixels.box.max.f-xMin;
waitTime: CARD16 ← timeResolution*3;
buf1: ImagerSample.SampleBuffer ← ImagerSample.NewSamples[width];
buf2: ImagerSample.SampleBuffer ← ImagerSample.NewSamples[width];
DO    -- stuff buffered pixels onto screen every waitTime seconds
Process.Pause[Process.SecondsToTicks[waitTime]];
DoUpdateFullColorDisplay[context, screenPixels, buf1, buf2, xMin, width];
CedarProcess.CheckAbort[ ];
ENDLOOP;
};
DoUpdateFullColorDisplay: PROC [
context: REF Context,
screenPixels: PixelMap,
buf1, buf2: ImagerSample.SampleBuffer,
xMin, width: NAT]
~ {
IF screenPixels # NIL
THEN FOR i: NAT IN [context.pixels.box.min.s..context.pixels.box.max.s) DO
Read 8 bit samples into red buffer:
ImagerSample.GetSamples[
map: context.pixels[0], initIndex: [i, xMin], buffer: buf1, count: width];
Read 8 bit samples into green buffer:
ImagerSample.GetSamples[
map: context.pixels[1], initIndex: [i, xMin], buffer: buf2, count: width];
Now, interleave the red and green 8 bit samples into the 16 bit red/green color display:
Write the red, starting at 0, with delta.f = 2, thus writing the even bytes:
ImagerSample.PutSamples[
map: screenPixels[0], initIndex: [i, 2*xMin], buffer: buf1, delta: [0, 2], count: width];
Write the green, starting at 1, with delta.f = 2, thus writing the odd bytes:
ImagerSample.PutSamples[
map: screenPixels[0], initIndex: [i, 1+2*xMin], buffer: buf2, delta: [0, 2], count: width];
ENDLOOP;
};
UpdateViewer: CedarProcess.ForkableProc ~ { -- updates viewer from buffered pixel map
PROC [data: REF] RETURNS [results: REFNIL]
context: REF Context ← NARROW[data];
waitTime: CARD16IF context.class.displayType = $FullColor THEN timeResolution * 3
                  ELSE timeResolution;
WHILE TRUE DO   -- stuff buffered pixels onto screen every waitTime seconds
Process.Pause[ Process.SecondsToTicks[waitTime] ];
context.class.drawInViewer[ context, NEW[ImagerProcRec ← [StuffBuf, NIL]] ];
CedarProcess.CheckAbort[ ];
ENDLOOP;
};
Initialize Standard Displays
GetDisplay: ContextProc ~ { 
Establishes PixelMap corresponding to bits on color display. If viewers are being buffered,
sets up pixel maps in VM to be blitted across as appropriate.
This is called by G3dRender.LoadDisplayType
IF context.viewer # NIL
THEN {
context.class.updateViewer[ context ];
G3dRenderWithPixels.AllocatePixelMemory[context];
}
ELSE GrabColorDisplay[context];    -- clear off color display and take it over
SELECT context.class.displayType FROM
$FullColor, $Gray => {
LoadColorRamp[ context.terminal, [0.,0.,0.], [1.,1.,1.], [.43, .43, .43] ];
};
$PseudoColor => {
LoadStd8BitClrMap[context.terminal];
};
ENDCASE => SIGNAL G3dRender.Error[[$MisMatch, "Unexpected displayType"]];
context.pixelAspectRatio ← 1.0;   -- standard square-pixel display assumed
context.displayInValid ← TRUE;
};
GrabColorDisplay: PROC[ context: REF Context ] ~ { 
For using color display without viewers
vt: Terminal.Virtual;
box: ImagerSample.Box;
s0, s2: ImagerSample.RasterSampleMap ← NIL;
clrType: ATOM;
IF context.terminal = NIL
THEN context.terminal ← vt ← Terminal.Current[]
ELSE vt ← context.terminal;
SELECT context.class.displayType FROM
$PseudoColor => clrType ← $Dither8;
$Gray   => clrType ← $Gray8;
$FullColor => clrType ← $FullColor;
ENDCASE => SIGNAL G3dRender.Error[[$MisMatch, "Unexpected Display type"]];
ColorDisplayManager.Start[
type: clrType,  -- ATOM, Colordisplay types: $Gray8, $Dither8, $Dither1, $FullColor
side: left,   -- Interminal.Side {left, right}
level: mouse,  -- ColorDisplayManager.Level ~ {off, allocated, visible, mouse, viewers}
resolution: none -- ColorDisplayDefs.ColorDisplayType {none, standard, highResolution}
];
SELECT context.class.displayType FROM
$FullColor => {
s0 ← ImagerSample.MapFromFrameBuffer[vt.GetColorFrameBufferA[]]; -- 16 bits, R&G
s2 ← ImagerSample.MapFromFrameBuffer[vt.GetColorFrameBufferB[]]; -- 8 bits, B
};
$PseudoColor, $Gray =>
s0 ← ImagerSample.MapFromFrameBuffer[vt.GetColorFrameBufferA[]];
ENDCASE => SIGNAL G3dRender.Error[[$MisMatch, "Unexpected Display type"]];
box ← ImagerSample.GetBox[s0];     -- get frame buffer size
G3dRenderWithPixels.AllocatePixelMemory[context];
IF context.class.displayType = $FullColor THEN {
Reform screen memory to address by 8-bit chunks
screenPxls: PixelMap;
TRUSTED {
reconfigure R&G color display sample maps as a big, double-width 8-bit sample map:
screenPxls ← ImagerPixel.MakePixelMap[
ImagerSample.UnsafeNewSampleMap[
box: [box.min, [box.max.s, 2 * box.max.f]],
bitsPerSample: 8,
bitsPerLine: ImagerSample.GetBitsPerLine[s0],
base: ImagerSample.GetBase[s0],
ref: ImagerSample.GetRef[s0],
words: ImagerSample.WordsForLines[ box.max.s, ImagerSample.GetBitsPerLine[s0] ]
]
];
};
context.displayProps ← Atom.PutPropOnList[context.displayProps, $ScreenPixels, screenPxls];
};
First sample map is on-screen, for full-color, first sample map is interleaved R&G, second is off-screen green, third is on-screen blue. Green and R&G and blitted together as necessary
IF s0 # NIL THEN context.pixels[0] ← s0;  -- on-screen mem (pseudoclr, grey, 16-bit R&G)
IF s2 # NIL THEN context.pixels[2] ← s2;  -- on-screen mem (Blue record for full-color)
context.viewPort ← NIL;
Computed by G3dSurfaceRender.ValidateView or ThreeDViewer.GetViewportFromViewer
context.window ← NIL;
};
ValidateDisplay: ContextProc ~{   -- update viewPort, etc.
IF context.viewer # NIL THEN {
IF Atom.GetPropFromList[ context.displayProps, $ViewerAdjusted ] # NIL
THEN context.class.drawInViewer[context, NIL];  -- get specs for new viewer
IF context.pixels = NIL OR context.pixels.samplesPerPixel = 1
THEN context.class.updateViewer[ context ];   -- drawing directly on screen
context.stopMe^ ← FALSE;    -- make sure stop button is released
};
IF context.pixels # NIL    -- check for mismatched viewPort and pixel storage
THEN IF context.pixels.box.max.f < context.viewPort.w
   OR context.pixels.box.max.s < context.viewPort.h
THEN G3dRenderWithPixels.AllocatePixelMemory[context];
};
Init: PROC[] ~ {       -- register Imager-based drawing types
standardClass: ContextClass ← [
displayType: $PseudoColor,
setUpDisplayType: GetDisplay,
validateDisplay: ValidateDisplay,
render: MakeFrame,
loadBackground: FillInBackGround,
draw2DLine: Draw2DLine,
draw2DPolygon: Draw2DPoly,
draw2DRope: Draw2DRope,
displayPolygon: PolygonTiler 
];
G3dRender.RegisterDisplayType[ standardClass, $PseudoColor ];
standardClass.displayType ← $FullColor;
G3dRender.RegisterDisplayType[ standardClass, $FullColor ];
standardClass.displayType ← $Gray;
G3dRender.RegisterDisplayType[ standardClass, $Gray ];
};
Colors
MappedRGB: PUBLIC PROC[context: REF Context, clr: Pixel] RETURNS[Pixel] ~ {
SELECT context.class.displayType FROM 
$ImagerDithered  => {
mapVal: NAT ← 24 * (clr[r]*5 / 256) + 4 * (clr[g]*6 / 256) + (clr[b]*4 / 256);
IF mapVal >= 60 THEN mapVal ← mapVal + 135;   -- move to top of map
clr[r] ← mapVal;
};
$PseudoColor => clr[r] ← 42 * (clr[r]*6 / 256) + 6 * (clr[g]*7 / 256) + (clr[b]*6 / 256) +2;
$Gray => clr[r] ← ( clr[r] + clr[g] + clr[b] ) / 3;
ENDCASE;    -- ignore other modes
RETURN[ clr ];
};
LoadStd8BitClrMap: PUBLIC PROC [vt: Terminal.Virtual] ~ {
Linearize: PROC [value: REAL] RETURNS[NAT] ~ {
RETURN[ Real.Round[RealFns.Power[value / 255.0, .43] * 255.0] ];
};
IF vt = NIL THEN vt ← Terminal.Current[];
vt.SetColor[0, 0, 0, 0, 0]; vt.SetColor[1, 0, 0, 0, 0];
FOR i: NAT IN [2..254) DO          -- 6 x 7 x 6 color cube
j: NAT ← i - 2;
red: NAT ← Linearize[51.0 * (j/42)];
grn: NAT ← Linearize[42.5 * ((j/6) MOD 7)];
blu: NAT ← Linearize[51.0 * (j MOD 6)];
vt.SetColor[i, 0, red, grn, blu];
ENDLOOP;
vt.SetColor[254, 0, 255, 255, 255]; vt.SetColor[255, 0, 255, 255, 255];
};
LoadColorRamp: PUBLIC PROC [vt: Terminal.Virtual, clr1: RGB ← [0,0,0],
          clr2: RGB ← [255,255,255], exponent: RGB ← [.43,.43,.43] ] ~ {
state: Terminal.ColorMode;
maxVal: REAL;
IF vt = NIL THEN vt ← Terminal.Current[];
state ← vt.GetColorMode[];
maxVal ← IF state.full
THEN 255.0
ELSE Real.Float[Basics.BITSHIFT[1, state.bitsPerPixelChannelA] - 1];
clr1.R ← MAX[0.0, MIN[1.0, clr1.R]]; clr2.R ← MAX[0.0, MIN[1.0, clr2.R]];
clr1.G ← MAX[0.0, MIN[1.0, clr1.G]]; clr2.G ← MAX[0.0, MIN[1.0, clr2.G]];
clr1.B ← MAX[0.0, MIN[1.0, clr1.B]]; clr2.B ← MAX[0.0, MIN[1.0, clr2.B]];
FOR i: NAT IN [ 0 .. INTEGER[Real.Fix[maxVal]] ] DO   -- linear ramp exponentiated
jr: [0..256) ← Real.Fix[
    RealFns.Power[clr1.R + i/maxVal * (clr2.R - clr1.R), exponent.R] * maxVal];
jg: [0..256) ← Real.Fix[
    RealFns.Power[clr1.G + i/maxVal * (clr2.G - clr1.G), exponent.G] * maxVal];
jb: [0..256) ← Real.Fix[
    RealFns.Power[clr1.B + i/maxVal * (clr2.B - clr1.B), exponent.B] * maxVal];
IF Terminal.GetColorMode[vt].full
THEN { vt.SetRedMap[i, jr]; vt.SetGreenMap[i, jg]; vt.SetBlueMap[i, jb]; }
ELSE vt.SetColor[i, 0, jr, jg, jb];
ENDLOOP;
};
Low-level drawing
FillInBackGround: ContextProc ~ {
IF context.viewer # NIL            -- do through viewer
THEN context.class.drawInViewer[context, NEW[ImagerProcRec ← [ViewerBackFill, NIL]]]
ELSE G3dRenderWithPixels.FillInBackGround[context];   -- clear directly
};
ViewerBackFill: ImagerProc ~ {  -- get pixelmap into context and then do it
DoFillInBackGround: PROC[pixelMap: PixelMap] ~ {
tempPixels: PixelMap ← context.pixels; tempViewPort: Rectangle ← context.viewPort^;
context.pixels ← pixelMap;
context.viewPort.x ← context.viewPort.x + pixelMap.box.min.f;  -- adjust to device coords
context.viewPort.y ← context.viewPort.y + pixelMap.box.min.s;
G3dRenderWithPixels.FillInBackGround[context];
context.pixels ← tempPixels; context.viewPort^ ← tempViewPort;
};
ImagerBackdoor.AccessBufferRectangle[imagerCtx, DoFillInBackGround, context.viewPort^];
};
Draw2DLine: PROC[context: REF Context, p1, p2: Pair, color: Pixel] ~ {
mapClr: Pixel ← MappedRGB[context, color];
IF context.viewer # NIL               -- do through viewer
THEN {
data: REF LineDesc ← NEW[LineDesc ← [p1, p2, mapClr] ];
context.class.drawInViewer[context, NEW[ImagerProcRec ← [ViewerLine, data]]];
}
ELSE G3dRenderWithPixels.Draw2DLine[context, p1, p2, mapClr];  -- do directly
};
LineDesc: TYPE ~ RECORD[p1, p2: Pair, color: Pixel];
ViewerLine: ImagerProc ~ {  -- get pixelmap into context and then do it
DoDrawLine: PROC[pixelMap: PixelMap] ~ {
desc: REF LineDesc ← NARROW[data];
tempPixels: PixelMap ← context.pixels; context.pixels ← pixelMap;
G3dRenderWithPixels.Draw2DLine[context, desc.p1, desc.p2, desc.color];
context.pixels ← tempPixels;
};
ImagerBackdoor.AccessBufferRectangle[imagerCtx, DoDrawLine, context.viewPort^];
};
Draw2DPoly: PROC[context: REF Context, poly: REF PairSequence, color: Pixel] ~ {
mapClr: Pixel ← MappedRGB[context, color];
IF context.viewer # NIL            -- do through viewer
THEN {
data: REF PolyDesc ← NEW[PolyDesc ← [poly, mapClr] ];
context.class.drawInViewer[context, NEW[ImagerProcRec ← [Viewer2DPoly, data]]];
}
ELSE G3dRenderWithPixels.Draw2DPoly[context, poly, mapClr];   -- do directly
};
PolyDesc: TYPE ~ RECORD[poly: REF PairSequence, color: Pixel];
Viewer2DPoly: ImagerProc ~ {   -- get pixelmap into context and then do it
DoDraw2DPoly: PROC[pixelMap: PixelMap] ~ {
desc: REF PolyDesc ← NARROW[data];
tempPixels: PixelMap ← context.pixels; context.pixels ← pixelMap;
G3dRenderWithPixels.Draw2DPoly[context, desc.poly, desc.color];
context.pixels ← tempPixels;
};
ImagerBackdoor.AccessBufferRectangle[imagerCtx, DoDraw2DPoly, context.viewPort^];
};
PolygonTiler: PatchProc ~ {
IF context.viewer # NIL            -- do through viewer
THEN {
plyData: REF PolygonDesc ← NEW[PolygonDesc ← [patch, data] ];
context.class.drawInViewer[context, NEW[ImagerProcRec ← [ViewerTiler, plyData]]];
}
ELSE {
[] ← G3dRenderWithPixels.PolygonTiler[context, patch, data];   -- do directly
};
RETURN[NIL];
};
PolygonDesc: TYPE ~ RECORD[patch: REF Patch, data: REF ANY];
ViewerTiler: ImagerProc ~ {    -- get pixelmap into context and then do it
DoPolygonTiler: PROC[pixelMap: PixelMap] ~ {
desc: REF PolygonDesc ← NARROW[data];
tempPixels: PixelMap ← context.pixels;
context.pixels ← pixelMap;
[] ← G3dRenderWithPixels.PolygonTiler[context, desc.patch, desc.data];
context.pixels ← tempPixels;
};
ImagerBackdoor.AccessBufferRectangle[imagerCtx, DoPolygonTiler, context.viewPort^];
};
Draw2DRope: PUBLIC RopeProc ~ {
Put a string of characters on the screen
IF context.viewer = NIL
THEN G3dRenderWithPixels.DrawRope[context, rope, position, color, size, font]
ELSE {
ropeData: REF RopeDesc ← NEW[ RopeDesc ← [rope, position, color, size, font] ];
context.class.drawInViewer[
context,
NEW[ImagerProcRec ← [G3dRenderWithPixels.DoRope, ropeData]]
];
};
};
Frame Generation and Animation
MakeFrame: PUBLIC ContextProc ~ {
refreshProc: CedarProcess.Process ← NIL;
tmpViewer: ViewerClasses.Viewer;
tmpContext: REF Context;
The following blows up if the viewPort is NIL (As it often can be) !!!
G3dSurfaceRender.ValidateContext[context]; -- ensures viewer, viewPort and pixel store updated
IF context.viewer # NIL
THEN tmpContext ← SceneUtilities.GetTmpContext[context] -- get modifiable context
ELSE tmpContext ← context;    -- drawing into memory or directly on display
{ ENABLE UNWIND => CedarProcess.Abort[refreshProc]; -- in case of aborts, etc.
IF NOT tmpContext.doVisibly        -- buffering, copy over when done
THEN {tmpViewer ← tmpContext.viewer; tmpContext.viewer ← NIL}
ELSE IF tmpContext.viewer # NIL AND tmpContext.pixels # NIL
AND
tmpContext.pixels.samplesPerPixel > 1 THEN {
Use forked process to copy bits over periodically, too complex to do onscreen
refreshProc ← CedarProcess.Fork[UpdateViewer, context]; -- start refresh
tmpViewer ← tmpContext.viewer; tmpContext.viewer ← NIL;
};
IF Atom.GetPropFromList[ context.displayProps, $ScreenPixels ] # NIL -- full color, no vwr
THEN refreshProc ← CedarProcess.Fork[PeriodicUpdateFullColorDisplay, context];
G3dRenderWithPixels.MakeFrame[tmpContext];
};
IF refreshProc # NIL THEN CedarProcess.Abort[refreshProc];  -- kill periodic update
IF NOT tmpContext.doVisibly OR tmpViewer # NIL THEN {
tmpContext.viewer ← tmpViewer;
context.class.drawInViewer[ tmpContext, NEW[ImagerProcRec ← [StuffBuf, NIL]] ];
};
IF refreshProc # NIL THEN {
[] ← CedarProcess.Join[refreshProc]; -- wait for refreshProc done
If PeriodicUpdateFullColorDisplay, in its final pass, missed some pixels being rendered:
UpdateFullColorDisplay[context];
};
};
StuffBuf: PUBLIC ImagerProc ~ {
DoTransfer: PROC[pixelMap: PixelMap] ~ {
FOR i: NAT IN [0..samplesPerColor) DO
ImagerSample.Transfer[dst: pixelMap[i], src: context.pixels[i], delta: pixelMap.box.min];
ENDLOOP;
};
samplesPerColor: NATIF context.class.displayType = $FullColor THEN 3 ELSE 1;
ImagerBackdoor.AccessBufferRectangle[imagerCtx, DoTransfer, context.viewPort^];
};
MakeHiResFrame: PUBLIC PROC[ context: REF Context, width, height: NAT, name: ROPE,
          keepLog: BOOLEANTRUE ] ~ {
hiResCtxt: REF Context ← SceneUtilities.GetTmpContext[context];
hiResCtxt.viewer ← NIL;
hiResCtxt.viewPort ← NEW[      -- set viewport directly to define pixelMap size
Imager.Rectangle ← [ x: 0.0, y: 0.0, w: Real.Float[width], h: Real.Float[height] ]
];
G3dRenderWithPixels.AllocatePixelMemory[hiResCtxt];     -- get display memory
SceneUtilities.SetViewPort[ hiResCtxt, [0.0, 0.0, Real.Float[width], Real.Float[height]] ];
hiResCtxt.class.render ← G3dRenderWithPixels.MakeFrame;  -- load direct memory procs
hiResCtxt.class.displayPolygon ← G3dRenderWithPixels.PolygonTiler;
IF context.antiAliasing THEN G3dRenderWithPixels.AntiAliasing[hiResCtxt]; -- get state straight
IF context.depthBuffering THEN G3dRenderWithPixels.DepthBuffering[hiResCtxt];
IF keepLog THEN [] ← SceneUtilities.StartLog[hiResCtxt];
FOR i: NAT IN [0..hiResCtxt.shapes.length) DO
hiResCtxt.shapes[i].vtcesInValid ← TRUE;
ENDLOOP;
hiResCtxt.class.render[hiResCtxt];
AISAnimation.StoreFiles[hiResCtxt, name ];       -- store resulting image
};
DitherImage: PUBLIC PROC[dstContext, rgbContext: REF Context] ~ {
Action: PROC ~ {
width: NAT ← Real.Fix[MIN[dstContext.viewPort.w, rgbContext.viewPort.w] ];
height: NAT ← Real.Fix[MIN[dstContext.viewPort.h, rgbContext.viewPort.h] ];
scanSegIn: PixelBuffer ← ImagerPixel.NewPixels[rgbContext.pixels.samplesPerPixel, width];
scanSegOut: PixelBuffer ← ImagerPixel.NewPixels[dstContext.pixels.samplesPerPixel, width];
IF rgbContext.pixels.samplesPerPixel < 3
THEN SIGNAL G3dRender.Error[[$MisMatch, "24-bit input needed for dithering"]];
FOR y: NAT IN [0..height) DO
ImagerPixel.GetPixels[          -- get rgb pixels
self: rgbContext.pixels, pixels: scanSegIn, initIndex: [f: 0, s: y], count: width ];
ImagerPixel.GetPixels[          -- get rgb pixels
self: dstContext.pixels, pixels: scanSegOut, initIndex: [f: 0, s: y], count: width ];
FOR x: NAT IN [0..width) DO
scanSegOut[0][x] ← DitheredRGB[$PseudoColor, x, y,
          scanSegIn[0][x], scanSegIn[1][x], scanSegIn[2][x] ];
ENDLOOP;
ImagerPixel.PutPixels[          -- store result in foreground
self: dstContext.pixels, pixels: scanSegOut, initIndex: [f: 0, s: y], count: width ];
ENDLOOP;
};
CedarProcess.DoWithPriority[background, Action];    -- be nice to other processess
};
ditherTable: ARRAY [0..4) OF ARRAY [0..4) OF NAT
             [[0,12,3,15], [8,4,11,7], [2,14,1,13], [10,6,9,5]];
DitheredRGB: PROC[renderMode: ATOM, x, y, red, grn, blu: INTEGER] RETURNS[INTEGER] ~ {
val2R, val2G, val2B, pixelValue: NAT;
SELECT renderMode FROM
$PseudoColor => {
Ordered dither for crude looks at full-color
threshold: NAT ← ditherTable[ Basics.BITAND[x,3] ][ Basics.BITAND[y,3] ];
valR: NAT ← Basics.BITSHIFT[ Basics.BITSHIFT[red,2] + red, -4 ];  -- (red * 5) / 16
valG: NAT ← Basics.BITSHIFT[ Basics.BITSHIFT[grn,2] + Basics.BITSHIFT[grn,1], -4 ];
valB: NAT ← Basics.BITSHIFT[ Basics.BITSHIFT[blu,2] + blu, -4 ];  -- (blu * 5) / 16
val2R ← Basics.BITSHIFT[valR,-4];           -- valR / 16
IF Basics.BITAND[valR,15] > threshold THEN val2R ← val2R + 1;  -- valr MOD 16
val2G ← Basics.BITSHIFT[valG,-4];
IF Basics.BITAND[valG,15] > threshold THEN val2G ← val2G + 1;
val2B ← Basics.BITSHIFT[valB,-4];
IF Basics.BITAND[valB,15] > threshold THEN val2B ← val2B + 1;
RETURN[ MIN[ 255,
Basics.BITSHIFT[val2R,5] + Basics.BITSHIFT[val2R,3] + Basics.BITSHIFT[val2R,1]
+ Basics.BITSHIFT[val2G,2] + Basics.BITSHIFT[val2G,1]
+ val2B + 2 ] ];     --val2R*42 + val2G*6 + val2B + 2
};
$ImagerDithered => {
Ordered dither for crude looks at full-color using Imager's color map
threshold: NAT ← ditherTable[x MOD 4][y MOD 4];
valR: NAT ← 4* red / 16;
valG: NAT ← 5* grn / 16;
valB: NAT ← 3* blu / 16;
val2R ← valR/16; IF valR MOD 16 > threshold THEN val2R ← val2R + 1;
val2G ← valG/16; IF valG MOD 16 > threshold THEN val2G ← val2G + 1;
val2B ← valB/16; IF valB MOD 16 > threshold THEN val2B ← val2B + 1;
pixelValue ← val2R*24 + val2G*4 + val2B;
IF pixelValue >= 60 THEN pixelValue ← pixelValue + 135;  -- move to top of map
RETURN[ MIN[255, pixelValue] ];
};
ENDCASE => SIGNAL G3dRender.Error[[$MisMatch, "Unexpected display type"]];
RETURN[ 255 ];
};
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];
};
NewFrame: ENTRY Animation3D.ViewProc ~ {
PROC[context: Context, lookingFrom, lookingAt: Triple, frameNo: NAT]
ENABLE UNWIND => NULL;
filename: ROPENARROW[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: REF Context ← NARROW[
Atom.GetPropFromList[context.props, $DitherContext]
];
DitherImage[ditherContext, context];
AISAnimation.StoreFiles[ditherContext, filename, frameNo];
}
ELSE AISAnimation.StoreFiles[context, filename, frameNo];
};
SetUpAnimation: PROC[ context: REF Context, filename: ROPENIL ] ~ {
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: REF Context ← G3dRender.Create[];
ditherContext.class ← NEW[
ContextClass ← G3dRender.GetDisplayType[$PseudoColor]
];
G3dSurfaceRender.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[Imager.Rectangle ← [ 0.0, 0.0, 720.0, 486.0 ]];
G3dRenderWithPixels.AllocatePixelMemory[context]; -- get buffer memory of right size
SceneUtilities.SetViewPort[ context, [0.0, 0.0, 720.0, 486.0] ];
SceneUtilities.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: REF Context ] ~ {
context.props ← Atom.RemPropFromList[context.props, $OutputFile];
context.props ← Atom.RemPropFromList[context.props, $DitherContext];
};
Orbit: PUBLIC PROC[ context: REF Context, lookingFrom, lookingAt, axis, base: Triple,
      moveEPNotCI: BOOLEANTRUE,
      framesPerRev: NAT ← 16, startAt: NAT ← 0, endAt: NAT ← 32767,
      filename: ROPENIL ] ~ {
ENABLE UNWIND => NULL;
SetUpAnimation[context, filename];
IF moveEPNotCI
THEN Animation3D.MoveEyePointInOrbit[
context, lookingFrom, lookingAt, axis, base,
NewFrame, framesPerRev, startAt, endAt
]
ELSE Animation3D.MoveCtrOfInterestInOrbit[
context, lookingFrom, lookingAt, axis, base,
NewFrame, framesPerRev, startAt, endAt
];
FinishAnimation[context];
};
MakeFramesFromTo: PUBLIC PROC[context: REF Context,
        lookingFrom, lookingAt, toLookingFrom, toLookingAt: Triple,
        framesOnLine: NAT, startAt, endAt: NAT ← 0,
        filename: ROPENIL] ~ {
ENABLE UNWIND => NULL;
SetUpAnimation[context, filename];
IF endAt = 0 THEN endAt ← framesOnLine;   -- default is full number of frames
Animation3D.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: REF Context,
           lookingFrom, lookingAt: LIST OF Triple,
           framesOnPath: NAT, startAt, endAt: NAT ← 0,
           filename: ROPENIL, closed: BOOLEANTRUE ] ~ {
Animate along curved path of typed in points
ENABLE UNWIND => NULL;
ExpandSequence: PROC[seq: REF TripleSequence] ~ {
newSeq: REF TripleSequence ← NEW[TripleSequence[seq.length + 64]];
FOR i: NAT IN [0..seq.length) DO newSeq[i] ← seq[i]; ENDLOOP;
seq ← newSeq;
};
lookingFroms: REF TripleSequence ← NEW[TripleSequence[16]];
lookingAts: REF TripleSequence ← NEW[TripleSequence[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 Animation3D.MoveOnClosedCurve[ context, lookingFroms, lookingAts,
            NewFrame, framesOnPath, startAt, endAt ]
ELSE Animation3D.MoveOnOpenCurve[ context, lookingFroms, lookingAts,
            NewFrame, framesOnPath, startAt, endAt ];
FinishAnimation[context];
};
Init[];
END.