ThreeDMiscImpl.mesa
Last Edited by: Crow, December 16, 1986 5:43:41 pm PST
DIRECTORY
Basics     USING [ BITSHIFT, BITOR, BITAND ],
BasicTime   USING [ PulsesToSeconds, GetClockPulses ],
Atom     USING [ DottedPair, DottedPairNode, PropList, PutPropOnList,
         GetPropFromList, RemPropFromList ],
CedarProcess   USING [ CheckAbort, DoWithPriority ],
ViewerIO    USING [ CreateViewerStreams ],
Terminal    USING [ Virtual, GetColorMode, SetColor, GetColor,
         WaitForBWVerticalRetrace, ColorMode,
        GetRedMap, GetGreenMap, GetBlueMap,
        SetRedMap, SetGreenMap, SetBlueMap ],
IO      USING [ STREAM, PutF, PutRope, Flush, Close, GetLineRope,
         rope, time ],
UserCredentials  USING [ Get ],
FS      USING [ StreamOpen ],
Rope     USING [ ROPE, Index, Cat, Equal ] ,
Convert    USING [ RopeFromReal, RopeFromCard ],
Real     USING [ Float, FixC, RoundC, FixI ],
RealFns    USING [ Sin, Cos, Power ],
QuickViewer   USING [ DrawInViewer, QuickView ],
Imager    USING [ Context, Rectangle, SetColor, Font, MaskRectangle,
         SetFont, SetXY, ShowRope ],
ImagerColor   USING [ ColorFromRGB, RGBFromHSL, RGB ],
ImagerFont   USING [ Find, Scale ],
ImagerInterpress  USING [ Close, Create, DoPage, Ref ],
NamedColors   USING [ RopeToHSL ],
Pixels     USING [ PixelBuffer, Extent, BYTE, ValueOp, Transfer, GetSampleSet,
         SampleSet, PutPixel, GetScanSeg, PutScanSeg,
         SampleSetSequence, SubMapSequence],
Plane3d    USING [ DistanceToPt ],
ScanConvert   USING [ GetColorProc, MappedRGB, DitheredRGB, PutLine ],
ThreeDBasics  USING [ ClipState, Context, NoneOut, OutCode, NatSequence, Pair, Quad,
         RGB
, Rectangle, ShadingSequence, ShadingValue,
         ShapeInstance, ShapeSequence, Triple, Vertex, VertexSequence,
         VtxToRealSeqProc ],
ThreeDScenes  USING [ AddAlphaBuffer, AddDepthBuffer, AddShape, Create,
         DisplayFromImagerContext, DisplayFromVM, Error,
         FillInBackGround, FillViewPort, FindShape, GetShading,
         InitShades, NewShape, PlaceShape, PutShading, ReadScene,
         WriteScene, SetView, ShadingProcs, XfmPtToEyeSpace,
         XfmPtToDisplay
         ],
ThreeDSurfaces  USING [ Patch, PtrPatch, ShowObjects, ShowWireFrameObjects,
         PtrPatchSequence, ReadShape, CloneShape, GetPolyNormals,
         PatchProcs, PatchDisplayProc, RegisterSurfaceType ],
Tilers     USING [ FancyTiler],
SolidTextures   USING [ RopeToProc, GetLerpedVals ],
AISAnimation  USING [ GetAIS, PutAIS, StoreFiles ],
Animation3D   USING [ MoveInOrbit, MoveOnLine ],
StandardPatches  USING [ BezierExpand, BezierSubdivide, BezierDisplay,
         BezierDisplayLines ],
ThreeDMisc   USING [ ];
ThreeDMiscImpl: CEDAR MONITOR
IMPORTS AISAnimation, Animation3D, Atom, Basics, BasicTime, CedarProcess, Convert, FS, Imager, ImagerColor, ImagerFont, ImagerInterpress, IO, NamedColors, Pixels, Plane3d, QuickViewer, Real, RealFns, Rope, ScanConvert, SolidTextures, StandardPatches, Terminal, ThreeDScenes, ThreeDSurfaces, Tilers, ViewerIO, UserCredentials
EXPORTS ThreeDMisc
~ BEGIN
Types
Context: TYPE ~ ThreeDBasics.Context;
Pair: TYPE ~ ThreeDBasics.Pair;           -- RECORD [ x, y: REAL];
Triple: TYPE ~ ThreeDBasics.Triple;
RGB: TYPE ~ ThreeDBasics.RGB;
IntRGB: TYPE ~ RECORD[ r, g, b: CARDINAL];
IntRGBSequence: TYPE ~RECORD [ SEQUENCE length: NAT OF IntRGB ];
Rectangle: TYPE ~ ThreeDBasics.Rectangle;
NatSequence: TYPE ~ ThreeDBasics.NatSequence;
ClipState: TYPE ~ ThreeDBasics.ClipState;
Utility Procedures
RGBtoPixelValue: PUBLIC PROC[context: REF Context, clr: RGB,
          values: Pixels.SampleSet ← NIL]
      RETURNS[ Pixels.SampleSet ] ~ {
base: NATIF context.alphaBuffer THEN 1 ELSE 0;
IF values = NIL THEN values ← Pixels.GetSampleSet[5];
values[0] ← 0;        
SELECT context.renderMode FROM
$FullColor, $Dorado24  => {
values[0] ← Real.RoundC[clr.R*255.0];
values[1] ← Real.RoundC[clr.G*255.0];
values[2] ← Real.RoundC[clr.B*255.0];
values.length ← 3;
};
$Grey   => {
values[0] ← Real.RoundC[(clr.R + clr.G + clr.B) / 3.0 * 255.0];
values.length ← 1;
};
$Dithered, $PseudoColor => {
values[0] ← ScanConvert.MappedRGB[
context.renderMode,
[ Real.RoundC[clr.R*255.0], Real.RoundC[clr.G*255.0], Real.RoundC[clr.B*255.0] ]
];
values.length ← 1;
};
ENDCASE  => SIGNAL ThreeDScenes.Error[[$MisMatch, "Improper renderMode"]];
RETURN[values];
};
PutPixel: PUBLIC PROC[context: REF Context, x, y: NAT, clr: RGB] ~ {
clrValues: Pixels.SampleSet ← RGBtoPixelValue[context, clr];
Pixels.PutPixel[context.display, x, y, clrValues];
};
ClipLineWithPlane: PUBLIC PROC[plane: ThreeDBasics.Quad, p1, p2: Triple]
       RETURNS[ insidePt, clippedPt: Triple, state: 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
clrValues: Pixels.SampleSet ← RGBtoPixelValue[context, clr];
p1, p2: Triple;
code1, code2, orOfCodes : ThreeDBasics.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] ],
ThreeDBasics.OutCode
];
IF orOfCodes # ThreeDBasics.NoneOut        -- not all inside?
THEN {
andOfCodes: ThreeDBasics.OutCode ← LOOPHOLE[
Basics.BITAND[LOOPHOLE[code1], LOOPHOLE[code2] ],
ThreeDBasics.OutCode
];
IF andOfCodes # ThreeDBasics.NoneOut     -- all outside?
THEN RETURN[]
ELSE {          -- clipping needed, do it
clipState: 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, NIL, p1];  -- xfm to screen
p2 ← ThreeDScenes.XfmPtToDisplay[context, NIL, p2];
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 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] ~ {
timeX10: REAL ← 10.0 * (BasicTime.PulsesToSeconds[BasicTime.GetClockPulses[]] - startTime);
RETURN[ Rope.Cat[ Convert.RopeFromReal[ Real.FixC[timeX10] / 10.0 ], " secs. " ] ];
};
CurrentTime: PROC[] RETURNS[REAL] ~ {
RETURN[ BasicTime.PulsesToSeconds[BasicTime.GetClockPulses[]] ];
};
UpdateDisplay: PUBLIC PROC[context: REF Context] RETURNS[changed: BOOLEAN] ~ {
GetDisplay: PROC[ imagerCtx: Imager.Context ] ~ {
ThreeDScenes.DisplayFromImagerContext[context, imagerCtx];
IF context.depthBuffer THEN ThreeDScenes.AddDepthBuffer[context];
IF context.alphaBuffer THEN ThreeDScenes.AddAlphaBuffer[context];
};
changed ← FALSE;
IF context.viewer # NIL THEN {
view: REF QuickViewer.QuickView ← NARROW[context.viewer];
IF view.changed THEN {
view.changed ← FALSE;
changed ← TRUE;
QuickViewer.DrawInViewer[view, GetDisplay];   -- gets up-to-date imager context
FOR i: NAT IN [0..context.shapes.length) DO
context.shapes[i].vtcesInValid ← TRUE;
context.shapes[i].shadingInValid ← TRUE;
ENDLOOP;
ThreeDScenes.SetView[     -- get new screen dimensions into transformations
context:  context,
eyePoint:  context.eyePoint,
ptOfInterest: context.ptOfInterest,
fieldOfView: context.fieldOfView,
rollAngle: context.rollAngle,
upDirection: context.upDirection,
hitherLimit: context.hitherLimit,
yonLimit:  context.yonLimit
];
};
};
};
CopyContextData: PUBLIC PROC [dstCtx, srcCtx: REF 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, most uses want a separate one
dstCtx.viewPort ← srcCtx.viewPort;
dstCtx.renderMode ← srcCtx.renderMode;
dstCtx.lineDrawing ← srcCtx.lineDrawing;
dstCtx.depthResolution ← srcCtx.depthResolution;
dstCtx.sortSequence ← srcCtx.sortSequence;
FOR list: Atom.PropList ← srcCtx.props, list.rest UNTIL list = NIL DO-- make new proplist
element: Atom.DottedPair ← NEW[Atom.DottedPairNode ← list.first^];
dstCtx.props ← CONS[element, dstCtx.props];
ENDLOOP;
};
CopyDisplayData: PUBLIC PROC [dstCtx, srcCtx: REF Context] ~ {
dstCtx.display ← srcCtx.display;
dstCtx.display.pixels ← NEW[ Pixels.SubMapSequence[srcCtx.display.pixels.length] ];
FOR i: NAT IN [0..srcCtx.display.pixels.length) DO
dstCtx.display.pixels[i] ← srcCtx.display.pixels[i];
ENDLOOP;
dstCtx.display.props ← NIL;         -- make new proplist
FOR list: Atom.PropList ← srcCtx.display.props, list.rest UNTIL list = NIL DO
element: Atom.DottedPair ← NEW[Atom.DottedPairNode ← list.first^];
dstCtx.display.props ← CONS[element, dstCtx.display.props];
ENDLOOP;
dstCtx.alphaBuffer ← srcCtx.alphaBuffer;
dstCtx.depthBuffer ← srcCtx.depthBuffer;
};
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"]];
};
RegisterSurfaceDisplayProc: PUBLIC PROC[context: REF Context, type: ATOM,
       proc: ThreeDSurfaces.PatchDisplayProc, lines: BOOLEANFALSE ] ~ {
procRecord: REF ThreeDSurfaces.PatchProcs ← NARROW[
              Atom.GetPropFromList[context.props, type] ];
IF lines
THEN procRecord.displayLines ← proc
ELSE procRecord.display ← proc;
ThreeDSurfaces.RegisterSurfaceType[ context, type, procRecord];
};
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 Context, clr: RGB]
      RETURNS[Pixels.BYTE] ~ {
value: NAT ← ScanConvert.MappedRGB[context.renderMode, clr];
RETURN[value];
};
SetNamedColor: PUBLIC PROC [imagerCtx: Imager.Context, color: Rope.ROPE] ~ {
clr: RGB ← ImagerColor.RGBFromHSL[ NamedColors.RopeToHSL[color] ];
Imager.SetColor[ imagerCtx, ImagerColor.ColorFromRGB[clr] ];
};
SetRGBColor: PUBLIC PROC [imagerCtx: Imager.Context, clr: RGB] ~ {
Imager.SetColor[ imagerCtx, 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 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;
};
ShowMapOnLog: PUBLIC PROC [context: REF Context, vt: Terminal.Virtual] ~ {
RopeFromQuad: PROC[ i, r, g, b: CARD] RETURNS[ Rope.ROPE ] ~ {
output: Rope.ROPE ← Rope.Cat[ " \t ", Convert.RopeFromCard[i], " ",
           Convert.RopeFromCard[r], " " ];
output ← Rope.Cat[ output, Convert.RopeFromCard[g], " ", Convert.RopeFromCard[b] ];
RETURN[ output ];
};
log: IO.STREAMNARROW[ Atom.GetPropFromList[context.props, $Log] ];
state: Terminal.ColorMode ← vt.GetColorMode[];
maxVal: REALIF state.full
THEN 255.0
ELSE Real.Float[Basics.BITSHIFT[1, state.bitsPerPixelChannelA] - 1];
FOR i: NAT IN [ 0 .. Real.FixC[maxVal] ] DO      -- linear ramp exponentiated
jr, jg, jb: CARD;
IF Terminal.GetColorMode[vt].full
THEN {
jr ← vt.GetRedMap[i];
jg ← vt.GetGreenMap[i];
jb ← vt.GetBlueMap[i];
}
ELSE [jr, jg, jb] ← vt.GetColor[i];
IF log # NIL THEN {
log.PutRope[ RopeFromQuad[ i, jr, jg, jb ] ];
IF (i/4) * 4 = i THEN log.PutRope["\n"];
FlushLog[context];
};
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 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] ];
SELECT context.renderMode FROM
$FullColor, $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
$FullColor, $Dorado24  => {
scanSeg[0][x] ← expTableR[scanSeg[0][x]];
scanSeg[1][x] ← expTableG[scanSeg[1][x]];
scanSeg[2][x] ← expTableB[scanSeg[2][x]];
};
$Grey   => scanSeg[0][x] ← expTableG[scanSeg[0][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 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;
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
$FullColor, $Dorado24  => {
scale: REAL;
redVal: CARDINAL ← scanSeg[0][x];
grnVal: CARDINAL ← scanSeg[1][x];
bluVal: CARDINAL ← scanSeg[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[0][x] ← redVal;
scanSeg[1][x] ← grnVal;
scanSeg[2][x] ← bluVal;
};
ENDCASE => SIGNAL ThreeDScenes.Error[[$MisMatch, "Only for full color"]];
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 Context, x, y: REAL, rope: Rope.ROPE,
       color: Rope.ROPENIL, fontRope: Rope.ROPENIL, size: REAL ← 20] ~ {
DoRope: PROC[imagerCtx: Imager.Context] ~ {
font: Imager.Font;
IF color = NIL THEN color ← "Vivid Yellow";
SetNamedColor[imagerCtx, color ];
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];
};
QuickViewer.DrawInViewer[        -- gets up-to-date imager context
NARROW[context.viewer],
DoRope
];
};
AIS Files and Texture Generation
DitherImage: PUBLIC PROC[dstContext, rgbContext: REF 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] ];
scanSegIn, scanSegOut: REF Pixels.SampleSetSequence ← NIL;
IF rgbContext.display.samplesPerPixel < 3
THEN SIGNAL ThreeDScenes.Error[[$MisMatch, "24-bit input needed for dithering"]];
FOR y: NAT IN [0..height) DO
scanSegIn ← Pixels.GetScanSeg[rgbContext.display, 0, y,
         Real.RoundC[rgbContext.viewPort.w], scanSegIn];
scanSegOut ← Pixels.GetScanSeg[dstContext.display, 0, y,
         Real.RoundC[dstContext.viewPort.w], scanSegOut];
FOR x: NAT IN [0..width) DO
scanSegOut[0][x] ← ScanConvert.DitheredRGB[$PseudoColor, x, y,
         scanSegIn[0][x], scanSegIn[1][x], scanSegIn[2][x] ];
ENDLOOP;
Pixels.PutScanSeg[dstContext.display, 0, y, Real.RoundC[dstContext.viewPort.w],
     scanSegOut];
ENDLOOP;
};
CedarProcess.DoWithPriority[background, Action];    -- be nice to other processess
};
ScaleDownImage: PUBLIC PROC[dstContext, srcContext: REF Context] ~ {
Bresenham-like walk across image, areas averaged down
Action: PROC ~ {
srcHght: NAT ← Real.RoundC[srcContext.viewPort.h];
dstHght: NAT ← Real.RoundC[dstContext.viewPort.h];
srcWdth: NAT ← Real.RoundC[srcContext.viewPort.w];
dstWdth: NAT ← Real.RoundC[dstContext.viewPort.w];
numHits: REF NatSequence ← NEW[ NatSequence[dstWdth] ];
xPos, yPos: INTEGER;
xCtr, yCtr: NAT ← 0;
scanSegIn, scanSegOut: REF Pixels.SampleSetSequence ← NIL;
scanSegOutPixels.GetScanSeg[dstContext.display, 0, 0, dstWdth];
FOR x: NAT IN [0..dstWdth) DO          -- clear the pixels
FOR i: NAT IN [0..scanSegOut.length) DO scanSegOut[i][x] ← 0; ENDLOOP;
ENDLOOP;
yPos ← 2 * dstHght - srcHght;
FOR x: NAT IN [0..dstWdth) DO numHits[x] ← 0; ENDLOOP;
FOR y: NAT IN [ 0 .. srcHght ) DO     -- work up through source scan lines
scanSegIn ← Pixels.GetScanSeg[srcContext.display, 0, y, srcWdth, scanSegIn];
xPos ← 2 * dstWdth - srcWdth;
xCtr ← 0;
FOR x: NAT IN [ 0 .. srcWdth ) DO     -- work across source scan line
FOR i: NAT IN [0..scanSegOut.length) DO
scanSegOut[i][xCtr] ← scanSegOut[i][xCtr] + scanSegIn[i][x]; -- sum intensities
ENDLOOP;
numHits[xCtr] ← numHits[xCtr] + 1;        -- sum hits per pixel
IF xPos > 0 THEN { 
xCtr ← xCtr + 1; xPos ← xPos - 2 * srcWdth; IF xCtr > dstWdth THEN EXIT;
};
xPos ← xPos + 2 * dstWdth;
ENDLOOP;
IF yPos > 0 THEN {            -- big increment exceeded
FOR x: NAT IN [0..dstWdth) DO
FOR i: NAT IN [0..scanSegOut.length) DO
scanSegOut[i][x] ← scanSegOut[i][x] / numHits[x];
ENDLOOP;
numHits[x] ← 0;
ENDLOOP;
Pixels.PutScanSeg[dstContext.display, 0, yCtr, dstWdth, scanSegOut];
FOR x: NAT IN [0..dstWdth) DO          -- clear the pixels
FOR i: NAT IN [0..scanSegOut.length) DO scanSegOut[i][x] ← 0; ENDLOOP;
ENDLOOP;
yPos ← yPos - 2 * srcHght;     -- subtract big increment from position
yCtr ← yCtr + 1; IF yCtr > dstHght THEN EXIT;
};
yPos ← yPos + 2 * dstHght;  -- add in little incrment until big increment exceeded
ENDLOOP;
};
CedarProcess.DoWithPriority[background, Action];    -- be nice to other processess
};
MakeStripes: PUBLIC PROC[context: REF Context, numStripes, min, max, power: REAL] ~ {
factor: REAL ← ( numStripes * 2. * 3.1416 ) / Real.RoundC[context.viewPort.w];
maxValue: REAL ← 255.0;
scanSeg: REF Pixels.SampleSetSequence ← Pixels.GetScanSeg[context.display, 0, 0,
             Real.RoundC[context.viewPort.w], scanSeg];
FOR x: NAT IN [ 0 .. Real.RoundC[context.viewPort.w] ) DO
sineValue: REAL ← RealFns.Sin[ factor * x ];
sineValue ← RealFns.Power[ABS[sineValue], power] * Sgn[sineValue];
scanSeg[0][x] ← Real.RoundC[maxValue * (((sineValue + 1.) / 2.0) * (max - min) + min)];
ENDLOOP;
FOR y: NAT IN [ 0 .. Real.RoundC[context.viewPort.h] ) DO
Pixels.PutScanSeg[context.display, 0, y, Real.RoundC[context.viewPort.w], scanSeg];
ENDLOOP;
};
MakeStretchedSpots: PUBLIC PROC[context: REF Context,
           spotsAcross, min, max, power: REAL,
           stretched: BOOLEANFALSE] ~ {
Action: PROC ~ {
maxValue: REAL ← 255.0;
factor: REAL ← (spotsAcross * 2 * 3.1416) / Real.RoundC[context.viewPort.w];
FOR y: NAT IN [ 0 .. Real.RoundC[context.viewPort.h] ) DO
scanSeg: REF Pixels.SampleSetSequence ← Pixels.GetScanSeg[context.display, 0, y,
             Real.RoundC[context.viewPort.w] ];
spread: REAL
IF stretched THEN
RealFns.Cos[ 3.1416 * (y - Real.RoundC[context.viewPort.h] / 2.0) /
              (Real.RoundC[context.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[context.viewPort.w] ) DO
sineX: REAL ← RealFns.Cos[ spread * factor * (x - Real.RoundC[context.viewPort.w]/2.0) ];
sineX ← RealFns.Power[ABS[sineX], power] * Sgn[sineX];
scanSeg[0][x] ← Real.RoundC[
maxValue * (((sineX * sineY + 1.) / 2.0) * (max - min) + min) ];
ENDLOOP;
Pixels.PutScanSeg[context.display, 0, y, Real.RoundC[context.viewPort.w], scanSeg];
ENDLOOP;
};
CedarProcess.DoWithPriority[background, Action];    -- be nice to other processess
};
DrawQuad: PUBLIC PROC[context: REF 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 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;
};
SetBackgroundColor: PUBLIC PROC [context: REF Context, color: Rope.ROPE] ~ {
bkgrdColor: REF RGBNEW[ RGB
         ImagerColor.RGBFromHSL[ NamedColors.RopeToHSL[color] ] ];
context.props ← Atom.PutPropOnList[context.props, $BackGround, bkgrdColor]; -- set color
};
SetBackgroundImage: PUBLIC PROC [context: REF Context, aisFile: Rope.ROPE] ~ {
bkGrdCtx: REF Context ← ThreeDScenes.Create[];
ThreeDScenes.DisplayFromVM[ bkGrdCtx,
Real.RoundC[context.viewPort.w], Real.RoundC[context.viewPort.h],
context.renderMode
];
IF context.depthBuffer THEN ThreeDScenes.AddDepthBuffer[bkGrdCtx];
IF context.alphaBuffer THEN ThreeDScenes.AddAlphaBuffer[bkGrdCtx];
[] ← AISAnimation.GetAIS[bkGrdCtx, aisFile];
context.props ← Atom.PutPropOnList[context.props, $BackGround,
           NEW[Pixels.PixelBuffer ← bkGrdCtx.display] ];
};
SetBackgroundContext: PUBLIC PROC [context, bkGrdCtx: REF Context ] ~ {
context.props ← Atom.PutPropOnList[context.props, $BackGround,
           NEW[Pixels.PixelBuffer ← bkGrdCtx.display] ];
};
GetBackgroundColor: PUBLIC PROC [context: REF 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 ThreeDBasics.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 ThreeDBasics.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 ThreeDBasics.ShapeInstance ← ThreeDScenes.NewShape[shapeName];
vtcesPerSide: NAT ← squaresPerSide + 1;
shape.numSurfaces ← squaresPerSide * squaresPerSide;
shape.vertex ← NEW[ ThreeDBasics.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[ThreeDBasics.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
IF surface[i] = NIL THEN surface[i] ← NEW[ThreeDSurfaces.PtrPatch];
surface[i].vtxPtr ← NEW[NatSequence[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.] ] ~ {
shape: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.NewShape[shapeName];
cloneShape: REF ThreeDBasics.ShapeInstance ← NIL;
shape.fileName ← PrependWorkingDirectory[context, fileName];
FOR i: NAT IN [0..context.shapes.length) DO   -- same data as another shape?
IF Rope.Equal[shape.fileName, context.shapes[i].fileName]
THEN cloneShape ← context.shapes[i];
ENDLOOP;
IF cloneShape # NIL        -- save data reads if previously read
THEN ThreeDSurfaces.CloneShape[ shape, cloneShape ]
ELSE ThreeDSurfaces.ReadShape[ shape, shape.fileName ];
ThreeDScenes.PlaceShape[shape, position];
context.shapes ← ThreeDScenes.AddShape[context.shapes, shape];
IF Atom.GetPropFromList[context.props, shape.type] = NIL
THEN RegisterSurfaceType[context, shape.type];
};
SetFacetedColor: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE, color: RGB] ~ {
shape: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes,
                    shapeName ];
IF shape # NIL
THEN {
patchInfo: REF ThreeDBasics.ShadingSequence ← NARROW[
ThreeDScenes.GetShading[ shape, $PatchColors ]
];
IF patchInfo = NIL
THEN patchInfo ← NEW[ThreeDBasics.ShadingSequence[shape.numSurfaces] ]
ELSE IF ThreeDScenes.GetShading[ shape, $PatchColorsInFile ] # NIL
THEN SIGNAL ThreeDScenes.Error[[$MisMatch, "Patches individually colored"]];
FOR i: NAT IN [0..shape.numSurfaces) DO
patchInfo[i] ← NEW[ ThreeDBasics.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 ];
ThreeDSurfaces.GetPolyNormals[shape ! ThreeDScenes.Error =>
           IF reason.code = $MisMatch THEN CONTINUE ];
};
};
SetSmoothColor: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE, color: RGB] ~ {
shape: REF ThreeDBasics.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, $VertexColorsInFile ] # NIL
THEN SIGNAL ThreeDScenes.Error[[$MisMatch, "Vertices individually colored"]];
shape.shade ← ThreeDScenes.InitShades[ shape ]; -- color vtces
};
};
SetLinesColor: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE, color: RGB] ~ {
shape: REF ThreeDBasics.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, $VertexColorsInFile ] # 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 ThreeDBasics.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 ThreeDBasics.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 ThreeDBasics.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes,
                    shapeName ];
shadeProc: ScanConvert.GetColorProc ← SolidTextures.RopeToProc[procName];
storeProc: ThreeDBasics.VtxToRealSeqProc ← SolidTextures.GetLerpedVals;
ThreeDScenes.PutShading[ shape, $ShadingProcs,
        NEW
[ ThreeDScenes.ShadingProcs ← [ storeProc, shadeProc ] ] ];
};
GetShadingProcs: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE,
          storeProc: ThreeDBasics.VtxToRealSeqProc,
          shadeProc: ScanConvert.GetColorProc] ~{
shape: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes,
                    shapeName ];
ThreeDScenes.PutShading[ shape, $ShadingProcs,
        NEW
[ ThreeDScenes.ShadingProcs ← [ storeProc, shadeProc ] ] ];
};
Scene Manipulation
CombineBoxes: PUBLIC PROC[context: REF Context] ~ { -- get combined bounding box
context.extentCovered ← [LAST[NAT], 0, LAST[NAT], 0];
FOR i: NAT IN [0..context.shapes.length) DO
IF context.shapes[i] # NIL
 AND
Atom.GetPropFromList[context.shapes[i].props, $Hidden] = NIL
 AND
context.shapes[i].surface # NIL AND context.shapes[i].clipState # out THEN {
IF context.extentCovered.left > context.shapes[i].screenExtent.left
THEN context.extentCovered.left ← context.shapes[i].screenExtent.left;
IF context.extentCovered.right < context.shapes[i].screenExtent.right
THEN context.extentCovered.right ← context.shapes[i].screenExtent.right;
IF context.extentCovered.bottom > context.shapes[i].screenExtent.bottom
THEN context.extentCovered.bottom ← context.shapes[i].screenExtent.bottom;
IF context.extentCovered.top < context.shapes[i].screenExtent.top
THEN context.extentCovered.top ← context.shapes[i].screenExtent.top;
};
ENDLOOP;
};
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.PutF[ output, " -- %g - Created by: %g at %g\n\n",
IO.rope[fileName],
IO.rope[UserCredentials.Get[].name],
IO.time[]
];
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,
streamOptions: [FALSE, TRUE, TRUE, TRUE, TRUE], 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 Context ← ThreeDScenes.Create[];
bufferCtx.display ← context.display;
bufferCtx.renderMode ← context.renderMode;
bufferCtx.alphaBuffer ← context.alphaBuffer;
bufferCtx.props ← Atom.PutPropOnList[bufferCtx.props, $WDir, wDir];
bufferCtx.props ← Atom.PutPropOnList[bufferCtx.props, $Log, log];
time ← CurrentTime[];
RestoreFromFile[bufferCtx, fileName];
IF log # NIL THEN log.PutRope[ Rope.Cat[ "Setup Time: ", ElapsedTime[time], "\n"] ];
MakeFrame[bufferCtx];
bufferCtx ← NIL;
};
Frame Generation and Animation
MakeFrame: PUBLIC PROC[context: REF Context] ~ {
time: REAL ← CurrentTime[];
log: IO.STREAMNARROW[ Atom.GetPropFromList[context.props, $Log] ];
[] ← UpdateDisplay[context];
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[ " Frame Time: ", ElapsedTime[time], "\n\n"] ];
FlushLog[context];
};
};
MakeHiResFrame: PUBLIC PROC[context: REF Context,
          width, height: NAT, name: Rope.ROPE] ~ {
log: IO.STREAMNARROW[ Atom.GetPropFromList[context.props, $Log] ];
hiResCtxt: REF Context ← ThreeDScenes.Create[];
ThreeDScenes.DisplayFromVM[hiResCtxt, width, height];   -- get display memory
IF context.alphaBuffer THEN ThreeDScenes.AddAlphaBuffer[hiResCtxt];
IF context.depthBuffer THEN ThreeDScenes.AddDepthBuffer[hiResCtxt];
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;
MakeFrame[hiResCtxt];
AISAnimation.PutAIS[hiResCtxt, name ];       -- store resulting image
};
MakeInterpressPage: PUBLIC PROC[context: REF Context, fileName: Rope.ROPE] ~ {
ipCtxt: REF Context ← ThreeDScenes.Create[];
ipRef: ImagerInterpress.Ref ← ImagerInterpress.Create[
             PrependWorkingDirectory[context, fileName] ];
scale: REAL ← 1.0 / (72.0 * 39.37);       -- meters per pt.
Action: PROC[imagerCtx: Imager.Context] ~ {
bkgrdClr: REF RGBNARROW[
Atom.GetPropFromList[ipCtxt.props, $BackGround]
! ANY => SIGNAL ThreeDScenes.Error[[$MisMatch,
           "Interpress needs constant background"]]
];
ipCtxt.props ← Atom.PutPropOnList[ipCtxt.props, $ImagerCtx, imagerCtx];
Imager.SetColor[ imagerCtx, ImagerColor.ColorFromRGB[bkgrdClr^] ];
Imager.MaskRectangle[ imagerCtx, ipCtxt.viewPort ];
ThreeDSurfaces.ShowObjects[ ipCtxt ];
};
CopyContextData[ipCtxt, context];
ipCtxt.renderMode ← $Interpress;
ipCtxt.alphaBuffer ← FALSE;
ipCtxt.depthBuffer ← FALSE;
ImagerInterpress.DoPage[ipRef, Action, scale];
ImagerInterpress.Close[ipRef];
};
ShowShapes: PUBLIC PROC[context: REF Context] ~ {
Action: PROC ~ {
ThreeDSurfaces.ShowObjects[
context: context,
frontToBack: context.alphaBuffer
]
};
CedarProcess.DoWithPriority[background, Action];
};
ShowWireFrameShapes: PUBLIC PROC[context: REF Context] ~ {
Action: PROC ~ {
ThreeDSurfaces.ShowWireFrameObjects[ context ];
};
CedarProcess.DoWithPriority[background, Action];
};
OrbitEye: PUBLIC PROC[context: REF Context,
      lookingFrom, lookingAt, axis: Triple, framesPerRev: NAT,
      filename: Rope.ROPENIL, numFrames: NAT ← 32767, startAt: NAT ← 0] ~ {
frameNo: NAT ← startAt;
bufContext, ditherContext: REF Context ← NIL;
GetBufferContext: PROC[context: REF Context] RETURNS[bufContext: REF Context] ~ {
IF context.renderMode # $Bitmap AND context.renderMode # $Dithered
THEN {
bufContext ← ThreeDScenes.Create[];
ThreeDScenes.DisplayFromVM[ bufContext, Real.FixC[context.viewPort.w],
         Real.FixC[context.viewPort.h], context.renderMode ];
IF context.alphaBuffer THEN ThreeDScenes.AddAlphaBuffer[bufContext];
IF context.depthBuffer THEN ThreeDScenes.AddDepthBuffer[bufContext];
CopyContextData[dstCtx: bufContext, srcCtx: context];
context.props ← Atom.PutPropOnList[context.props, $BufferContext, bufContext];
}
ELSE {   -- using Imager, no double buffer
bufContext ← context;
context.props ← Atom.RemPropFromList[context.props, $BufferContext];
};
};
NewFrame: ENTRY PROC[lookingFrom, lookingAt: Triple] ~ {
ENABLE UNWIND => NULL;
changed: BOOLEAN ← UpdateDisplay[context];
IF changed THEN bufContext ← GetBufferContext[context];
ThreeDScenes.SetView[ bufContext, lookingFrom, lookingAt];
MakeFrame[bufContext];
IF bufContext # context THEN {   -- double buffering, make sure display is current
DoTransfer: PROCEDURE [imagerCtx: Imager.Context] ~ {
Pixels.Transfer[context.display, bufContext.display];
};
QuickViewer.DrawInViewer[NARROW[context.viewer], DoTransfer];
};
IF context.stopMe THEN ERROR ABORTED;
CedarProcess.CheckAbort[];   -- check for external abort request before proceeding
IF filename # NIL THEN    -- if storing frames
IF context.renderMode = $Dorado24 OR context.renderMode = $FullColor
THEN {       -- dither to 8 bits per pixel for storage savings
DitherImage[ditherContext, context];
AISAnimation.StoreFiles[ditherContext, filename, frameNo];
}
ELSE AISAnimation.StoreFiles[context, filename, frameNo];
frameNo ← frameNo + 1;
};
IF filename = NIL       -- double buffer if not storing files
THEN bufContext ← GetBufferContext[context]
ELSE {        -- no double buffer if storing files
bufContext ← context;
IF context.renderMode = $Dorado24 OR context.renderMode = $FullColor
THEN {
ditherContext ← ThreeDScenes.Create[];
ThreeDScenes.DisplayFromVM[bufContext,
context.display.width, context.display.height,
$PseudoColor
];
};
};
Animation3D.MoveInOrbit[ lookingFrom, lookingAt, axis, NewFrame,
        framesPerRev, numFrames, startAt ];
};
MakeFramesFromTo: PUBLIC PROC[context: REF 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, startAt ];
};
END.