ThreeDMiscImpl.mesa
Last Edited by: Crow, May 24, 1986 1:40:30 pm PDT
DIRECTORY
Basics     USING [BITSHIFT, BITOR, BITAND],
BasicTime   USING [PulsesToSeconds, GetClockPulses],
Atom     USING [PutPropOnList, GetPropFromList, RemPropFromList],
CedarProcess   USING [CheckAbort, DoWithPriority],
ViewerIO    USING [CreateViewerStreams],
Terminal    USING [Virtual, GetColorMode, SetColor, GetColor,
        WaitForBWVerticalRetrace, ColorMode,
        SetRedMap, SetGreenMap, SetBlueMap],
IO      USING [STREAM, PutRope, Flush, Close, GetLineRope],
FS      USING [StreamOpen],
Rope     USING [ROPE, Index, Cat],
Convert    USING [RopeFromCard],
Real     USING [Float, FixC, RoundC, FixI],
RealFns    USING [Sin, Cos, Power],
Imager    USING [Context, Rectangle, SetColor, Font,
        SetFont, SetXY, ShowRope],
Vector2    USING [ VEC ],
ImagerBackdoor  USING [ GetBounds ],
ImagerColor   USING [ ColorFromRGB, RGBFromHSL, RGB ],
ImagerFont   USING [ Find, Scale ],
NamedColors   USING [ RopeToHSL ],
Pixels     USING [ PixelBuffer, Extent, BYTE, ValueOp, GetValue, PutValue,
         Transfer, GetSampleSet, SampleSet, GetPixel, PutPixel,
         GetScanSeg, PutScanSeg, SampleSetSequence ],
Vector3d    USING [ Pair, Triple, Quad ],
Matrix3d    USING [ Matrix ],
Plane3d    USING [ DistanceToPt ],
ScanConvert   USING [ IntRGB, GetColorProc, MappedRGB,
         DitheredRGB, PutLine ],
ThreeDScenes  USING [ AddShape, ClipState, Context, Create, Error, FillInBackGround,
         FillViewPort, FindShape, GetShading, InitShades,
         NewShape, NoneOut, OutCode, PlaceShape, PutShading,
         ReadColors, ReadNormals, ReadScene, WriteScene,
         ReadTextureCoords, SetUpStandardFile, SetView, SetViewPort,
         ShadingProcs, ShadingSequence, ShadingValue, ShapeInstance,
         ShapeSequence, Vertex, VertexSequence, VtxToRealSeqProc,
         XfmPtToEyeSpace, XfmPtToDisplay ],
ThreeDSurfaces  USING [ Patch, PtrPatch, ShowObjects, ShowWireFrameObjects,
         PtrPatchSequence, LoadShape, GetPolyNormals, GetPatchColors,
         GetVtxNormals, PatchProcs, RegisterSurfaceType ],
Tilers     USING [ FancyTiler],
TextureMaps   USING [ TextureMap ],
SolidTextures   USING [ RopeToProc, GetLerpedVals ],
AISAnimation  USING [ StoreFiles, PutAIS ],
Animation3D   USING [ MoveInOrbit, MoveOnLine ],
StandardPatches  USING [ BezierExpand, BezierSubdivide, BezierDisplay,
         BezierDisplayLines ],
ThreeDMisc   USING [ ];
ThreeDMiscImpl: CEDAR PROGRAM
IMPORTS Basics, Imager, Pixels, ThreeDScenes, ThreeDSurfaces, Tilers, Real, RealFns, IO,
   ImagerFont, AISAnimation, Animation3D, CedarProcess, Atom,
   Terminal, ImagerColor, NamedColors, ImagerBackdoor, ScanConvert,
   Rope, ViewerIO, Convert, BasicTime, Plane3d, SolidTextures, StandardPatches, FS
EXPORTS ThreeDMisc
~ BEGIN
Types
Context: TYPE ~ ThreeDScenes.Context;
Pair: TYPE ~ Vector3d.Pair;           -- RECORD [ x, y: REAL];
Triple: TYPE ~ Vector3d.Triple;
RGB: TYPE ~ ImagerColor.RGB;
IntRGB: TYPE ~ ScanConvert.IntRGB;
IntRGBSequence: TYPE ~RECORD [ SEQUENCE length: NAT OF IntRGB ];
Rectangle: TYPE ~ Pixels.Extent;
NatSequence: TYPE ~ RECORD [ SEQUENCE length: NAT OF NAT ];
Utility Procedures
clrValues: Pixels.SampleSet ← Pixels.GetSampleSet[4];
RGBtoPixelValue: PUBLIC PROC[context: REF ThreeDScenes.Context, clr: RGB,
          values: Pixels.SampleSet]
      RETURNS[ Pixels.SampleSet ] ~ {
base: NATIF context.alphaBuffer THEN 1 ELSE 0;
values[0] ← 0;        
SELECT context.renderMode FROM
$FullClr, $Dorado24  => {
values[base ] ← Real.RoundC[clr.R*255.0];
values[base+1] ← Real.RoundC[clr.G*255.0];
values[base+2] ← Real.RoundC[clr.B*255.0];
};
$Grey   => {
values[base] ← Real.RoundC[(clr.R + clr.G + clr.B) / 3.0 * 255.0];
};
$Dithered, $PseudoClr => {
values[base] ← ScanConvert.MappedRGB[
context.renderMode,
[ Real.RoundC[clr.R*255.0], Real.RoundC[clr.G*255.0], Real.RoundC[clr.B*255.0] ]
];
};
ENDCASE  => SIGNAL ThreeDScenes.Error[[$MisMatch, "Improper renderMode"]];
RETURN[values];
};
PutPixel: PUBLIC PROC[context: REF ThreeDScenes.Context, x, y: NAT, clr: RGB] ~ {
clrValues ← RGBtoPixelValue[context, clr, clrValues];
Pixels.PutPixel[context.display, x, y, clrValues];
};
ClipLineWithPlane: PUBLIC PROC[plane: Vector3d.Quad, p1, p2: Triple]
       RETURNS[ insidePt, clippedPt: Triple, state: ThreeDScenes.ClipState ] ~ {
alpha: REAL;
dist1: REAL ← Plane3d.DistanceToPt[ p1, plane ];
dist2: REAL ← Plane3d.DistanceToPt[ p2, plane ];
IF dist1 > 0.0 AND dist2 > 0.0
THEN RETURN[p1, p2, in]     -- all inside, no clipping
ELSE IF dist1 < 0.0 AND dist2 < 0.0
THEN RETURN[p1, p2, out]   -- all outside, no clipping
ELSE {
pOut: Triple;
alpha ← dist1 / (dist1 - dist2);    -- clip it
pOut.x ← p1.x * (1.0 - alpha) + p1.x * alpha;
pOut.y ← p1.y * (1.0 - alpha) + p1.y * alpha;
pOut.z ← p1.z * (1.0 - alpha) + p1.z * alpha;
IF dist1 > 0.0
THEN RETURN [p1, pOut, clipped]
ELSE RETURN [p2, pOut, clipped];
};
};
DrawRGBLine: PUBLIC PROC[context: REF Context, inP1, inP2: Triple, clr: RGB] ~ {
Draw 3D lines, clipped
p1, p2: Triple;
code1, code2, orOfCodes : ThreeDScenes.OutCode;
[p1, code1] ← ThreeDScenes.XfmPtToEyeSpace[context, inP1];  -- xfm and get clip code
[p2, code2] ← ThreeDScenes.XfmPtToEyeSpace[context, inP2];
orOfCodes ← LOOPHOLE[
Basics.BITOR[ LOOPHOLE[code1], LOOPHOLE[code2] ],
ThreeDScenes.OutCode
];
IF orOfCodes # ThreeDScenes.NoneOut        -- not all inside?
THEN {
andOfCodes: ThreeDScenes.OutCode ← LOOPHOLE[
Basics.BITAND[LOOPHOLE[code1], LOOPHOLE[code2] ],
ThreeDScenes.OutCode
];
IF andOfCodes # ThreeDScenes.NoneOut     -- all outside?
THEN RETURN[]
ELSE {          -- clipping needed, do it
clipState: ThreeDScenes.ClipState;
IF orOfCodes.near THEN
[p1, p2, clipState] ← ClipLineWithPlane[ context.clippingPlanes[Near], p1, p2];
IF orOfCodes.far THEN
[p1, p2, clipState] ← ClipLineWithPlane[ context.clippingPlanes[Far], p1, p2];
IF orOfCodes.left THEN
[p1, p2, clipState] ← ClipLineWithPlane[ context.clippingPlanes[Left], p1, p2];
IF orOfCodes.right THEN
[p1, p2, clipState] ← ClipLineWithPlane[ context.clippingPlanes[Right], p1, p2];
IF orOfCodes.bottom THEN
[p1, p2, clipState] ← ClipLineWithPlane[ context.clippingPlanes[Bottom], p1, p2];
IF orOfCodes.top THEN
[p1, p2, clipState] ← ClipLineWithPlane[ context.clippingPlanes[Top], p1, p2];
};
};
p1 ← ThreeDScenes.XfmPtToDisplay[context, p1];  -- xfm to screen
p2 ← ThreeDScenes.XfmPtToDisplay[context, p2];
clrValues ← RGBtoPixelValue[context, clr, clrValues];
ScanConvert.PutLine[
context.display,
[Real.FixC[p1.x], Real.FixC[p1.y] ],
[Real.FixC[p2.x], Real.FixC[p2.y] ],
clrValues
];
};
PrependWorkingDirectory: PUBLIC PROC[context: REF ThreeDScenes.Context, file: Rope.ROPE]          RETURNS[Rope.ROPE] ~ {
wDir: Rope.ROPENARROW[ Atom.GetPropFromList[context.props, $WDir] ];
IF wDir = NIL
THEN RETURN[file]
ELSE IF file = NIL OR (Rope.Index[s1: file, s2: "/"] > 0 AND Rope.Index[s1: file, s2: "["] > 0)  
THEN file ← Rope.Cat[ wDir, file ];  -- if first char not / or [ then prepend wDir
RETURN[ file ];
};
ElapsedTime: PROC[startTime: REAL] RETURNS[Rope.ROPE] ~ {
RETURN[ Rope.Cat[
Convert.RopeFromCard[
Real.FixC[BasicTime.PulsesToSeconds[BasicTime.GetClockPulses[]] - startTime]
],
" secs. "
] ];
};
CurrentTime: PROC[] RETURNS[REAL] ~ {
RETURN[ BasicTime.PulsesToSeconds[BasicTime.GetClockPulses[]] ];
};
GetImagerContext: PUBLIC PROC[context: REF ThreeDScenes.Context] RETURNS[Imager.Context] ~ {
imagerCtx: Imager.Context ← NIL;
ref: REF ANY ← Atom.GetPropFromList[context.display.props, $ImagerContext];
IF ref # NIL
THEN imagerCtx ← NARROW[ref, Imager.Context]
ELSE SIGNAL ThreeDScenes.Error[[$Condition, "No Imager context"]];
RETURN[imagerCtx];
};
SetViewPortFromImager: PUBLIC PROC[context: REF ThreeDScenes.Context,
            imagerCtx: Imager.Context] ~ {
bounds: Imager.Rectangle ← ImagerBackdoor.GetBounds[imagerCtx];
ThreeDScenes.SetViewPort[ context, bounds ];   -- set clippers etc.
};
CopyContextData: PUBLIC PROC [dstCtx, srcCtx: REF ThreeDScenes.Context] ~ {
dstCtx.shapes ← srcCtx.shapes;
dstCtx.lights ← srcCtx.lights;
dstCtx.environment ← srcCtx.environment;
dstCtx.eyePoint ← srcCtx.eyePoint;
dstCtx.ptOfInterest ← srcCtx.ptOfInterest;
dstCtx.rollAngle ← srcCtx.rollAngle;
dstCtx.upDirection ← srcCtx.upDirection;
dstCtx.fieldOfView ← srcCtx.fieldOfView;
dstCtx.window ← srcCtx.window;
dstCtx.hitherLimit ← srcCtx.hitherLimit;
dstCtx.yonLimit ← srcCtx.yonLimit;
dstCtx.clippingPlanes ← srcCtx.clippingPlanes;
dstCtx.eyeSpaceXfm ← srcCtx.eyeSpaceXfm;
dstCtx.eyeToNDC ← srcCtx.eyeToNDC;
Don't copy display, assumed to be using that for double buffering or something else
dstCtx.viewPort ← srcCtx.viewPort;
dstCtx.renderMode ← srcCtx.renderMode;
dstCtx.lineDrawing ← srcCtx.lineDrawing;
dstCtx.depthResolution ← srcCtx.depthResolution;
dstCtx.sortSequence ← srcCtx.sortSequence;
dstCtx.props ← srcCtx.props;
};
RegisterSurfaceType: PUBLIC PROC[context: REF Context, type: ATOM] ~ {
procRecord: REF ThreeDSurfaces.PatchProcs ← NEW[ThreeDSurfaces.PatchProcs];
SELECT type FROM
$ConvexPolygon => RETURN[];
$Bezier => {
procRecord.expand ← StandardPatches.BezierExpand;
procRecord.subdivide ← StandardPatches.BezierSubdivide;
procRecord.display ← StandardPatches.BezierDisplay;
procRecord.displayLines ← StandardPatches.BezierDisplayLines;
ThreeDSurfaces.RegisterSurfaceType[ context, $Bezier, procRecord];
};
ENDCASE => SIGNAL ThreeDScenes.Error[[$UnImplemented, "Unknown surface type"]];
};
StartLog: PUBLIC PROC [context: REF Context] RETURNS[IO.STREAM] ~ {
log: IO.STREAM;
[out: log] ← ViewerIO.CreateViewerStreams[
name: "ThreeDWorld.log",
backingFile: PrependWorkingDirectory[context, "ThreeDWorld.log"]
];
context.props ← Atom.PutPropOnList[context.props, $Log, log];
RETURN[ log ];
};
FlushLog: PUBLIC PROC [context: REF Context] ~ {
log: IO.STREAMNARROW[Atom.GetPropFromList[context.props, $Log]];
IF log # NIL THEN IO.Flush[log];
};
CloseLog: PUBLIC PROC [context: REF Context] ~ {
log: IO.STREAMNARROW[Atom.GetPropFromList[context.props, $Log]];
IF log # NIL THEN IO.Close[log];
};
Sgn: PROC [number: REAL] RETURNS [REAL] ~ INLINE {
IF number < 0. THEN RETURN[-1.] ELSE RETURN[1.];
};
Colors and Text
GetMappedColor: PUBLIC PROC[context: REF ThreeDScenes.Context, clr: IntRGB]
      RETURNS[Pixels.BYTE] ~ {
value: NAT ← ScanConvert.MappedRGB[context.renderMode, clr];
RETURN[value];
};
SetNamedColor: PUBLIC PROC [context: REF ThreeDScenes.Context, color: Rope.ROPE] ~ {
clr: RGB ← ImagerColor.RGBFromHSL[ NamedColors.RopeToHSL[color] ];
Imager.SetColor[ GetImagerContext[context], ImagerColor.ColorFromRGB[clr] ];
};
SetRGBColor: PUBLIC PROC [context: REF ThreeDScenes.Context, clr: RGB] ~ {
Imager.SetColor[ GetImagerContext[context], ImagerColor.ColorFromRGB[clr] ];
};
LoadStd8BitClrMap: PUBLIC PROC [vt: Terminal.Virtual] ~ {
Linearize: PROC [value: REAL] RETURNS[NAT] ~ {
RETURN[ Real.RoundC[RealFns.Power[value / 255.0, .43] * 255.0] ];
};
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];
};
Rotate8BitClrMap: PUBLIC PROC [vt: Terminal.Virtual, firstValue, lastValue, duration: NAT ← 0] ~ { 
Rotates color map entries in given range
r, g, b: ARRAY [0..256) OF [0..256);
times: NAT;
IF lastValue = 0 THEN lastValue ← 255;
FOR i: NAT IN [firstValue .. lastValue] DO
[r[i], g[i], b[i]] ← Terminal.GetColor[ vt, i];
ENDLOOP;
IF duration = 0
THEN times ← 0          -- will be one complete cycle
ELSE times ← duration * 75;      -- seconds times fields per second
times ← times +            -- Fill out full cycle
  (lastValue - firstValue + 1) - times MOD (lastValue - firstValue + 1);
FOR i: NAT IN [0..times) DO
tr, tg, tb: [0..256);
CedarProcess.CheckAbort[];   -- check for external abort request before proceeding
vt.WaitForBWVerticalRetrace[];    -- await top of scan (to control update rate)
tr ← r[firstValue]; tg ← g[firstValue]; tb ← b[firstValue];
FOR j: NAT IN (firstValue .. lastValue] DO
r[j-1] ← r[j]; g[j-1] ← g[j]; b[j-1] ← b[j];
ENDLOOP;
r[lastValue] ← tr; g[lastValue] ← tg; b[lastValue] ← tb;
FOR i: NAT IN [firstValue .. lastValue] DO
Terminal.SetColor[ vt, i, 0, r[i], g[i], b[i]];
ENDLOOP;
ENDLOOP;
};
Show8BitClrMap: PUBLIC PROC [context: REF ThreeDScenes.Context] ~ {
firstBottom: INTEGER ← Real.FixC[.75 * context.viewPort.h];
size: INTEGER ← Real.FixC[ .025 * MIN[context.viewPort.h, context.viewPort.w] ];
firstTop: INTEGER ← firstBottom + size;
firstLeft: INTEGER ← Real.FixC[context.viewPort.w/2.0] - 16*size;
firstRight: INTEGER ← firstLeft + size;
FOR i: NAT IN [0..256) DO
top: NAT ← firstTop + size * (i / 32);
bottom: NAT ← firstBottom + size * (i / 32);
left: NAT ← firstLeft + size * (i MOD 32);
right: NAT ← firstRight + size * (i MOD 32);
Pixels.ValueOp[context.display, [left, bottom, right-left, top-bottom], i];
ENDLOOP;
};
LoadColorRamp: PUBLIC PROC [vt: Terminal.Virtual, clr1, clr2, exponent: RGB] ~ {
state: Terminal.ColorMode ← vt.GetColorMode[];
maxVal: REALIF 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 .. Real.FixC[maxVal] ] DO      -- linear ramp exponentiated
jr: [0..256) ← Real.FixC[
    RealFns.Power[clr1.R + i/maxVal * (clr2.R - clr1.R), exponent.R] * maxVal];
jg: [0..256) ← Real.FixC[
    RealFns.Power[clr1.G + i/maxVal * (clr2.G - clr1.G), exponent.G] * maxVal];
jb: [0..256) ← Real.FixC[
    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;
};
LoadMultiRamps: PUBLIC PROC [vt: Terminal.Virtual, colors: LIST OF RGB] ~ {
clr: REF IntRGBSequence ← NEW[ IntRGBSequence[32] ];
numClrs, thisClr, nextClr, rampLength: NAT ← 0;
state: Terminal.ColorMode ← vt.GetColorMode[];
maxVal: REALIF state.full
THEN 255.0
ELSE Real.Float[Basics.BITSHIFT[1, state.bitsPerPixelChannelA] - 1];
WHILE colors # NIL DO
clr[numClrs].r ← Real.FixC[maxVal * MAX[0.0, MIN[1.0, colors.first.R]] ];
clr[numClrs].g ← Real.FixC[maxVal * MAX[0.0, MIN[1.0, colors.first.G]] ];
clr[numClrs].b ← Real.FixC[maxVal * MAX[0.0, MIN[1.0, colors.first.B]] ];
numClrs ← numClrs + 1;
colors ← colors.rest;
ENDLOOP;
rampLength ← Real.FixC[maxVal] / (numClrs-1);
FOR i: NAT IN [ 0 .. Real.FixC[maxVal] ] DO
jr, jg, jb: [0..256);
position: NAT ← i MOD rampLength;
IF position = 0 THEN IF nextClr < numClrs-1
THEN { thisClr ← nextClr; nextClr ← nextClr + 1; }
ELSE thisClr ← nextClr;
jr ← clr[thisClr].r * (rampLength - position) / rampLength
+ clr[nextClr].r * position / rampLength;
jg ← clr[thisClr].g * (rampLength - position) / rampLength
 + clr[nextClr].g * position / rampLength;
jb ← clr[thisClr].b * (rampLength - position) / rampLength
 + clr[nextClr].b * position / rampLength;
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;
};
AdjustValueRamp: PUBLIC PROC[context: REF ThreeDScenes.Context, exponent: RGB] ~ {
Adjust the values in the display memory for TRC or ? (only for full-color or grey)
Action: PROC[] ~ {
scanSeg: REF Pixels.SampleSetSequence ← NIL;
maxValue: REAL ← 255.0;
expTableR: REF NatSequence ← NEW[ NatSequence[256] ];
expTableG: REF NatSequence ← NEW[ NatSequence[256] ];
expTableB: REF NatSequence ← NEW[ NatSequence[256] ];
start: NATIF context.alphaBuffer THEN 1 ELSE 0;
SELECT context.renderMode FROM
$FullClr, $Dorado24  => FOR i: NAT IN [0..256) DO
expTableR[i] ← Real.RoundC[ RealFns.Power[ i/255.0, exponent.R] * 255.0 ];
expTableG[i] ← Real.RoundC[ RealFns.Power[ i/255.0, exponent.G] * 255.0 ];
expTableB[i] ← Real.RoundC[ RealFns.Power[ i/255.0, exponent.B] * 255.0 ];
ENDLOOP;
$Grey   => {
exp: REAL ← (exponent.R + exponent.G + exponent.B) / 3.0;
FOR i: NAT IN [0..256) DO
expTableG[i] ← Real.RoundC[ RealFns.Power[ i/255.0, exp] * 255.0 ];
ENDLOOP;
};
ENDCASE => SIGNAL ThreeDScenes.Error[[$MisMatch, "Improper renderMode"]];
FOR y: NAT IN [ 0 .. Real.RoundC[context.viewPort.h] ) DO
scanSeg ← Pixels.GetScanSeg[context.display, 0, y, Real.RoundC[context.viewPort.w], scanSeg];
FOR x: NAT IN [ 0 .. Real.RoundC[context.viewPort.w] ) DO
SELECT context.renderMode FROM
$FullClr, $Dorado24  => FOR i: NAT IN [start .. start+3) DO
SELECT i FROM
start => scanSeg[i][x] ← expTableR[scanSeg[i][x]];
start+1 => scanSeg[i][x] ← expTableG[scanSeg[i][x]];
start+2 => scanSeg[i][x] ← expTableB[scanSeg[i][x]];
ENDCASE;
ENDLOOP;
$Grey   => scanSeg[start][x] ← expTableG[scanSeg[start][x]];
ENDCASE => SIGNAL ThreeDScenes.Error[[$MisMatch, "Improper renderMode"]];
ENDLOOP;
Pixels.PutScanSeg[context.display, 0, y, Real.RoundC[context.viewPort.w], scanSeg];
ENDLOOP;
};
CedarProcess.DoWithPriority[background, Action];
};
AdjustSaturation: PUBLIC PROC[context: REF ThreeDScenes.Context, percent: REAL] ~ {
Adjust the values in the display memory for Saturation (only for full-color)
Action: PROC[] ~ {
scanSeg: REF Pixels.SampleSetSequence ← NIL;
maxValue: REAL ← 255.0;
start: NATIF context.alphaBuffer THEN 1 ELSE 0;
FOR y: NAT IN [ 0 .. Real.RoundC[context.viewPort.h] ) DO
scanSeg ← Pixels.GetScanSeg[context.display, 0, y, Real.RoundC[context.viewPort.w], scanSeg];
FOR x: NAT IN [ 0 .. Real.RoundC[context.viewPort.w] ) DO
SELECT context.renderMode FROM
$FullClr, $Dorado24  => {
scale: REAL;
redVal: CARDINAL ← scanSeg[start][x];
grnVal: CARDINAL ← scanSeg[start+1][x];
bluVal: CARDINAL ← scanSeg[start+2][x];
min: CARDINALMIN[redVal, grnVal, bluVal];
max: CARDINALMAX[redVal, grnVal, bluVal];
newMin: CARDINALMIN[max, Real.FixC[percent * min]];
IF (min = 0) OR (max - min = 0) THEN LOOP;
scale ← 1.0 * (max - newMin) / (max - min);
redVal ← Real.FixC[ (redVal - min) * scale + newMin ];
grnVal ← Real.FixC[ (grnVal - min) * scale + newMin ];
bluVal ← Real.FixC[ (bluVal - min) * scale + newMin ];
scanSeg[start ][x] ← redVal;
scanSeg[start+1][x] ← grnVal;
scanSeg[start+2][x] ← bluVal;
};
ENDCASE => SIGNAL ThreeDScenes.Error[[$MisMatch, "Improper renderMode"]];
ENDLOOP;
Pixels.PutScanSeg[context.display, 0, y, Real.RoundC[context.viewPort.w], scanSeg];
ENDLOOP;
};
CedarProcess.DoWithPriority[background, Action];
};
LoadOldStd8BitClrMap: PUBLIC PROC [vt: Terminal.Virtual] ~ {
Linearize: PROC [value: REAL] RETURNS[NAT] ~ {
RETURN[ Real.RoundC[RealFns.Power[value / 255.0, .43] * 255.0] ];
};
FOR i: NAT IN [0..40) DO              -- greyscale
grey: NAT ← Linearize[i*6.5];
vt.SetColor[i + 216, 0, grey, grey, grey];
ENDLOOP;
FOR i: NAT IN [0..216) DO          -- 6 x 6 x 6 color cube
vt.SetColor[i, 0,
Linearize[42.5 * (i/36 + 1)],
Linearize[42.5 * ((i/6) MOD 6 + 1)],
Linearize[42.5 * (i MOD 6 + 1)]
];
ENDLOOP;
vt.SetColor[0, 0, 0, 0, 0];
};
SetUpTerrainColors: PUBLIC PROC[vt: Terminal.Virtual] ~ {   -- for setting up colors for old terrain pictures
terrainHues: ARRAY [0..4) OF RGB ← [[.05, .4, .0], [.5, .6, .1], [.4, .4, .4], [1., 1., 1.]];
{ forest, savannah, rock, snow }
skyHue: RGB ← [.5, .5, 1.0];
waterHue: RGB ← [.2, .2, .8];
start: NAT ← 8;      -- start^ ← 8;
length: NAT ← 240;
valueStepSize, minValue, rampValue, index, hueRampSize: NAT ← 0;
hueRampSize ← 238 / (LENGTH[terrainHues] + 1);
valueStepSize ← 256 / hueRampSize;
minValue ← 256 - valueStepSize*hueRampSize;
FOR j: NAT IN [0..4) DO     -- do hue steps from each hue to next
currentHue: RGB ← [terrainHues[j].R, terrainHues[j].G, terrainHues[j].B];
rampValue ← minValue;
FOR i: NAT IN [0..hueRampSize) DO   -- build hue ramps
r, g, b: NAT;
r ← Real.RoundC[ rampValue * currentHue.R ];
g ← Real.RoundC[ rampValue * currentHue.G ];
b ← Real.RoundC[ rampValue * currentHue.B ];
vt.SetColor[index+start, 0, r, g, b ];
rampValue ← rampValue + valueStepSize;
index ← index + 1;
ENDLOOP;
ENDLOOP;
vt.SetColor[239+start, 0,
   Real.FixC[skyHue.R*255], Real.FixC[skyHue.G*255], Real.FixC[skyHue.B*255] ];
vt.SetColor[start, 0,
  Real.FixC[waterHue.R*255], Real.FixC[waterHue.G*255], Real.FixC[waterHue.B*255] ];
};
ShowRope: PUBLIC PROC[context: REF ThreeDScenes.Context, x, y: REAL,
        rope: Rope.ROPE, fontRope: Rope.ROPENIL, size: REAL ← 20] ~{
imagerCtx: Imager.Context ← GetImagerContext[context];
font: Imager.Font;
IF fontRope = NIL THEN fontRope ← "Xerox/Pressfonts/TimesRoman-MRR";
font ← ImagerFont.Find[fontRope];
font ← ImagerFont.Scale[font, size];
Imager.SetFont[imagerCtx, font];
Imager.SetXY[imagerCtx, [x, y]];
Imager.ShowRope[imagerCtx, rope];
};
AIS Files and Texture Generation
DitherImage: PUBLIC PROC[dstContext, rgbContext: REF ThreeDScenes.Context] ~ {
Action: PROC ~ {
width: NAT ← Real.FixI[MIN[dstContext.viewPort.w, rgbContext.viewPort.w] ];
height: NAT ← Real.FixI[MIN[dstContext.viewPort.h, rgbContext.viewPort.h] ];
pixel: Pixels.SampleSet ← NIL;
value, i1, i2, i3: NAT;
IF rgbContext.display.samplesPerPixel < 3
THEN SIGNAL ThreeDScenes.Error[[$MisMatch, "24-bit input needed for dithering"]];
i1 ← IF rgbContext.alphaBuffer THEN 1 ELSE 0; i2 ← i1 + 1; i3 ← i2 + 1;
FOR y: NAT IN [0..height) DO
FOR x: NAT IN [0..width) DO
pixel ← Pixels.GetPixel[rgbContext.display, x, y, pixel];
value ← ScanConvert.DitheredRGB[$PseudoClr, x, y, pixel[i1], pixel[i2], pixel[i3] ];
Pixels.PutValue[dstContext.display, x, y, value];
ENDLOOP;
ENDLOOP;
};
CedarProcess.DoWithPriority[background, Action];    -- be nice to other processess
};
ScaleImage: PUBLIC PROC[dstContext, srcContext: REF Context] ~ {
Action: PROC ~ {
widthScale: REAL ← dstContext.viewPort.w / srcContext.viewPort.w;
heightScale: REAL ← dstContext.viewPort.h / srcContext.viewPort.h;
height, width: NAT ← 0;
pixel: Pixels.SampleSet ← NIL;
i1, i2, i3: NAT;
IF srcContext.display.samplesPerPixel < 9
THEN SIGNAL ThreeDScenes.Error[[$UnImplemented, "No scaling yet"]];
i1 ← IF srcContext.alphaBuffer THEN 1 ELSE 0; i2 ← i1 + 1; i3 ← i2 + 1;
FOR y: NAT IN [0..height) DO
FOR x: NAT IN [0..width) DO
pixel ← Pixels.GetPixel[srcContext.display, x, y, pixel];
Pixels.PutPixel[dstContext.display, x, y, pixel];
ENDLOOP;
ENDLOOP;
};
CedarProcess.DoWithPriority[background, Action];    -- be nice to other processess
};
MakeStripes: PUBLIC PROC[context: REF ThreeDScenes.Context, numStripes, min, max, power: REAL] ~ {
viewPort: Rectangle ← [
Real.RoundC[context.viewPort.x],
Real.RoundC[context.viewPort.y],
Real.RoundC[context.viewPort.w],
Real.RoundC[context.viewPort.h]
];
factor: REAL ← ( numStripes * 2. * 3.1416 ) / viewPort.w;
maxValue: REAL ← 256.0;
map: NATIF context.alphaBuffer THEN 1 ELSE 0;
FOR x: NAT IN [ 0 .. viewPort.w ] DO
pxlValue: CARDINAL;
sineValue: REAL ← RealFns.Sin[ factor * x ];
sineValue ← RealFns.Power[ABS[sineValue], power] * Sgn[sineValue];
pxlValue ← Real.FixC[ maxValue * (((sineValue + 1.) / 2.0) * (max - min) + min) ];
Pixels.PutValue[ context.display, x, 0, pxlValue ];
ENDLOOP;
FOR y: NAT IN [ 1 .. viewPort.h ] DO
FOR x: NAT IN [ 0 .. viewPort.w ] DO
pxlValue: CARDINAL ← Pixels.GetValue[ context.display, x, 0, map ];
Pixels.PutValue[ context.display, x, y, pxlValue, map];
ENDLOOP;
ENDLOOP;
};
MakeStretchedSpots: PUBLIC PROC[context: REF ThreeDScenes.Context,
           spotsAcross, min, max, power: REAL,
           stretched: BOOLEANFALSE] ~ {
viewPort: Rectangle ← [
Real.RoundC[context.viewPort.x],
Real.RoundC[context.viewPort.y],
Real.RoundC[context.viewPort.w],
Real.RoundC[context.viewPort.h]
];
maxValue: REAL ← 255.0;
factor: REAL ← (spotsAcross * 2 * 3.1416) / viewPort.w;
map: NATIF context.alphaBuffer THEN 1 ELSE 0;
FOR y: NAT IN [ 0 .. viewPort.w ) DO
spread: REAL
IF stretched THEN
RealFns.Cos[ 3.1416 * (y - viewPort.h/2.0) / (viewPort.h) ]
ELSE 1.0;
sineY: REAL ← RealFns.Cos[ factor * y ];
sineY ← RealFns.Power[ABS[sineY], power] * Sgn[sineY];
FOR x: NAT IN [ 0 .. Real.RoundC[viewPort.w] ) DO
pxlValue: LONG CARDINAL;
sineX: REAL ← RealFns.Cos[ spread * factor * (x - viewPort.w/2.0) ];
sineX ← RealFns.Power[ABS[sineX], power] * Sgn[sineX];
pxlValue ← Real.FixC[
maxValue * (((sineX * sineY + 1.) / 2.0) * (max - min) + min) ];
Pixels.PutValue[ context.display, x, y, pxlValue, map];
ENDLOOP;
ENDLOOP;
};
DrawQuad: PUBLIC PROC[context: REF ThreeDScenes.Context, intensity: REAL,
      x1, y1, x2, y2, x3, y3, x4, y4: REAL] ~ {
poly: REF ThreeDSurfaces.Patch ← NEW[ ThreeDSurfaces.Patch[4] ];
poly.nVtces ← 4;
poly.vtx[0].coord.x ← x1; poly.vtx[0].coord.y ← y1; poly.vtx[0].shade.g ← intensity;
poly.vtx[1].coord.x ← x2; poly.vtx[1].coord.y ← y2; poly.vtx[1].shade.g ← intensity;
poly.vtx[2].coord.x ← x3; poly.vtx[2].coord.y ← y3; poly.vtx[2].shade.g ← intensity;
poly.vtx[3].coord.x ← x4; poly.vtx[3].coord.y ← y4; poly.vtx[3].shade.g ← intensity;
Tilers.FancyTiler[context, poly];
};
View and Lighting control
SetAmbientLight: PUBLIC PROC [context: REF ThreeDScenes.Context, color: Rope.ROPE] ~ {
ambientColor: REF RGBNEW[ RGB
         ImagerColor.RGBFromHSL[ NamedColors.RopeToHSL[color] ] ];
[] ← Atom.PutPropOnList[context.environment, $AmbientLight, ambientColor];
FOR i: NAT IN [0..context.shapes.length) DO
context.shapes[i].shadingInValid ← TRUE;
ENDLOOP;
};
SetBackground: PUBLIC PROC [context: REF ThreeDScenes.Context, color: Rope.ROPE] ~ {
bkgrdColor: REF RGBNEW[ RGB
         ImagerColor.RGBFromHSL[ NamedColors.RopeToHSL[color] ] ];
context.props ← Atom.PutPropOnList[context.props, $BackGround, bkgrdColor]; -- set color
};
GetBackground: PUBLIC PROC [context: REF ThreeDScenes.Context] RETURNS [color: RGB] ~ {
ref: REF ← Atom.GetPropFromList[context.props, $BackGround]; -- get background color
IF ref # NIL THEN color ← NARROW[ref, REF RGB]^ ELSE color ← [0., 0., 0.];
};
Shape Manipulation
Hide: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE] ~ {
Maintain data but don't display
shape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes,
                    shapeName ];
shape.props ← Atom.PutPropOnList[shape.props, $Hidden, $DoIt];
};
Reveal: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE] ~ { -- undo Hide
shape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes,
                    shapeName ];
shape.props ← Atom.RemPropFromList[shape.props, $Hidden];
};
MakePlane: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE, squaresPerSide: NAT] ~{
surface: REF ThreeDSurfaces.PtrPatchSequence;
shape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.NewShape[shapeName];
vtcesPerSide: NAT ← squaresPerSide + 1;
shape.numSurfaces ← squaresPerSide * squaresPerSide;
shape.vertex ← NEW[ ThreeDScenes.VertexSequence[ vtcesPerSide * vtcesPerSide ] ];
shape.boundingRadius ← 1.414;
shape.surface ← NEW[ ThreeDSurfaces.PtrPatchSequence[shape.numSurfaces] ];
surface ← NARROW[shape.surface, REF ThreeDSurfaces.PtrPatchSequence];
FOR i: NAT IN [0..shape.vertex.length) DO
IF shape.vertex[i] # NIL THEN {
divisor: REAL ← Real.Float[squaresPerSide] / 2.;
shape.vertex[i] ← NEW[ThreeDScenes.Vertex];
shape.vertex[i].x ← (i / vtcesPerSide) / divisor - 1.;    -- x-coordinate
shape.vertex[i].y ← (i MOD vtcesPerSide) / divisor - 1.;  -- y-coordinate
shape.vertex[i].z ← 0.;   -- constant z-coordinate
};
ENDLOOP;
FOR i: INT IN [0..shape.numSurfaces) DO
vtxNumber: NAT ← i + (i / squaresPerSide); -- add one at end of each row
surface[i] ← NEW[ThreeDSurfaces.PtrPatch[4]];
surface[i].nVtces ← 4;
surface[i].vtxPtr[0] ← vtxNumber;
surface[i].vtxPtr[1] ← vtxNumber + 1;
surface[i].vtxPtr[2] ← vtxNumber + vtcesPerSide + 1;
surface[i].vtxPtr[3] ← vtxNumber + vtcesPerSide;
ENDLOOP;
context.shapes ← ThreeDScenes.AddShape[context.shapes, shape];
};
AddShapeAt: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE, fileName: Rope.ROPE,
        position: Triple←[0.,0.,0.],
        type: ATOM ← $ConvexPolygon, insideVisible: BOOLEANFALSE] ~ {
shape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.NewShape[shapeName];
shape.fileName ← PrependWorkingDirectory[context, fileName];
ThreeDSurfaces.LoadShape[   -- get vertices, polygons, etc.
shape,
PrependWorkingDirectory[context, fileName],
type,   -- $ConvexPolygon, $Bezier
insideVisible
];
ThreeDScenes.PlaceShape[shape, position];
context.shapes ← ThreeDScenes.AddShape[context.shapes, shape];
IF Atom.GetPropFromList[context.props, type] = NIL
THEN RegisterSurfaceType[context, type];
};
SetFacetedColor: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE, color: RGB] ~ {
shape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes,
                    shapeName ];
IF shape # NIL
THEN {
patchInfo: REF ThreeDScenes.ShadingSequence ← NARROW[
ThreeDScenes.GetShading[ shape, $PatchColors ]
];
IF patchInfo = NIL
THEN patchInfo ← NEW[ThreeDScenes.ShadingSequence[shape.numSurfaces] ]
ELSE IF ThreeDScenes.GetShading[ shape, $PatchColorFile ] # NIL
THEN SIGNAL ThreeDScenes.Error[[$MisMatch, "Patches individually colored"]];
FOR i: NAT IN [0..shape.numSurfaces) DO
patchInfo[i] ← NEW[ ThreeDScenes.ShadingValue ];
patchInfo[i].r ← color.R;
patchInfo[i].g ← color.G;
patchInfo[i].b ← color.B;
ENDLOOP;
ThreeDScenes.PutShading[shape, $Type, $Faceted];
ThreeDScenes.PutShading[ shape, $Color, NEW[RGB ← color] ];
ThreeDScenes.PutShading[ shape, $PatchColors, patchInfo ];
shape.shade ← ThreeDScenes.InitShades[ shape ];
ThreeDSurfaces.GetPolyNormals[shape ! ThreeDScenes.Error =>
           IF reason.code = $MisMatch THEN CONTINUE ];
};
};
SetSmoothColor: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE, color: RGB] ~ {
shape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes,
                    shapeName ];
IF shape # NIL
THEN {
ThreeDScenes.PutShading[shape, $Type, $Smooth];
ThreeDScenes.PutShading[ shape, $Color, NEW[RGB ← color] ];
IF ThreeDScenes.GetShading[ shape, $VertexColorFile ] # NIL
THEN SIGNAL ThreeDScenes.Error[[$MisMatch, "Vertices individually colored"]];
shape.shade ← ThreeDScenes.InitShades[ shape ]; -- color vtces
ThreeDSurfaces.GetVtxNormals[ shape ! ThreeDScenes.Error =>
         IF reason.code = $MisMatch THEN CONTINUE ];
};
};
SetLinesColor: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE, color: RGB] ~ {
shape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes,
                    shapeName ];
IF shape # NIL
THEN {
ThreeDScenes.PutShading[shape, $Type, $Lines];
ThreeDScenes.PutShading[ shape, $Color, NEW[RGB ← color] ];
IF ThreeDScenes.GetShading[ shape, $VertexColorFile ] # NIL
THEN SIGNAL ThreeDScenes.Error[[$MisMatch, "Vertices individually colored"]];
shape.shade ← ThreeDScenes.InitShades[ shape ]; -- color vtces
};
};
SetShininess: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE, shininess: REAL] ~ {
shape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes,
                    shapeName ];
IF shape # NIL THEN ThreeDScenes.PutShading[shape, $Shininess, NEW[REAL ← shininess]];
};
SetTransmittance: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE, t: REAL] ~ {
shape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes,
                    shapeName ];
IF shape # NIL THEN {
ThreeDScenes.PutShading[ shape, $Transmittance, NEW[REAL ← t] ];
IF shape.shade # NIL THEN FOR i: NAT IN [0..shape.shade.length) DO
IF shape.shade[i] # NIL THEN shape.shade[i].t ← t;
ENDLOOP;
};
};
ShadingProcName: PUBLIC PROC[context: REF Context, shapeName, procName: Rope.ROPE ] ~ {
shape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes,
                    shapeName ];
shadeProc: ScanConvert.GetColorProc ← SolidTextures.RopeToProc[procName];
storeProc: ThreeDScenes.VtxToRealSeqProc ← SolidTextures.GetLerpedVals;
ThreeDScenes.PutShading[ shape, $ShadingProcs,
        NEW
[ ThreeDScenes.ShadingProcs ← [ storeProc, shadeProc ] ] ];
};
GetShadingProcs: PUBLIC PROC[context: REF ThreeDScenes.Context, shapeName: Rope.ROPE,
          storeProc: ThreeDScenes.VtxToRealSeqProc,
          shadeProc: ScanConvert.GetColorProc] ~{
shape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes,
                    shapeName ];
ThreeDScenes.PutShading[ shape, $ShadingProcs,
        NEW
[ ThreeDScenes.ShadingProcs ← [ storeProc, shadeProc ] ] ];
};
GetNormals: PUBLIC PROC[context: REF ThreeDScenes.Context, shapeName, normalFile: Rope.ROPE] ~ {
shape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes,
                     shapeName ];
stream: IO.STREAM;
numEntries: NAT;
[stream, numEntries] ← ThreeDScenes.SetUpStandardFile[
             PrependWorkingDirectory[context, normalFile] ];
ThreeDScenes.ReadNormals[ shape.shade, stream, numEntries ];
};
GetVertexColors: PUBLIC PROC[context: REF ThreeDScenes.Context, shapeName, colorFile: Rope.ROPE] ~ {
shape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes,
                     shapeName ];
stream: IO.STREAM;
numEntries: NAT;
[stream, numEntries] ← ThreeDScenes.SetUpStandardFile[
             PrependWorkingDirectory[context, colorFile] ];
ThreeDScenes.ReadColors[ shape.shade, stream, numEntries ];
ThreeDScenes.PutShading[ shape, $VertexColorFile, colorFile ];
};
GetPolygonColors: PUBLIC PROC[context: REF ThreeDScenes.Context, shapeName, colorFile:Rope.ROPE] ~ {
ThreeDSurfaces.GetPatchColors[
ThreeDScenes.FindShape[ context.shapes, shapeName ],
PrependWorkingDirectory[context, colorFile]
];
};
GetTextureCoords: PUBLIC PROC[context: REF ThreeDScenes.Context, shapeName, coordinateFile: Rope.ROPE] ~ {
Get Texture Coordinates for vertices
texture: REF TextureMaps.TextureMap;
shape: REF ThreeDScenes.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes,
                    shapeName ];
stream: IO.STREAM;
numEntries: NAT;
[stream, numEntries] ← ThreeDScenes.SetUpStandardFile[
           PrependWorkingDirectory[context, coordinateFile] ];
ThreeDScenes.ReadTextureCoords[ shape.shade, stream, numEntries ];
texture ← NARROW[ThreeDScenes.GetShading[shape, $TextureMap],
      REF
TextureMaps.TextureMap];
IF texture = NIL THEN texture ← NEW[TextureMaps.TextureMap];
texture.props ← Atom.PutPropOnList[texture.props, $CoordType, $File];
texture.props ← Atom.PutPropOnList[texture.props, $Coords, coordinateFile];
ThreeDScenes.PutShading[shape, $TextureMap, texture];
};
Scene Manipulation
SaveOnFile: PUBLIC PROC[context: REF Context, fileName: Rope.ROPE] ~ {
wDir: Rope.ROPE ← PrependWorkingDirectory[context, NIL];
output: IO.STREAMFS.StreamOpen[fileName: fileName, accessOptions: $create, wDir: wDir];
IO.PutRope[ output, Rope.Cat[" -- ", fileName, "\n"] ];
ThreeDScenes.WriteScene[context, output];
IO.Close[output];
};
RestoreFromFile: PUBLIC PROC[context: REF Context, fileName: Rope.ROPE] ~ {
wDir: Rope.ROPE ← PrependWorkingDirectory[context, NIL];
input: IO.STREAMFS.StreamOpen[fileName: fileName, accessOptions: $read, wDir: wDir];
[] ← IO.GetLineRope[ input ];   -- ignore first line
ThreeDScenes.ReadScene[context, input];
IO.Close[input];
};
MakeFrameFromFile: PUBLIC PROC[context: REF Context, fileName: Rope.ROPE] ~ {
wDir: Rope.ROPENARROW[ Atom.GetPropFromList[context.props, $WDir] ];
time: REAL;
log: IO.STREAMNARROW[ Atom.GetPropFromList[context.props, $Log] ];
bufferCtx: REF ThreeDScenes.Context ← ThreeDScenes.Create[
10,
10,
context.renderMode,
context.alphaBuffer
];
bufferCtx.display ← context.display;
bufferCtx.props ← Atom.PutPropOnList[bufferCtx.props, $WDir, wDir];
time ← CurrentTime[];
RestoreFromFile[bufferCtx, fileName];       -- may mash viewport
IF log # NIL THEN log.PutRope[ Rope.Cat[ "Setup Time: ", ElapsedTime[time], "\n"] ];
MakeFrame[bufferCtx];
bufferCtx ← NIL;
ThreeDScenes.SetViewPort[context, context.viewPort];   -- restore viewport
};
Frame Generation and Animation
MakeFrame: PUBLIC PROC[context: REF ThreeDScenes.Context] ~ {
time: REAL ← CurrentTime[];
log: IO.STREAMNARROW[ Atom.GetPropFromList[context.props, $Log] ];
IF context.alphaBuffer
THEN ThreeDScenes.FillViewPort[context, [0.0,0.0,0.0] ] -- clear screen and alpha buffer
ELSE ThreeDScenes.FillInBackGround[context];    -- load background
IF context.lineDrawing AND NOT context.alphaBuffer
THEN ShowWireFrameShapes[context]
ELSE ShowShapes[context];
IF context.alphaBuffer THEN ThreeDScenes.FillInBackGround[context]; -- load background
IF log # NIL THEN {
log.PutRope[ Rope.Cat[ "Home Processor Frame Time: ", ElapsedTime[time], "\n"] ];
FlushLog[context];
};
};
MakeHiResFrame: PUBLIC PROC[context: REF ThreeDScenes.Context,
          width, height: NAT, name: Rope.ROPE] ~ {
time: REAL ← CurrentTime[];
log: IO.STREAMNARROW[ Atom.GetPropFromList[context.props, $Log] ];
hiResCtxt: REF ThreeDScenes.Context ← ThreeDScenes.Create[
width,
height,
context.renderMode,
context.alphaBuffer
];
CopyContextData[dstCtx: hiResCtxt, srcCtx: context];
hiResCtxt.viewPort ← [ x: 0.0, y: 0.0, w: Real.Float[width], h: Real.Float[height] ];
[] ← StartLog[hiResCtxt];
FOR i: NAT IN [0..hiResCtxt.shapes.length) DO
hiResCtxt.shapes[i].vtcesInValid ← TRUE;
ENDLOOP;
IF hiResCtxt.alphaBuffer
THEN ThreeDScenes.FillViewPort[hiResCtxt, [0.0,0.0,0.0] ] -- clear screen and alpha buffer
ELSE ThreeDScenes.FillInBackGround[hiResCtxt];    -- load background
IF hiResCtxt.lineDrawing AND NOT hiResCtxt.alphaBuffer
THEN ShowWireFrameShapes[hiResCtxt]
ELSE ShowShapes[hiResCtxt];
IF hiResCtxt.alphaBuffer            -- load background
THEN ThreeDScenes.FillInBackGround[hiResCtxt];
AISAnimation.PutAIS[hiResCtxt, name ];
IF log # NIL THEN {
log.PutRope[ Rope.Cat[ "Home Processor Frame Time: ", ElapsedTime[time], "\n"] ];
FlushLog[context];
};
};
ShowShapes: PUBLIC PROC[context: REF ThreeDScenes.Context] ~ {
Action: PROC ~ {
ThreeDSurfaces.ShowObjects[
context: context,
frontToBack: context.alphaBuffer
]
};
CedarProcess.DoWithPriority[background, Action];
};
ShowWireFrameShapes: PUBLIC PROC[context: REF ThreeDScenes.Context] ~ {
Action: PROC ~ {
ThreeDSurfaces.ShowWireFrameObjects[ context ];
};
CedarProcess.DoWithPriority[background, Action];
};
OrbitEye: PUBLIC PROC[context: REF ThreeDScenes.Context,
      lookingFrom, lookingAt, axis: Triple, framesPerRev: NAT,
      filename: Rope.ROPENIL, numFrames: NAT ← 32767, startAt: NAT ← 0] ~ {
frameNo: NAT ← startAt;
bufContext, ditherContext: REF ThreeDScenes.Context ← NIL;
NewFrame: PROC[lookingFrom, lookingAt: Triple] ~ {
ThreeDScenes.SetView[ bufContext, lookingFrom, lookingAt];
MakeFrame[bufContext];
IF bufContext # context THEN Pixels.Transfer[ context.display, bufContext.display];
CedarProcess.CheckAbort[];   -- check for external abort request before proceeding
frameNo ← frameNo + 1;
IF filename # NIL THEN
IF context.renderMode = $Dorado24 OR context.renderMode = $FullClr
THEN {
DitherImage[ditherContext, context];
AISAnimation.StoreFiles[ditherContext, filename, frameNo];
}
ELSE AISAnimation.StoreFiles[context, filename, frameNo];
};
IF filename = NIL AND context.renderMode # $LF AND context.renderMode # $Dithered
THEN {             -- double buffer if not storing files
bufContext ← ThreeDScenes.Create[
context.display.pixels[0].subMap.size.f,
context.display.pixels[0].subMap.size.s,
context.renderMode,
context.alphaBuffer
];
CopyContextData[dstCtx: bufContext, srcCtx: context];
}
ELSE {        -- no double buffer if storing files or using Imager
bufContext ← context;
IF context.renderMode = $Dorado24 OR context.renderMode = $FullClr
THEN ditherContext ← ThreeDScenes.Create[
context.display.pixels[context.display.samplesPerPixel-1].subMap.size.f,
context.display.pixels[context.display.samplesPerPixel-1].subMap.size.s,
$PseudoClr,
];
};
Animation3D.MoveInOrbit[ lookingFrom, lookingAt, axis, NewFrame,
        framesPerRev, numFrames ];
};
MakeFramesFromTo: PUBLIC PROC[context: REF ThreeDScenes.Context,
        lookingFrom, lookingAt, toLookingFrom, toLookingAt: Triple,
        numFrames: NAT, startAt: NAT ← 0, filename: Rope.ROPENIL] ~ {
frameNo: NAT ← startAt;
NewFrame: PROC[lookingFrom, lookingAt: Triple] ~ {
ThreeDScenes.SetView[ context, lookingFrom, lookingAt];
MakeFrame[context];
CedarProcess.CheckAbort[];   -- check for external abort request before proceeding
frameNo ← frameNo + 1;
IF filename # NIL THEN
AISAnimation.StoreFiles[context, filename, frameNo];
};
Animation3D.MoveOnLine[ lookingFrom, lookingAt, toLookingFrom, toLookingAt,
        NewFrame, numFrames ];
};
END.