SceneUtilitiesImpl.mesa
Copyright Ó 1984, 1987 by Xerox Corporation. All rights reserved.
Last Edited by: Crow, April 11, 1988 5:41:15 pm PDT
Bloomenthal, September 28, 1988 3:15:31 pm PDT
DIRECTORY
BasicTime   USING [ GetClockPulses, PulsesToSeconds ],
Atom     USING [ DottedPair, DottedPairNode, GetPropFromList,
         PropList, PutPropOnList, RemPropFromList ],
Real     USING [ Float, Fix, Round ],
Rope     USING [ Cat, Equal, FromChar, Index, ROPE, Substr ],
IO      USING [ STREAM, Close, Flush, GetAtom, GetChar,
         GetLineRope, GetReal, PutF, PutRope, EndOfStream,
         SkipWhitespace, SP, CR, real, rope, time ],
FS      USING [ StreamOpen, ComponentPositions, ExpandName ],
Convert    USING [ RopeFromAtom, RopeFromReal ],
UserCredentials  USING [ Get ],
ViewerIO    USING [ CreateViewerStreams ],
ViewerClasses  USING [ ViewerRec ],
Imager    USING [ Rectangle ],
ImagerColorFns  USING [ RGBFromHSL ],
NamedColors   USING [ RopeToHSL ],
G3dMatrix   USING [ Transform, TransformVec ],
G3dVector   USING [ Add ],
G3dIO     USING [ FieldRep, FieldSequenceRep, FieldType,
         Shape, ShapeFromFile, WriteFields ],
ThreeDBasics  USING [ AllOut, ClipState, Context, ContextClass, Create, Error,
         IntegerSequence, LoadDisplayType,
         LoadShadingClass, LoadSurfaceType, NatSequence, NatTable,
         NoneOut, OutCode, Pair, PairSequence, PtrPatch,
         PtrPatchSequence, Quad, RealSequence, RGB,
         RGBSequence, ScaleAndAddXfm, SetPosition, SetView,
         ShadingClass, ShadingSequence, ShadingValue, ShapeInstance,
         ShapeProc, ShapeSequence, SixSides, TextureMap,
         TextureFunction, Triple, TripleSequence, Vertex, VertexInfo,
         VertexInfoSequence, VertexSequence, VtxToRealSeqProc, Xfm3D,
         Xfm3DRep ],
SceneUtilities  USING [ ];
SceneUtilitiesImpl: CEDAR PROGRAM
IMPORTS Atom, BasicTime, Convert, FS, G3dIO, G3dMatrix, G3dVector, ImagerColorFns, IO, NamedColors, Real, Rope, ThreeDBasics, UserCredentials, ViewerIO
EXPORTS SceneUtilities
~ BEGIN
Basic Types
RGB: TYPE ~ ThreeDBasics.RGB;
RGBSequence: TYPE ~ ThreeDBasics.RGBSequence;
Pair: TYPE ~ ThreeDBasics.Pair;          -- RECORD [ x, y: REAL];
PairSequence: TYPE ~ ThreeDBasics.PairSequence;
Triple: TYPE ~ ThreeDBasics.Triple;        -- RECORD [ x, y, z: REAL];
TripleSequence: TYPE ~ ThreeDBasics.TripleSequence;
Quad: TYPE ~ ThreeDBasics.Quad;        -- RECORD [ x, y, z, w: REAL];
NatSequence: TYPE ~ ThreeDBasics.NatSequence;
NatTable: TYPE ~ ThreeDBasics.NatTable;
IntegerSequence: TYPE ~ ThreeDBasics.IntegerSequence;
RealSequence: TYPE ~ ThreeDBasics.RealSequence;
Xfm3D: TYPE ~ ThreeDBasics.Xfm3D; -- REF ARRAY [0..4) OF ARRAY [0..4) OF REAL;
Xfm3DRep: TYPE ~ ThreeDBasics.Xfm3DRep;
ScaleAndAddXfm: TYPE ~ ThreeDBasics.ScaleAndAddXfm;
RECORD[scaleX, scaleY, scaleZ, addX, addY, addZ: REAL];
SixSides: TYPE ~ ThreeDBasics.SixSides;  -- {Left, Right, Bottom, Top, Near, Far}
ClipState: TYPE ~ ThreeDBasics.ClipState;   -- {in, out, clipped}
OutCode: TYPE ~ ThreeDBasics.OutCode; -- RECORD[bottom,top,left,right,near,far: BOOLEAN]
NoneOut: OutCode ~ ThreeDBasics.NoneOut; -- [FALSE,FALSE,FALSE,FALSE,FALSE,FALSE]
AllOut: OutCode ~ ThreeDBasics.AllOut; -- [TRUE, TRUE, TRUE, TRUE, TRUE, TRUE]
Context: TYPE ~ ThreeDBasics.Context;
ContextClass: TYPE ~ ThreeDBasics.ContextClass;
ShadingClass: TYPE ~ ThreeDBasics.ShadingClass;
ShapeInstance: TYPE ~ ThreeDBasics.ShapeInstance;
ShapeSequence: TYPE ~ ThreeDBasics.ShapeSequence;
ShapeProc: TYPE ~ ThreeDBasics.ShapeProc;
ShadingSequence: TYPE ~ ThreeDBasics.ShadingSequence;
ShadingValue: TYPE ~ ThreeDBasics.ShadingValue;
Vertex: TYPE ~ ThreeDBasics.Vertex;
VertexSequence: TYPE ~ ThreeDBasics.VertexSequence;
VertexInfo: TYPE ~ ThreeDBasics.VertexInfo;
VertexInfoSequence: TYPE ~ ThreeDBasics.VertexInfoSequence;
PtrPatch: TYPE ~ ThreeDBasics.PtrPatch;
PtrPatchSequence: TYPE ~ ThreeDBasics.PtrPatchSequence;
TextureMap: TYPE ~ ThreeDBasics.TextureMap;
TextureFunction: TYPE ~ ThreeDBasics.TextureFunction;
Field: TYPE ~ G3dIO.FieldRep;
FieldSequence: TYPE ~ G3dIO.FieldSequenceRep;
ROPE: TYPE = Rope.ROPE;
LORA: TYPE = LIST OF REF ANY;
Renamed Procedures
Transform: PROC[p: Triple, mat: Xfm3D] RETURNS[Triple] ~ G3dMatrix.Transform;
TransformVec: PROC[vec: Triple, mat: Xfm3D] RETURNS[Triple] ~
                  G3dMatrix.TransformVec;
GetProp: PROC [propList: Atom.PropList, prop: REF ANY] RETURNS [REF ANY] ~
                     Atom.GetPropFromList;
PutProp: PROC [propList: Atom.PropList, prop: REF ANY, val: REF ANY]
   RETURNS
[Atom.PropList] ~ Atom.PutPropOnList;
Utility Procedures
Sqr: PROCEDURE [number: REAL] RETURNS [REAL] ~ INLINE { RETURN[number * number]; };
Ceiling: PROC[number: REAL] RETURNS[result: INTEGER] ~ {
result ← INTEGER[Real.Round[number]];
IF result < number THEN result ← result + 1;
};
Floor: PROC[ in: REAL ] RETURNS[ out: INTEGER ] ~ {
out ← INTEGER[Real.Round[in]];
IF Real.Float[out] > in THEN out ← out - 1;
};
ElapsedTime: PROC[startTime: REAL] RETURNS[ROPE] ~ {
timeX100: REAL ← 100.0 * (CurrentTime[] - startTime);
RETURN[ Rope.Cat[ Convert.RopeFromReal[ Real.Fix[timeX100] / 100.0 ], "s," ] ];
};
CurrentTime: PROC[] RETURNS[REAL] ~ {
RETURN[ BasicTime.PulsesToSeconds[BasicTime.GetClockPulses[]] ];
};
DiffPosns: PROC[vtx1, vtx2: REF Vertex] RETURNS[Triple] ~ {
RETURN[[vtx1.x - vtx2.x, vtx1.y - vtx2.y, vtx1.z - vtx2.z]]
};
GetRope: PUBLIC PROC[input: IO.STREAM] RETURNS[ROPE] ~ {
Gets set of characters bound by white space
output: ROPENIL;
char: CHAR;
[] ← IO.SkipWhitespace[input];       -- Strip whitespace and comments
char ← IO.GetChar[input];
WHILE char # IO.SP AND char # IO.CR DO     -- do until trailing space or CR
output ← Rope.Cat[ output, Rope.FromChar[char] ];
char ← IO.GetChar[input];
ENDLOOP;
RETURN[output];
};
Procedures for Setting up and using Contexts
CreateDefaultContext: PUBLIC PROC[] RETURNS [REF Context] ~ {
sets up context with blue background, white light source over left shoulder, view from 12 units or so, rendering through fancy renderer
context: REF Context ← ThreeDBasics.Create[];
context.preferredRenderMode ← $Pixels;
NameBackgroundColor[ context, "Darkish Blue" ]; -- set background color
SetLight[context, "Default", [-100., -200., 50.] ];
ThreeDBasics.SetView[ context, [2.0, -10.0, 3.0], [0.0, 0.0, 0.0] ];
RETURN[context];
};
PrependWorkingDirectory: PUBLIC PROC[context: REF Context, file: ROPE]          RETURNS[ROPE] ~ {
wDir: ROPENARROW[ GetProp[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 ];
};
TackOnExtension: PUBLIC PROC[file, extension: ROPE] RETURNS[ROPE] ~ {
cp: FS.ComponentPositions; fullFName, fName: ROPE;
[fullFName, cp, ] ← FS.ExpandName[file];
IF cp.ext.length = 0
THEN {
fName ← Rope.Substr[ fullFName, 0, cp.ext.start];
RETURN[ Rope.Cat[fName, ".", extension] ];
}
ELSE RETURN[ file ];
};
GetTmpContext: PUBLIC PROC [srcCtx: REF Context] RETURNS[dstCtx: REF Context] ~ {
copies context so temporary modifications can be made
dstCtx ← ThreeDBasics.Create[];
CopyContextData[dstCtx, srcCtx];
dstCtx.pixels ← srcCtx.pixels;
};
CopyContextData: PUBLIC PROC [dstCtx, srcCtx: REF Context] ~ {
dstCtx.class ← IF srcCtx.class # NIL THEN NEW[ ContextClass ← srcCtx.class^ ] ELSE NIL;
dstCtx.stopMe ← srcCtx.stopMe;   -- inheriting REF so Stop signals will propagate
dstCtx.frameNumber ← srcCtx.frameNumber;
dstCtx.shapes ← srcCtx.shapes;
dstCtx.visibleShapes ← srcCtx.visibleShapes;
dstCtx.lightSources ← srcCtx.lightSources;
dstCtx.environment ← srcCtx.environment;
dstCtx.viewInValid ← srcCtx.viewInValid;
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;
dstCtx.ndcToPixels ← srcCtx.ndcToPixels;
IF srcCtx.viewer # NIL THEN {
dstCtx.viewer ← NEW[ViewerClasses.ViewerRec ← srcCtx.viewer^];
FOR list: Atom.PropList ← srcCtx.viewer.props, list.rest UNTIL list = NIL DO-- new proplist
element: Atom.DottedPair ← NEW[Atom.DottedPairNode ← list.first^];
dstCtx.viewer.props ← CONS[element, dstCtx.viewer.props];
ENDLOOP;
dstCtx.viewer.props ← PutProp[ dstCtx.viewer.props, $Context3D, dstCtx ];
};
dstCtx.terminal ← srcCtx.terminal;
dstCtx.displayInValid ← srcCtx.displayInValid;
Don't copy pixels, some uses want their own
dstCtx.viewPort ← IF srcCtx.viewPort # NIL THEN NEW[ Imager.Rectangle ← srcCtx.viewPort^]
             ELSE NIL;
dstCtx.preferredViewPort ← srcCtx.preferredViewPort;
dstCtx.extentCovered ← srcCtx.extentCovered;
dstCtx.preferredRenderMode ← srcCtx.preferredRenderMode;
dstCtx.displayProps ← NIL;
FOR list: Atom.PropList ← srcCtx.displayProps, list.rest UNTIL list = NIL DO-- new proplist
element: Atom.DottedPair ← NEW[Atom.DottedPairNode ← list.first^];
dstCtx.displayProps ← CONS[element, dstCtx.displayProps];
ENDLOOP;
dstCtx.autoRedraw ← srcCtx.autoRedraw;
dstCtx.delayClear ← srcCtx.delayClear;
dstCtx.doVisibly ← srcCtx.doVisibly;
dstCtx.antiAliasing ← srcCtx.antiAliasing;
dstCtx.depthBuffering ← srcCtx.depthBuffering;
dstCtx.depthResolution ← srcCtx.depthResolution;
dstCtx.sortSequence ← srcCtx.sortSequence;
dstCtx.props ← NIL;
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;
};
CopyContextShapes: PUBLIC PROC [dstCtx, srcCtx: REF Context] ~ {
dstCtx.shapes ← NEW[ ShapeSequence[srcCtx.shapes.length] ];
FOR i: NAT IN [0..srcCtx.shapes.length) DO
dstCtx.shapes[i] ← NEW[ ShapeInstance ← srcCtx.shapes[i]^ ];
dstCtx.shapes[i].shadingClass ← NEW[ ShadingClass ← srcCtx.shapes[i].shadingClass^ ];
ENDLOOP;
dstCtx.shapes.length ← srcCtx.shapes.length;
};
StartLog: PUBLIC PROC [context: REF Context] RETURNS[IO.STREAM] ~ {
log: IO.STREAMNARROW[GetProp[context.props, $Log]];
IF log = NIL THEN {
[out: log] ← ViewerIO.CreateViewerStreams[
name: "ThreeDWorld.log",
backingFile: PrependWorkingDirectory[context, "ThreeDWorld.log"]
];
context.props ← PutProp[context.props, $Log, log];
};
RETURN[ log ];
};
FlushLog: PUBLIC PROC [context: REF Context] ~ {
log: IO.STREAMNARROW[GetProp[context.props, $Log]];
IF log # NIL THEN IO.Flush[log];
};
CloseLog: PUBLIC PROC [context: REF Context] ~ {
log: IO.STREAMNARROW[GetProp[context.props, $Log]];
IF log # NIL THEN IO.Close[log];
context.props ← Atom.RemPropFromList[context.props, $Log]
};
Procedures for Defining and Altering Environments
ForcePrioritySort: PUBLIC PROC[context: REF Context, on: BOOLEANTRUE] ~{
IF on
THEN context.props ← PutProp[context.props, $SortToPriority, $DoIt]
ELSE context.props ← Atom.RemPropFromList[context.props, $SortToPriority];
};
SetWindow: PUBLIC PROC[context: REF Context, size: Imager.Rectangle] ~{
context.window ← NEW[ Imager.Rectangle ← size ];
context.viewInValid ← TRUE;
};
SetViewPort: PUBLIC PROC[context: REF Context, size: Imager.Rectangle] ~{
IF size.w <= 0.0 OR size.h <= 0.0
THEN SIGNAL ThreeDBasics.Error[[$MisMatch, "Null clipper"]];
context.preferredViewPort ← size;
context.viewPort ← NIL;
Computed by SurfaceRender.ValidateView or ThreeDViewer.GetViewportFromViewer
context.window ← NIL;      -- resizing viewport forces update of window
context.displayInValid ← TRUE;
};
View and Lighting control
SetAmbientLight: PUBLIC PROC [context: REF Context, color: ROPE] ~ {
ambientColor: REF RGBNEW[
RGB ← ImagerColorFns.RGBFromHSL[ NamedColors.RopeToHSL[color] ]
];
context.environment ← PutProp[
context.environment, $AmbientLight, ambientColor
];
IF context.shapes # NIL THEN FOR i: NAT IN [0..context.shapes.length) DO
context.shapes[i].shadingInValid ← TRUE;
ENDLOOP;
};
SetBackgroundColor: PUBLIC PROC [context: REF Context, color: RGB] ~ {
bkgrdColor: REF RGBNEW[ RGB ← color ];
context.props ← PutProp[context.props, $BackGround, bkgrdColor]; -- set color
};
GetBackgroundColor: PUBLIC PROC [context: REF Context] RETURNS [color: RGB] ~ {
Get background color
ref: REF ← GetProp[context.props, $BackGround]; -- get background color
IF ref # NIL THEN color ← NARROW[ref, REF RGB]^ ELSE color ← [0., 0., 0.];
};
NameBackgroundColor: PUBLIC PROC [context: REF Context, color: ROPE] ~ {
bkgrdColor: REF RGBNEW[
RGB ← ImagerColorFns.RGBFromHSL[ NamedColors.RopeToHSL[color] ]
];
context.props ← PutProp[context.props, $BackGround, bkgrdColor]; -- set color
};
SetBackgroundImage: PUBLIC PROC [context: REF Context, aisFile: ROPE] ~ {
bkGrdCtx: REF Context ← ThreeDBasics.Create[];
bkGrdCtx.depthBuffering ← context.depthBuffering;
bkGrdCtx.antiAliasing ← context.antiAliasing;
bkGrdCtx.class ← context.class;
bkGrdCtx.props ← PutProp[bkGrdCtx.props, $BackGrdImage, aisFile];
SetBackgroundContext[context, bkGrdCtx];
};
SetBackgroundContext: PUBLIC PROC [context, bkGrdCtx: REF Context ] ~ {
context.props ← PutProp[context.props, $BackGround, bkGrdCtx];
};
SetLight: PUBLIC PROC[context: REF Context, name: ROPE, position: Triple,
       color: RGB ← [1, 1, 1] ] ~ {
light: REF ShapeInstance ← FindShape[ context, name
! ThreeDBasics.Error => IF reason.code = $MisMatch THEN RESUME
];
IF light = NIL THEN {
light ← NewShape[ name, $Light ];  -- name not used before, load light class
AddShape[ context, light ];
};
light.location ← position;
light.boundingRadius ← 2 * 93000000.0 * 1609.344; -- twice solar distance in meters
light.orientation ← [0.,0.,0.];       -- no orientation by default
light.positionInValid ← TRUE;
light.vtcesInValid ← TRUE;
light.shadingClass.color ← color;
light.props ← PutProp[light.props, $Hidden, $ok]; -- hide from display routines
FOR i: NAT IN [0..context.shapes.length) DO
context.shapes[i].shadingInValid ← TRUE;
ENDLOOP;
};
DeleteLight: PUBLIC PROC[context: REF Context, name: ROPE] ~ {
DeleteShape[context, name];
IF context.shapes # NIL THEN FOR i: NAT IN [0..context.shapes.length) DO
context.shapes[i].shadingInValid ← TRUE;
ENDLOOP;
};
Scene Input/Output
SaveOnFile: PUBLIC PROC[context: REF Context, fileName: ROPE] ~ {
wDir: 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[]
];
WriteScene[context, output];
IO.Close[output];
};
RestoreFromFile: PUBLIC PROC[context: REF Context, fileName: ROPE] ~ {
wDir: ROPE ← PrependWorkingDirectory[context, NIL];
input: IO.STREAMFS.StreamOpen[fileName: fileName, accessOptions: $read, wDir: wDir];
[] ← IO.GetLineRope[ input ];   -- ignore first line
ReadScene[context, input];
IO.Close[input];
};
MakeFrameFromFile: PUBLIC PROC[context: REF Context, fileName: ROPE] ~ {
wDir: ROPE ← NARROW[ GetProp[context.props, $WDir] ];
log: IO.STREAMNARROW[ GetProp[context.props, $Log] ];
bufferCtx: REF Context ← ThreeDBasics.Create[];
bufferCtx.pixels ← context.pixels;
bufferCtx.viewer ← context.viewer;
bufferCtx.terminal ← context.terminal;
bufferCtx.class ← context.class;
bufferCtx.antiAliasing ← context.antiAliasing;
bufferCtx.props ← PutProp[bufferCtx.props, $WDir, wDir];
bufferCtx.props ← PutProp[bufferCtx.props, $Log, log];
RestoreFromFile[bufferCtx, fileName];
bufferCtx.class.render[bufferCtx];
bufferCtx ← NIL;
};
ReadScene: PUBLIC PROC[context: REF Context, input: IO.STREAM] ~ {
shape: REF ShapeInstance ← NIL;
done: BOOLEANFALSE;
log: IO.STREAMNARROW[GetProp[context.props, $Log]];
startTime: REAL ← CurrentTime[];
WHILE NOT done DO
keyWd: ROPE ← GetRope[ input ! IO.EndOfStream => EXIT ];
SELECT TRUE FROM
View, Environment, and Display
Rope.Equal[ "View:", keyWd, FALSE] => {
[] ← GetRope[input];
context.eyePoint ← [ IO.GetReal[input], IO.GetReal[input], IO.GetReal[input] ];
[] ← GetRope[input];
context.ptOfInterest ← [ IO.GetReal[input], IO.GetReal[input], IO.GetReal[input] ];
[] ← GetRope[input];
context.upDirection ← [ IO.GetReal[input], IO.GetReal[input], IO.GetReal[input] ];
[] ← GetRope[input];
context.rollAngle ← IO.GetReal[input];
[] ← GetRope[input];
context.fieldOfView ← IO.GetReal[input];
[] ← GetRope[input];
context.hitherLimit ← IO.GetReal[input];
context.yonLimit ← IO.GetReal[input];
context.viewInValid ← TRUE;
};
Rope.Equal[ "ViewPort:", keyWd, FALSE] => {
SetViewPort[ context, [ x: IO.GetReal[input], y: IO.GetReal[input],
        w: IO.GetReal[input], h: IO.GetReal[input] ]
   ];
};
Rope.Equal[ "Window:", keyWd, FALSE] => { -- window must be set after viewport
SetWindow[ context, [ x: IO.GetReal[input], y: IO.GetReal[input],
       w: IO.GetReal[input], h: IO.GetReal[input] ]
   ];
};
Rope.Equal[ "BackgroundColor:", keyWd, FALSE] => {
bkgrdColor: REF RGBNEW[RGB];
bkgrdColor^ ← [ IO.GetReal[input], IO.GetReal[input], IO.GetReal[input] ];
context.props ← PutProp[context.props, $BackGround, bkgrdColor];
};
Rope.Equal[ "AntiAliasing:", keyWd, FALSE] => context.antiAliasing ← TRUE;
Rope.Equal[ "DepthBuffering:", keyWd, FALSE] => context.depthBuffering ← TRUE;
Rope.Equal[ "DisplayType:", keyWd, FALSE] =>
ThreeDBasics.LoadDisplayType[context, IO.GetAtom[input] ];
Rope.Equal[ "Light:", keyWd, FALSE] => {
name: ROPE ← GetRope[input];
pos: ROPE ← GetRope[input];
position: Triple ← [ IO.GetReal[input], IO.GetReal[input], IO.GetReal[input] ];
clr: ROPE ← GetRope[input];
color: RGB ← [ IO.GetReal[input], IO.GetReal[input], IO.GetReal[input] ];
[] ← SetLight[ context, name, position, color ];
};
Shape Manipulation
Rope.Equal[ "ReadShape:", keyWd, FALSE] => {  -- read in new shape data.
name: ROPE ← GetRope[input];
fileName: ROPE ← GetRope[input];
type: ATOMIO.GetAtom[ input ];
insideVisible: BOOLEAN ← Rope.Equal[GetRope[input], "Open", FALSE];
AddShapeAt[context, name, fileName, [0.,0.,0.]];
shape ← FindShape[context, name];
};
Rope.Equal[ "SetShape:",  keyWd, FALSE] => { -- set shape for subsequent mods.
name: ROPE ← GetRope[input];
shape ← FindShape[context, name];
};
Rope.Equal[ "PlaceShape:", keyWd, FALSE] => {
position: Triple ← [ IO.GetReal[input], IO.GetReal[input], IO.GetReal[input] ];
PlaceShape[ context, shape.name, position ];
};
Rope.Equal[ "MoveShape:", keyWd, FALSE] => {
delta: Triple ← [ IO.GetReal[input], IO.GetReal[input], IO.GetReal[input] ];
PlaceShape[ context, shape.name, delta ];
};
Rope.Equal[ "OrientShape:", keyWd, FALSE] => {
axis: Triple ← [ IO.GetReal[input], IO.GetReal[input], IO.GetReal[input] ];
OrientShape[ context, shape.name, axis ];
};
Rope.Equal[ "RotateShape:", keyWd, FALSE] => {
axisBase: Triple ← [ IO.GetReal[input], IO.GetReal[input], IO.GetReal[input] ];
axisEnd: Triple ← [ IO.GetReal[input], IO.GetReal[input], IO.GetReal[input] ];
theta: REALIO.GetReal[input];
RotateShape[ context, shape.name, axisBase, axisEnd, theta ];
};
Rope.Equal[ "Hide:", keyWd, FALSE] => Hide[context, GetRope[input]];
Rope.Equal[ "Reveal:", keyWd, FALSE] => Reveal[context, GetRope[input]];
Shape Shading
Rope.Equal[ "Color:", keyWd, FALSE] => {
color: RGB;
color.R ← IO.GetReal[input]; color.G ← IO.GetReal[input]; color.B ← IO.GetReal[input];
SetColor[context, shape.name, color];
};
Rope.Equal[ "Shading:", keyWd, FALSE] => {
SELECT IO.GetAtom[input] FROM
$Faceted => SetFaceted[context, shape.name];
$Smooth => SetSmooth[context, shape.name];
$Lines => SetLines[context, shape.name];
ENDCASE => SIGNAL ThreeDBasics.Error[[$MisMatch, "Unknown shading type"]];
};
Rope.Equal[ "Shininess:", keyWd, FALSE] => {
shininess: REALIO.GetReal[input];
SetShiny[context, shape.name, shininess];
};
Rope.Equal[ "Transmittance:", keyWd, FALSE] => {
transmittance: REALIO.GetReal[input];
SetTransparent[context, shape.name, transmittance];
};
Rope.Equal[ "TextureMap:", keyWd, FALSE] => {
proc: REF ShapeProc ← NARROW[
GetProp[context.props, $TextureMapFromStream]
];
IF proc # NIL
THEN [] ← proc^[context, shape, input]
ELSE SIGNAL ThreeDBasics.Error[[$Unloaded, "Texture procs not loaded"]];
};
Rope.Equal[ "TextureFunction:", keyWd, FALSE] => {
proc: REF ShapeProc ← NARROW[
GetProp[context.props, $TextureFunctionFromStream]
];
IF proc # NIL
THEN [] ← proc^[context, shape, input]
ELSE SIGNAL ThreeDBasics.Error[[$Unloaded, "Texture procs not loaded"]];
};
Control
Rope.Equal[ "Render:", keyWd, FALSE] => {
IF log # NIL AND (CurrentTime[] - startTime > .01)
THEN log.PutRope[ Rope.Cat[" Readin: ", ElapsedTime[startTime]] ];
context.class.render[context];
startTime ← CurrentTime[];   -- restart for modifications until next frame
};
Rope.Equal[ "EndOfScene:", keyWd, FALSE] => {
IF log # NIL AND (CurrentTime[] - startTime > .01)
THEN log.PutRope[ Rope.Cat[" Scene input: ", ElapsedTime[startTime]] ];
done ← TRUE;
};
ENDCASE => SIGNAL ThreeDBasics.Error[
[$MisMatch, Rope.Cat[keyWd, " - not understood"]]
];
ENDLOOP;
};
WriteScene: PUBLIC PROC[context: REF Context, output: IO.STREAM] ~ {
CatList: PROC[list: LIST OF ROPE] RETURNS[ROPE] ~ {
rope: ROPE ← NIL;
WHILE list # NIL DO rope ← Rope.Cat[ rope, list.first ]; list ← list.rest; ENDLOOP;
RETURN[ rope ];
};
Vec3toRope: PROC[ r1, r2, r3: REAL] RETURNS[ROPE] ~ {
rope: ROPE;
rope ← Rope.Cat[ " ", Convert.RopeFromReal[r1], " ", Convert.RopeFromReal[r2] ];
rope ← Rope.Cat[ rope, " ", Convert.RopeFromReal[r3], " " ];
RETURN[ rope ];
};
Vec4toRope: PROC[ r1, r2, r3, r4: REAL] RETURNS[ROPE] ~ {
rope: ROPE;
rope ← Rope.Cat[ " ", Convert.RopeFromReal[r1], " ", Convert.RopeFromReal[r2], " " ];
rope ← Rope.Cat[ rope, Convert.RopeFromReal[r3], " ", Convert.RopeFromReal[r4], " " ];
RETURN[ rope ];
};
ref: REF;
line: ROPE;
IO.PutRope[ output, Rope.Cat[
"DisplayType: ", Convert.RopeFromAtom[context.class.displayType ], "\n"
] ];
IF context.antiAliasing THEN IO.PutRope[ output, "AntiAliasing: \n" ];
IF context.depthBuffering THEN IO.PutRope[ output, "DepthBuffering: \n" ];
line ← CatList[ LIST[ "View: ",
"from:", Vec3toRope[context.eyePoint.x, context.eyePoint.y, context.eyePoint.z],
"at:", Vec3toRope[context.ptOfInterest.x, context.ptOfInterest.y, context.ptOfInterest.z],
"up:", Vec3toRope[context.upDirection.x, context.upDirection.y, context.upDirection.z],
"roll: ", Convert.RopeFromReal[ context.rollAngle ],
" fov: ", Convert.RopeFromReal[ context.fieldOfView ],
" hithr/yon: ", Convert.RopeFromReal[ context.hitherLimit],
" ", Convert.RopeFromReal[context.yonLimit],
"\n"
] ];
IO.PutRope[ output, line ];
IF context.viewPort # NIL THEN IO.PutRope[ output, Rope.Cat["ViewPort: ",
Vec4toRope[context.viewPort.x, context.viewPort.y, context.viewPort.w, context.viewPort.h],
"\n"
] ];
IF context.window # NIL THEN IO.PutRope[ output, Rope.Cat["Window: ",
Vec4toRope[context.window.x, context.window.y, context.window.w, context.window.h],
"\n"
] ];
ref ← GetProp[context.props, $BackGround]; -- get background color
IF ref # NIL THEN {
color: RGBNARROW[ref, REF RGB]^;
IO.PutRope[ output,
    Rope.Cat[ "BackgroundColor: ", Vec3toRope[color.R, color.G, color.B], "\n" ]
   ];
};
Shapes and Light Sources
IF context.shapes # NIL THEN FOR i: NAT IN [0..context.shapes.length) DO
IF context.shapes[i].class.type = $Light
THEN {             -- light source
color: RGB ← context.shapes[i].shadingClass.color;
IO.PutRope[ output, CatList[ LIST[ "Light: ", context.shapes[i].name,
" position:",
Vec3toRope[
context.shapes[i].location.x,
context.shapes[i].location.y,
context.shapes[i].location.z
],
"color:", Vec3toRope[color.R, color.G, color.B],
"\n"
] ] ];
}
ELSE {             -- shape
shape: REF ShapeInstance ← context.shapes[i];
IF shape # NIL AND GetProp[shape.props, $Hidden] = NIL
 AND
shape.clipState # out AND shape.surface # NIL THEN {
ref: REF ANYNIL;
xfm: Xfm3D ← shape.position;
File Name, instance name, and Surface Type
line ← Rope.Cat[ "ReadShape: ", shape.name, " ", shape.fileName, " " ];
line ← Rope.Cat[ line, Convert.RopeFromAtom[shape.class.type] ];
IF shape.insideVisible
THEN line ← Rope.Cat[line, " Open \n"]
ELSE line ← Rope.Cat[line, " Closed \n"];
IO.PutRope[ output, line];
Scaling
IF GetProp[shape.fixedProps, $Scale] # NIL THEN {
scale: REF Quad ← NARROW[GetProp[shape.fixedProps, $Scale]];
line ← Rope.Cat[" ScaleShape: ",
     Vec4toRope[scale.x, scale.y, scale.z, scale.w],
     "\n" ];
IO.PutRope[ output, line];
};
Orientation and Position
{
IF shape.location # [0.,0.,0.] THEN IO.PutRope[ output, Rope.Cat[
" PlaceShape:",
Vec3toRope[shape.location.x, shape.location.y, shape.location.z], "\n"
] ];
IF shape.orientation # [0.,0.,1.] THEN IO.PutRope[ output, Rope.Cat[
" OrientShape:",
Vec3toRope[shape.orientation.x, shape.orientation.y, shape.orientation.z], "\n"
] ];
IF shape.rotation # 0.0 THEN IO.PutRope[ output, Rope.Cat[
" RotateShape:",
Vec3toRope[shape.axisBase.x, shape.axisBase.y, shape.axisBase.z],
Vec3toRope[shape.axisEnd.x, shape.axisEnd.y, shape.axisEnd.z],
Convert.RopeFromReal[shape.rotation], "\n"
] ];
};
Color, Transmittance, Shininess
{
color: RGB ← shape.shadingClass.color;
shadingType: ATOM ← shape.shadingClass.shadingType;
IO.PutRope[output, Rope.Cat[
" Color: ", Vec3toRope[color.R, color.G, color.B], "\n"
] ];
IO.PutRope[output, Rope.Cat[
" Shading: ", Convert.RopeFromAtom[shape.shadingClass.shadingType],
"\n"
] ];
IF shape.shadingClass.shininess > 0.0
THEN IO.PutRope[ output, Rope.Cat[
" Shininess: ",
Convert.RopeFromReal[shape.shadingClass.shininess], "\n"
] ];
IF shape.shadingClass.transmittance # 0.0
THEN IO.PutRope[ output, Rope.Cat[
" Transmittance: ",
Convert.RopeFromReal[shape.shadingClass.transmittance], "\n"
] ];
};
Texture
IF shape.shadingClass.texture # NIL THEN {
tmpTxtr: LORANIL;
FOR txtrList: LORA ← shape.shadingClass.texture, txtrList.rest UNTIL txtrList = NIL DO
tmpTxtr ← CONS[txtrList.first, tmpTxtr];  -- reverse list to copy
ENDLOOP;
FOR txtrList: LORA ← tmpTxtr, txtrList.rest UNTIL txtrList = NIL DO
WITH txtrList.first SELECT FROM
texture: REF TextureMap => {    -- mapped texture
fileName: ROPE ← NARROW[
GetProp[texture.props, $FileName] ];
coordType: ATOMNARROW[
GetProp[texture.props, $CoordType] ];
coords: REF ← GetProp[texture.props, $Coords];
IF coordType = NIL THEN coordType ← $NoCoords;
line ← Rope.Cat[
" TextureMap: ", fileName, " ",
Convert.RopeFromAtom[texture.type]
];
line ← Rope.Cat[line, " ", Convert.RopeFromAtom[coordType], " " ];
SELECT coordType FROM
$FromVtxNos, $FromNormals => {
argList: LIST OF REALNARROW[coords];
WHILE argList # NIL DO
line ← Rope.Cat[line, Convert.RopeFromReal[argList.first], " " ];
argList ← argList.rest;
ENDLOOP;
};
ENDCASE => {
SIGNAL ThreeDBasics.Error[
[$Mismatch, "Unknown texture coordType"]
];
line ← NIL;      -- kill line to drop texture
};
IO.PutRope[ output, Rope.Cat[line, "\n"] ];
IF GetProp[shape.fixedProps, $TextureScale] # NIL
THEN {
textureScale: REF Triple ← NARROW[
GetProp[shape.fixedProps, $TextureScale]
];
IO.PutRope[ output, Rope.Cat[
" Scale: ",
Vec3toRope[textureScale.x, textureScale.y, textureScale.z],
"\n"
] ];
}
ELSE IO.PutRope[ output, Rope.Cat[
" Scale: 1.0 1.0 1.0 \n"
] ];
};
txtrFn: REF TextureFunction => {       -- solid texture
IO.PutRope[ output, Rope.Cat[
" TextureFunction: ", Convert.RopeFromAtom[txtrFn.name], "\n"
] ];
};
ENDCASE => SIGNAL ThreeDBasics.Error[
[$Mismatch, "Unknown texture type"]
];
ENDLOOP;
};
};
};
ENDLOOP;           -- end loop for all shapes
IO.PutRope[ output, "EndOfScene:\n"];   -- this causes stream closure when read
};
Procedures for Reading and Writing Shape Descriptions
SetTexture: PUBLIC PROC [shape: REF ShapeInstance, textures: REF PairSequence] ~ {
IF shape.shadingClass.loadShapeAux = NIL   -- get default if not previously specified
THEN ThreeDBasics.LoadShadingClass[shape, $MappedAndSolidTexture];
LoadShadingClass zeros the shadingProps since they're considered ephemeral (permanent stuff goes in fixedProps), so that call must precede this:
[] ← shape.shadingClass.loadShapeAux[ NIL, shape, textures ]; -- now load aux array
This tells ThreeDWorld eventually to fill in aux field of VertexInfo from textures.
This magic happens in ShapeUtilities.ShapePatchToPatch.
};
SetVertexProps: PUBLIC PROC [
shape: REF ShapeInstance,
normals, colors, textures, transmittance: BOOL]
~ {
IF normals THEN shape.fixedProps ←
Atom.PutPropOnList[shape.fixedProps, $VertexNormalsInFile, $ok];
IF colors THEN shape.fixedProps ←
Atom.PutPropOnList[shape.fixedProps, $VertexColorsInFile, $ok];
IF textures THEN shape.fixedProps ←
Atom.PutPropOnList[shape.fixedProps, $VertexTextureInFile, $ok];
IF transmittance THEN shape.fixedProps ←
Atom.PutPropOnList[shape.fixedProps, $VertexTransmittanceInFile, $ok];
};
ReadShape: PUBLIC PROC[shape: REF ShapeInstance, fileName: ROPE] ~ {
Changed to use G3dIO.ShapeFromFile; old code is at bottom of file (J Bloomenthal, 9/6/88).
s: G3dIO.Shape ← G3dIO.ShapeFromFile[fileName];
In General
shape.insideVisible ← s.insideVisible;
shape.fixedProps ← s.props;
Set Vertices
shape.vertex ← NEW[VertexSequence[s.nVertices]];
shape.shade ← NEW[ShadingSequence[s.nVertices]];
shape.vertex.length ← shape.shade.length ← s.nVertices;
SetVertexProps[shape,
s.vertexNormals#NIL, s.vertexColors#NIL, s.vertexTextures#NIL, s.vertexTransmits#NIL];
IF s.vertexTextures # NIL AND shape.shadingClass.loadShapeAux = NIL
THEN ThreeDBasics.LoadShadingClass[shape, $MappedAndSolidTexture];
IF s.vertexTextures # NIL THEN SetTexture[shape, s.vertexTextures];
FOR n: CARDINAL IN [0..s.nVertices) DO
v: REF VertexInfo ← NEW[VertexInfo];
[v.coord.x, v.coord.y, v.coord.z] ← s.vertexPositions[n];
IF s.vertexNormals # NIL THEN [v.shade.xn, v.shade.yn, v.shade.zn]←s.vertexNormals[n];
IF s.vertexColors # NIL THEN [v.shade.r, v.shade.g, v.shade.b] ← s.vertexColors[n];
IF s.vertexTransmits # NIL THEN v.shade.t ← s.vertexTransmits[n];
IF s.vertexTextures # NIL THEN v.aux ← NEW[Pair ← s.vertexTextures[n]];
shape.vertex[n] ← NEW[Vertex ← v.coord];   -- load vertex array
shape.shade[n] ← NEW[ShadingValue ← v.shade]; -- load shading array
ENDLOOP;
Set Polygons or Patches
{
AddFixedProp: PROC [key: ATOM] ~ {
shape.fixedProps ← Atom.PutPropOnList[shape.fixedProps, key, patchInfos];
};
NewPatch: PROC [nPatch, nSides: NAT] RETURNS [patch: REF PtrPatch] ~ {
patch ← surfaces[nPatch] ← NEW[PtrPatch ← [
vtxPtr: NEW[NatSequence[nSides]],
oneSided: NOT s.insideVisible,
type: shape.class.type,
nVtces: nSides]];
};
SetShadingValue: PROC [sv: REF ThreeDBasics.ShadingValue, nFace: NAT] ~ {
IF s.faceTransmits # NIL THEN sv.t ← s.faceTransmits[nFace];
IF s.faceColors # NIL THEN [sv.r, sv.g, sv.b] ← s.faceColors[nFace];
IF s.faceNormals # NIL THEN [sv.xn, sv.yn, sv.zn] ← s.faceNormals[nFace];
};
surfaces: REF PtrPatchSequence ← shape.surface ← NEW[PtrPatchSequence[s.nPolygons]];
patchInfos: REF ShadingSequence ← shape.shadingClass.patchShade;
ThreeDBasics.LoadSurfaceType[shape, IF s.type=poly THEN $ConvexPolygon ELSE $Bezier];
IF patchInfos = NIL OR patchInfos.maxLength < s.nPolygons THEN {
shape.shadingClass.patchShade ← patchInfos ← NEW[ShadingSequence[s.nPolygons]];
FOR i: NAT IN [0..s.nPolygons) DO
patchInfos[i] ← NEW[ThreeDBasics.ShadingValue];
ENDLOOP;
};
patchInfos.length ← shape.numSurfaces ← surfaces.length ← s.nPolygons;
IF s.faceTransmits # NIL THEN AddFixedProp[$PatchTransmittancesInFile];
IF s.faceColors # NIL THEN AddFixedProp[$PatchColorsInFile];
IF s.faceNormals # NIL THEN AddFixedProp[$PatchNormalsInFile];
FOR n: NAT IN [0..s.nPolygons) DO
poly: REF NatSequence ← s.polygons[n];
patch: REF PtrPatch ← surfaces[n] ← NewPatch[n, poly.length];
SetShadingValue[patchInfos[n], n];
FOR i: NAT IN [0..poly.length) DO patch.vtxPtr[i] ← poly[i]; ENDLOOP;
ENDLOOP;
};
};
CloneShape: PUBLIC PROC[newshape, oldShape: REF ShapeInstance] ~ { -- copy shape data
newSurface, oldSurface: REF PtrPatchSequence;
newshape.class ← oldShape.class;
newshape.shadingClass ← NEW[ShadingClass ← oldShape.shadingClass^];
newshape.shadingClass.texture ← NIL;     -- restore default values to new shape
newshape.shadingClass.color ← [R: 0.7, G: 0.7, B: 0.7];
newshape.shadingClass.shininess ← 0.0;
newshape.shadingClass.transmittance ← 0.0;
newshape.insideVisible ← oldShape.insideVisible;
newshape.centroid ← oldShape.centroid;
newshape.boundingRadius ← oldShape.boundingRadius;
newshape.vertex ← NEW[ VertexSequence[oldShape.vertex.length] ];
newshape.vertex.length ← oldShape.vertex.length;
newshape.shade ← NEW[ ShadingSequence[oldShape.shade.length] ];
newshape.shade.length ← oldShape.shade.length;
FOR i: NAT IN [0..oldShape.vertex.length) DO      -- copy vertices and shades
newshape.vertex[i] ← NEW [ Vertex ← oldShape.vertex[i]^ ];
newshape.shade[i] ← NEW [ ShadingValue ← oldShape.shade[i]^ ];
ENDLOOP;
newshape.numSurfaces ← oldShape.numSurfaces;
newshape.surface ← NEW[ PtrPatchSequence[oldShape.numSurfaces] ];
newSurface ← NARROW[newshape.surface, REF PtrPatchSequence];
newSurface.length ← oldShape.numSurfaces;
oldSurface ← NARROW[oldShape.surface, REF PtrPatchSequence];
FOR i: NAT IN [0..oldShape.numSurfaces) DO       -- copy surface
IF newSurface[i] = NIL THEN newSurface[i] ← NEW[PtrPatch];
newSurface[i].vtxPtr ← NEW[NatSequence[oldSurface[i].nVtces]];
newSurface[i].nVtces ← oldSurface[i].nVtces;
newSurface[i].type ← oldSurface[i].type;
newSurface[i].oneSided ← oldSurface[i].oneSided;
FOR j: NAT IN [0..oldSurface[i].nVtces) DO
newSurface[i].vtxPtr[j] ← oldSurface[i].vtxPtr[j];
ENDLOOP;
ENDLOOP;
newshape.fixedProps ← oldShape.fixedProps;    -- copy unchanging props
newshape.props ← NIL;             -- copy changeable props
FOR list: Atom.PropList ← oldShape.props, list.rest UNTIL list = NIL DO
element: Atom.DottedPair ← NEW[Atom.DottedPairNode ← list.first^];
newshape.props ← CONS[element, newshape.props];
ENDLOOP;
newshape.shadingProps ← NIL;          -- copy shading props
FOR list: Atom.PropList ← oldShape.shadingProps, list.rest UNTIL list = NIL DO
element: Atom.DottedPair ← NEW[Atom.DottedPairNode ← list.first^];
newshape.shadingProps ← CONS[element, newshape.shadingProps];
ENDLOOP;
IF newshape.shadingClass.patchShade # NIL THEN { -- copy facet info
polyShades: REF ShadingSequence ← newshape.shadingClass.patchShade;
newpolyShades: REF ShadingSequence ← NEW[ ShadingSequence[ polyShades.length ] ];
FOR i: NAT IN [0..polyShades.length) DO
newpolyShades[i] ← NEW[ShadingValue ← polyShades[i]^ ];
ENDLOOP;
newpolyShades.length ← polyShades.length;
newshape.shadingClass.patchShade ← newpolyShades;
};
};
AddShapeAt: PUBLIC PROC[context: REF Context, shapeName: ROPE, fileName: ROPE,
         position: Triple ← [0.,0.,0.] ] ~ {
shape: REF ShapeInstance ← NewShape[shapeName];
cloneShape: REF ShapeInstance ← NIL;
shape.fileName ← PrependWorkingDirectory[context, fileName];
IF context.shapes # NIL THEN FOR i: NAT IN [0..context.shapes.length) DO
IF Rope.Equal[shape.fileName, context.shapes[i].fileName]  -- same data as another shape?
THEN cloneShape ← context.shapes[i];
ENDLOOP;
IF cloneShape # NIL        -- save data reads if previously read
THEN CloneShape[ shape, cloneShape ]
ELSE ReadShape[ shape, shape.fileName ];
AddShape[context, shape];
PlaceShape[context, shape.name, position];
};
WriteShape: PUBLIC PROC[ context: REF Context, shapeName: ROPE, fileName: ROPE,
        transformed: BOOLFALSE, xyz: BOOLTRUE,
        normal, color, trans, texture, polyClr: BOOLFALSE] ~ {
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
stream: IO.STREAMFS.StreamOpen[Rope.Cat[fileName, ".shape"], $create];
numFields: NAT ← 0;
fields: REF FieldSequence ← NEW[FieldSequence[1]];
vertices: REF VertexInfoSequence ← NEW[ VertexInfoSequence[shape.vertex.length] ];
auxData: REF ← GetProp[shape.shadingProps, $AuxiliaryVtxData ];
surfaceType: ROPEIF shape.class.type = $ConvexPolygon THEN "Polygons"
                   ELSE "Patches";
insideVisible: ROPEIF shape.insideVisible THEN " InsideVisible" ELSE NIL;
Header
stream.PutRope[ Rope.Cat[fileName, ".shape\n\n"] ];    -- write title on first line
stream.PutRope[ Rope.Cat["SurfaceType ~ ", surfaceType, insideVisible, "\n\n"] ]; -- surface
Vertices
IF shape.positionInValid THEN ThreeDBasics.SetPosition[shape];   -- set position matrix
FOR i: NAT IN [0..shape.vertex.length) DO
vertices[i] ← NEW[VertexInfo];
vertices[i].coord ← shape.vertex[i]^;
IF transformed THEN { OPEN vertices[i].coord;
[ [x, y, z] ] ← Transform[ [x, y, z], shape.position ];
};
vertices[i].shade ← shape.shade[i]^;
IF transformed AND normal THEN { OPEN vertices[i].shade;
[ [xn, yn, zn] ] ← TransformVec[ [xn, yn, zn], shape.position ];
};
IF auxData # NIL THEN {
data: LORALIST[ auxData, NEW[INTEGER ← i] ];
vertices[i]^ ← shape.shadingClass.loadVtxAux[ NIL, vertices[i]^, data ];
};
ENDLOOP;
vertices.length ← shape.vertex.length;
WriteVertexInfoSequence[ stream, "Vertices", vertices,
           xyz, normal, color, trans, texture ];
Surface
IF polyClr THEN {
color: REF ShadingSequence ← shape.shadingClass.patchShade;
tripleSeq: REF TripleSequence ← NEW[ TripleSequence[color.length] ];
tripleSeq.length ← color.length;
FOR i: NAT IN [0..color.length) DO
tripleSeq[i].x ← color[i].r; tripleSeq[i].y ← color[i].g; tripleSeq[i].z ← color[i].b;
ENDLOOP;
fields ← NEW[FieldSequence[2]];
fields[0] ← NEW[Field];
fields[0].sequence ← tripleSeq;
fields[0].type ← triple;
fields[0].id ← "rgbColor";
numFields ← 1;
};
{ surfels: REF NatTable ← NEW[NatTable[shape.numSurfaces] ];
patches: REF PtrPatchSequence ← NARROW[shape.surface, REF PtrPatchSequence];
FOR i: NAT IN [0..shape.numSurfaces) DO
surfels[i] ← NEW[ NatSequence[patches[i].nVtces] ];
FOR j: NAT IN [0..patches[i].nVtces) DO
surfels[i][j] ← patches[i].vtxPtr[j];
ENDLOOP;
surfels[i].length ← patches[i].nVtces;
ENDLOOP;
surfels.length ← shape.numSurfaces;
fields[numFields] ← NEW[Field];
fields[numFields].sequence ← surfels;
fields[numFields].type ← nats;
fields[numFields].id ← "vertices";
fields.length ← numFields + 1;
};
G3dIO.WriteFields[stream, surfaceType, fields];
IO.Close[stream];
};
WriteVertexInfoSequence: PUBLIC PROC [
stream: IO.STREAM, keyword: ROPE, vertexInfo: REF VertexInfoSequence,
xyz, normal, color, trans, texture: BOOLTRUE] ~ {
IF vertexInfo # NIL THEN {
vi: REF VertexInfoSequence ← vertexInfo;
IO.PutF[stream, "%g~ ", IO.rope[keyword]];
IF xyz THEN IO.PutF[stream, "xyzCoords: triple"];
IF normal THEN IO.PutF[stream, ", normalVec: triple"];
IF color THEN IO.PutF[stream, ", rgbColor: triple"];
IF trans THEN IO.PutF[stream, ", transmittance: real"];
IF texture THEN IO.PutF[stream, ", textureCoords: pair"];
IO.PutF[stream, "\n\n"];
FOR n: NAT IN [0..vertexInfo.length) DO
IF xyz THEN IO.PutF[stream, "%9g %9g %9g\t\t",
IO.real[vi[n].coord.x], IO.real[vi[n].coord.y], IO.real[vi[n].coord.z]];
IF normal THEN IO.PutF[stream, "%9g %9g %9g\t\t",
IO.real[vi[n].shade.xn], IO.real[vi[n].shade.yn], IO.real[vi[n].shade.zn]];
IF color THEN IO.PutF[stream, "%9g %9g %9g\t\t",
IO.real[vi[n].shade.r], IO.real[vi[n].shade.g], IO.real[vi[n].shade.b]];
IF trans THEN IO.PutF[stream, "%9g\t\t", IO.real[vi[n].shade.t]];
IF texture THEN {
txtr: REF Pair ← NARROW[ vi[n].aux ];
IO.PutF[stream, "%9g %9g %9g", IO.real[txtr.x], IO.real[txtr.y] ];
};
IO.PutF[stream, "\n"];
ENDLOOP;
};
};
Procedures for Manipulating Shapes
NewShape: PUBLIC PROC[ name: ROPE, type: ATOM ← $ConvexPolygon ]
     RETURNS
[REF ShapeInstance] ~ {
shape: REF ShapeInstance ← NEW [ShapeInstance ];
shape.name ← name;
ThreeDBasics.LoadSurfaceType[shape, type];      -- load class structures
RETURN [shape];
};
ShapeFromData: PUBLIC PROC[ name: Rope.ROPENIL, surface: REF NatTable,
         vertices, normals: REF TripleSequence ← NIL,
         colors: REF RGBSequence ← NIL,
         trnsmttnce: REF RealSequence ← NIL,
         txtrCoord: REF PairSequence ← NIL,
         insideVisible, faceted: BOOLFALSE,
         type: ATOM ← $ConvexPolygon ]
     RETURNS[REF ShapeInstance] ~ {
shape: REF ShapeInstance ← NewShape[name];
shapeSurface: REF PtrPatchSequence;
IF vertices = NIL THEN ThreeDBasics.Error[[$Fatal, "Vertices needed for shape"]];
IF surface = NIL THEN ThreeDBasics.Error[[$Fatal, "Surface needed for shape"]];
shape.vertex ← NEW[VertexSequence[vertices.length]];
FOR i: NAT IN [0..vertices.length) DO OPEN shape.vertex[i];
x ← vertices[i].x; y ← vertices[i].y; z ← vertices[i].z;
ENDLOOP;
IF normals # NIL THEN {
shape.shade ← NEW[ShadingSequence[normals.length]];
FOR i: NAT IN [0..normals.length) DO OPEN shape.shade[i];
xn ← normals[i].x; yn ← normals[i].y; zn ← normals[i].z;
ENDLOOP;
};
IF normals.length > vertices.length / 2
THEN shape.fixedProps ← PutProp[shape.fixedProps, $VertexNormalsInFile, $ok ];
IF faceted = FALSE AND colors # NIL THEN {
FOR i: NAT IN [0..colors.length) DO OPEN shape.shade[i];
r ← colors[i].R; g ← colors[i].G; b ← colors[i].B;
ENDLOOP;
IF colors.length > vertices.length / 2
THEN shape.fixedProps ← PutProp[shape.fixedProps, $VertexColorsInFile, $ok];
IF trnsmttnce # NIL THEN FOR i: NAT IN [0..trnsmttnce.length) DO
shape.shade[i].t ← trnsmttnce[i];
ENDLOOP;
IF trnsmttnce.length > vertices.length / 2
THEN shape.fixedProps ← PutProp[ shape.fixedProps,
            $VertexTransmittanceInFile, $ok ];
};
shape.numSurfaces ← surface.length;
shape.surface ← shapeSurface ← NEW[ PtrPatchSequence[shape.numSurfaces] ];
shape.insideVisible ← insideVisible;
FOR n: NAT IN [0..shape.numSurfaces) DO
nVertices: INTEGER ← surface[n].length;
shapeSurface[n] ← NEW[PtrPatch ← [
vtxPtr: NEW[NatSequence[nVertices]],
oneSided: NOT insideVisible,
type: type,
nVtces: nVertices
]];
FOR i: NAT IN [0..nVertices) DO shapeSurface[n].vtxPtr[i] ← surface[n][i]; ENDLOOP;
ENDLOOP;
IF txtrCoord # NIL THEN {
IF shape.shadingClass.loadShapeAux = NIL
THEN ThreeDBasics.LoadShadingClass[ shape, $MappedAndSolidTexture ];
[] ← shape.shadingClass.loadShapeAux[ NIL, shape, txtrCoord ]; -- load aux array
};
RETURN[shape];
};
ShapeFromRope: PUBLIC PROC[ name: Rope.ROPENIL, message: Rope.ROPE,
          color: Rope.ROPENIL, size: REAL ← 0.5,
          font: Rope.ROPENIL ]
     RETURNS[REF ShapeInstance] ~ {
shape: REF ShapeInstance ← NewShape[name];
shapeSurface: REF PtrPatchSequence;
shape.fixedProps ← Atom.PutPropOnList[shape.fixedProps, $RopeMessage, message ];
shape.fixedProps ← Atom.PutPropOnList[shape.fixedProps, $RopeFont, font ];
ThreeDBasics.LoadSurfaceType[ shape, $RopeShape ];
ThreeDBasics.LoadShadingClass[ shape, $NoShading ];
shape.shadingClass.color ← ImagerColorFns.RGBFromHSL[ NamedColors.RopeToHSL[color] ];
Build dummy patch structure to pass through ShapeUtilities.ShapePatchToPatch
shape.vertex ← NEW[VertexSequence[2]];  
shape.vertex[0] ← NEW[Vertex];
shape.vertex[1] ← NEW[Vertex];     shape.vertex[1].z ← size;
shape.vertex.length ← 2;
shape.shade ← NEW[ShadingSequence[2]]; 
shape.shade[0] ← NEW[ShadingValue];  shape.shade[1] ← NEW[ShadingValue];
shape.surface ← shapeSurface ← NEW[ PtrPatchSequence[1] ];
shapeSurface[0] ← NEW[PtrPatch ← [
vtxPtr: NEW[NatSequence[3]], oneSided: FALSE, type: shape.class.type, nVtces: 3
]];
shapeSurface[0].vtxPtr[0] ← shapeSurface[0].vtxPtr[2] ← 0; shapeSurface[0].vtxPtr[1] ← 1;
shape.numSurfaces ← 1;
RETURN[shape];
};
ChangeRopeMessage: PUBLIC PROC[ context: REF Context, shapeName: ROPE,
           newMessage: Rope.ROPE ] ~ {
shape: REF ShapeInstance ← FindShape[context, shapeName];
shape.fixedProps ← Atom.PutPropOnList[shape.fixedProps, $RopeMessage, newMessage ];
};
FindShape: PUBLIC PROC[ context: REF Context, shapeName: ROPE ]
     RETURNS[REF ShapeInstance] ~ {
IF context.shapes = NIL THEN RETURN [NIL];
FOR i: NAT IN [0..context.shapes.length) DO
IF Rope.Equal[ shapeName, context.shapes[i].name, FALSE ]
THEN RETURN[context.shapes[i]];
ENDLOOP;
SIGNAL ThreeDBasics.Error[[$MisMatch, "No such shape name"]];
RETURN [NIL];
};
AddShape: PUBLIC PROC[ context: REF Context, shape: REF ShapeInstance ] ~ {
newSet: REF ShapeSequence;
newPos: NAT ← 0;
IF context.shapes = NIL
THEN { newSet ← NEW [ShapeSequence[8]]; newPos ← 0; }
ELSE { newSet ← context.shapes; newPos ← context.shapes.length; };
IF INT[newSet.maxLength] < newPos + 1 THEN {
newSet ← NEW [ ShapeSequence[context.shapes.maxLength + 8] ];
FOR i: NAT IN [0..context.shapes.length) DO newSet[i] ← context.shapes[i]; ENDLOOP;
};
newSet[newPos] ← shape;
context.shapes ← newSet;
context.shapes.length ← newPos+1;
};
DeleteShape: PUBLIC PROC[ context: REF Context, shapeName: ROPE ] ~ {
found: BOOLEANFALSE;
IF context.shapes = NIL THEN {
SIGNAL ThreeDBasics.Error[[$MisMatch, "No shapes to delete from"]];
RETURN[];
};
FOR i: NAT IN [0..context.shapes.length) DO IF found
THEN context.shapes[i] ← context.shapes[i+1]
ELSE {
found ← Rope.Equal[ shapeName, context.shapes[i].name, FALSE ];
IF found THEN {
context.shapes[i] ← context.shapes[i+1];
context.shapes.length ← context.shapes.length - 1;
};
};
ENDLOOP;
IF NOT found THEN SIGNAL ThreeDBasics.Error[[$MisMatch, "Can't delete - not there"]];
};
CopyShape: PUBLIC PROC[ context: REF Context, shapeName, newName: ROPENIL ]
    RETURNS[REF ShapeInstance] ~ {
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
RETURN[ CopyShapeDirect[shape, newName] ];
};
CopyShapeDirect: PUBLIC PROC[ shape: REF ShapeInstance, newName: ROPENIL ]
      RETURNS[REF ShapeInstance] ~ {
Makes a copy of a shape
newShape: REF ShapeInstance ← NEW[ShapeInstance];
newShape^ ← shape^;
IF newName # NIL THEN newShape.name ← newName;
IF shape.vertex # NIL THEN {
newShape.vertex ← NEW[ VertexSequence[shape.vertex.length] ];
FOR i: NAT IN [0..shape.vertex.length) DO
IF shape.vertex[i] # NIL THEN newShape.vertex[i] ← NEW[Vertex ← shape.vertex[i]^ ];
ENDLOOP;
newShape.vertex.length ← shape.vertex.length
};
IF shape.shade # NIL THEN {
newShape.shade ← NEW[ ShadingSequence[shape.shade.length] ];
FOR i: NAT IN [0..shape.shade.length) DO
IF shape.shade[i] # NIL THEN newShape.shade[i] ← NEW[ShadingValue ← shape.shade[i]^];
ENDLOOP;
newShape.shade.length ← shape.shade.length
};
newShape.surface ← ?
- Can't copy this since we can't know the type here, will point to the original
newShape.props ← newShape.shadingProps ← NIL;        -- copy props
FOR list: Atom.PropList ← shape.props, list.rest UNTIL list = NIL DO
element: Atom.DottedPair ← NEW[Atom.DottedPairNode ← list.first^];
newShape.props ← CONS[element, newShape.props];
ENDLOOP;
FOR list: Atom.PropList ← shape.shadingProps, list.rest UNTIL list = NIL DO
element: Atom.DottedPair ← NEW[Atom.DottedPairNode ← list.first^];
newShape.shadingProps ← CONS[element, newShape.shadingProps];
ENDLOOP;
RETURN[newShape];
};
PlaceShape: PUBLIC PROC[ context: REF Context, shapeName: ROPE, location: Triple ] ~ {
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
shape.location ← location;
shape.positionInValid ← TRUE;
shape.vtcesInValid ← TRUE;
shape.shadingInValid ← TRUE;
};
MoveShape: PUBLIC PROC[ context: REF Context, shapeName: ROPE, delta: Triple ] ~ {
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
shape.location ← G3dVector.Add[shape.location, delta];
shape.positionInValid ← TRUE;
shape.vtcesInValid ← TRUE;
shape.shadingInValid ← TRUE;
};
OrientShape: PUBLIC PROC[ context: REF Context, shapeName: ROPE, axis: Triple] ~ {
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
shape.orientation ← axis;
shape.positionInValid ← TRUE;
shape.vtcesInValid ← TRUE;
shape.shadingInValid ← TRUE;
};
RotateShapeLocal: PUBLIC PROC[ context: REF Context, shapeName: ROPE, theta: REAL] ~{
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
shape.rotation ← theta;
shape.positionInValid ← TRUE;
shape.vtcesInValid ← TRUE;
shape.shadingInValid ← TRUE;
};
RotateShape: PUBLIC PROC[ context: REF Context, shapeName: ROPE,
        axisBase, axisEnd: Triple, theta: REAL ] ~ {
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
shape.axisBase ← axisBase;
shape.axisEnd ← axisEnd;
shape.rotation ← theta;
shape.positionInValid ← TRUE;
shape.vtcesInValid ← TRUE;
shape.shadingInValid ← TRUE;
};
Hide: PUBLIC PROC[context: REF Context, shapeName: ROPE] ~ {
Maintain data but don't display
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
shape.props ← PutProp[shape.props, $Hidden, $ok];
};
Reveal: PUBLIC PROC[context: REF Context, shapeName: ROPE] ~ { -- undo Hide
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
shape.props ← Atom.RemPropFromList[shape.props, $Hidden];
};
Procedures for Shading Surfaces
SetColor: PUBLIC PROC[context: REF Context, shapeName: ROPE, color: RGB] ~ {
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
IF shape # NIL THEN {
shape.shadingClass.color ← color;
shape.shadingInValid ← TRUE;
};
};
SetFaceted: PUBLIC PROC[context: REF Context, shapeName: ROPE] ~ { -- default style
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
IF shape # NIL THEN {
shape.shadingClass.shadingType ← $Faceted;
shape.shadingInValid ← TRUE;
};
};
SetSmooth: PUBLIC PROC[context: REF Context, shapeName: ROPE] ~ {
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
IF shape # NIL THEN {
shape.shadingClass.shadingType ← $Smooth;
shape.shadingInValid ← TRUE;
};
};
SetLines: PUBLIC PROC[context: REF Context, shapeName: ROPE] ~ {
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
IF shape # NIL THEN {
shape.shadingClass.shadingType ← $Lines;
shape.shadingInValid ← TRUE;
};
};
SetShadedLines: PUBLIC PROC[context: REF Context, shapeName: ROPE] ~ {
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
IF shape # NIL THEN {
shape.shadingClass.shadingType ← $ShadedLines;
shape.shadingInValid ← TRUE;
};
};
SetHiddenLines: PUBLIC PROC[context: REF Context, shapeName: ROPE] ~ {
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
IF shape # NIL THEN {
shape.shadingClass.shadingType ← $HiddenLines;
shape.shadingInValid ← TRUE;
};
};
SetNormaledLines: PUBLIC PROC[context: REF Context, shapeName: ROPE] ~ {
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
IF shape # NIL THEN {
shape.shadingClass.shadingType ← $NormaledLines;
shape.shadingInValid ← TRUE;
};
};
SetShiny: PUBLIC PROC[context: REF Context, shapeName: ROPE, shininess: REAL ← 50.0] ~ {
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
IF shape # NIL THEN {
shape.shadingClass.shininess ← shininess;
shape.shadingInValid ← TRUE;
};
};
SetDull: PUBLIC PROC[context: REF Context, shapeName: ROPE] ~ {
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
IF shape # NIL THEN {
shape.shadingClass.shininess ← 0.0;
shape.shadingInValid ← TRUE;
};
};
SetTransparent: PUBLIC PROC[context: REF Context, shapeName: ROPE, t: REAL ← 0.8] ~ {
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
IF shape # NIL THEN {
shape.shadingClass.transmittance ← t;
IF NOT shape.insideVisible -- set prop for recovery of insideVisible if later made opaque
THEN shape.fixedProps ← PutProp[shape.fixedProps, $Closed, $ok];
IncludeBackFaces[context, shapeName];
shape.shadingInValid ← TRUE;
};
};
SetOpaque: PUBLIC PROC[context: REF Context, shapeName: ROPE] ~ {
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
IF shape # NIL THEN {
shape.shadingClass.transmittance ← 0.0;
IF GetProp[shape.fixedProps, $Closed] # NIL
THEN RemoveBackFaces[context, shapeName];
shape.shadingInValid ← TRUE;
};
};
IncludeBackFaces: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE] ~ {
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
IF shape # NIL THEN {
shape.insideVisible ← TRUE;
WITH shape.surface SELECT FROM
shapeSurface: REF PtrPatchSequence => FOR i: NAT IN [0..shapeSurface.length) DO
shapeSurface[i].oneSided ← NOT shape.insideVisible;
ENDLOOP;
ENDCASE;
};
};
RemoveBackFaces: PUBLIC PROC[context: REF Context, shapeName: Rope.ROPE] ~ {
shape: REF ShapeInstance ← FindShape[ context, shapeName ];
IF shape # NIL THEN {
shape.insideVisible ← FALSE;
WITH shape.surface SELECT FROM
shapeSurface: REF PtrPatchSequence => FOR i: NAT IN [0..shapeSurface.length) DO
shapeSurface[i].oneSided ← NOT shape.insideVisible;
ENDLOOP;
ENDCASE;
};
};
END.
..
Old Code
SetTexture: PUBLIC PROC [shape: REF ShapeInstance, textures: REF PairSequence] ~ {
ThreeDBasics.LoadShadingClass[shape, $MappedAndSolidTexture]; -- need this line?
LoadShadingClass zeros the shadingProps (why?), so that call must precede this:
shape.shadingProps ← Atom.PutPropOnList[shape.shadingProps, $AuxiliaryVtxData, textures];
This tells ThreeDWorld eventually to fill in aux field of VertexInfo from textures.
This magic happens in ShapeUtilities.ShapePatchToPatch.
};
ReadShape: PUBLIC PROC[shape: REF ShapeInstance, fileName: ROPE] ~ {
stream: IO.STREAMFS.StreamOpen[ fileName: fileName ];
surfaceType: ATOM;
firstVtxNo: NAT ← 0;         -- some files count from 0 some from 1
surfaceInfo: ROPE;
Get Surface Type
surfaceInfo ← G3dIO.ReadRope[
stream, "SurfaceType", TRUE
! G3dIO.Error => CONTINUE
];
surfaceType ← $ConvexPolygon;             -- default
shape.insideVisible ← FALSE;
IF surfaceInfo # NIL THEN {
IF Rope.Find[surfaceInfo, "Polygon", 0, FALSE] >= 0 THEN surfaceType ← $ConvexPolygon;
IF Rope.Find[surfaceInfo, "Bezier", 0, FALSE] >= 0  THEN surfaceType ← $Bezier;
IF Rope.Find[surfaceInfo, "InsideVisible", 0, FALSE] >= 0 THEN shape.insideVisible ← TRUE;
IF Rope.Find[surfaceInfo, "CountFromOne", 0, FALSE] >= 0 THEN firstVtxNo ← 1;
};
ThreeDBasics.LoadSurfaceType[ shape, surfaceType ];
Get Vertices
{ vertices: REF VertexInfoSequence;
fields: REF FieldSequence;
[vertices, fields] ← ReadVertexInfoSequence[stream, "Vertices"];
shape.vertex ← NEW[ VertexSequence[vertices.length] ];
shape.vertex.length ← vertices.length;
shape.shade ← NEW[ ShadingSequence[vertices.length] ];
shape.shade.length ← vertices.length;
FOR i: NAT IN [0..vertices.length) DO     -- load vertex and shading arrays
shape.vertex[i] ← NEW[ Vertex ← vertices[i].coord ];
shape.shade[i] ← NEW[ ShadingValue ← vertices[i].shade ];
ENDLOOP;
FOR i: NAT IN [0..fields.length) DO
SELECT TRUE FROM
Rope.Equal[fields[i].id, "normalVec", FALSE] =>    -- vertex normals read
shape.fixedProps ← PutProp[ shape.fixedProps, $VertexNormalsInFile, $ok ];
Rope.Equal[fields[i].id, "rgbColor", FALSE] =>    -- vertex colors read
shape.fixedProps ← PutProp[ shape.fixedProps, $VertexColorsInFile, $ok ];
Rope.Equal[fields[i].id, "transmittance", FALSE] =>   -- vertex transmittance read
shape.fixedProps ← PutProp[ shape.fixedProps, $VertexTransmittanceInFile, $ok ];
Rope.Equal[fields[i].id, "textureCoords", FALSE] => {  -- texture coordinates read
IF shape.shadingClass.loadShapeAux = NIL
THEN ThreeDBasics.LoadShadingClass[ shape, $MappedAndSolidTexture ];
[] ← shape.shadingClass.loadShapeAux[ NIL, shape, vertices ]; -- load aux array
shape.fixedProps ← PutProp[shape.fixedProps, $VertexTextureInFile, $ok ]
};
ENDCASE;
ENDLOOP;
};
Get Surface
{ keyWord: ROPEIF shape.class.type = $ConvexPolygon THEN "Polygons"
                   ELSE "Patches";
fields: REF FieldSequence ← G3dIO.ReadFields[stream, keyWord];
GetPatchInfo: PROC[shape: REF ShapeInstance] RETURNS[p: REF ShadingSequence] ~ {
p ← shape.shadingClass.patchShade;
IF p = NIL THEN {
p ← NEW[ShadingSequence[shape.numSurfaces] ];
p.length ← shape.numSurfaces;
FOR i: NAT IN [0..shape.numSurfaces) DO
p[i] ← NEW[ShadingValue];
ENDLOOP;
shape.shadingClass.patchShade ← p;
};
};
shape.numSurfaces ← 0;
FOR n: NAT IN [0..fields.length) DO
patchInfo: REF ShadingSequence;
SELECT fields[n].type FROM
integer => {
intSeq: REF IntegerSequence ← NARROW[fields[n].sequence];
shape.numSurfaces ← intSeq.length;
SELECT TRUE FROM
Rope.Equal[fields[n].id, "index", FALSE] => {};
ENDCASE => {
key: ATOM ← Convert.AtomFromRope[fields[n].id];  -- special, put on proplist
shape.fixedProps ← PutProp[shape.fixedProps, key, intSeq ];
};
};
real  => {
realSeq: REF RealSequence ← NARROW[fields[n].sequence];
shape.numSurfaces ← realSeq.length;
patchInfo ← GetPatchInfo[shape];
SELECT TRUE FROM
Rope.Equal[fields[n].id, "transmittance", FALSE] => {
FOR i: NAT IN [0..realSeq.length) DO patchInfo[i].t ← realSeq[i]; ENDLOOP;
shape.fixedProps ← PutProp[
shape.fixedProps, $PatchTransmittancesInFile, patchInfo
];
};
ENDCASE => {
key: ATOM ← Convert.AtomFromRope[fields[n].id];  -- special, put on proplist
shape.fixedProps ← PutProp[shape.fixedProps, key, realSeq ];
};
};
triple  => {
tripleSeq: REF TripleSequence ← NARROW[fields[n].sequence];
shape.numSurfaces ← tripleSeq.length;
patchInfo ← GetPatchInfo[shape];
SELECT TRUE FROM
Rope.Equal[fields[n].id, "rgbColor", FALSE] => {
FOR i: NAT IN [0..tripleSeq.length) DO
patchInfo[i].r ← tripleSeq[i].x;
patchInfo[i].g ← tripleSeq[i].y;
patchInfo[i].b ← tripleSeq[i].z;
ENDLOOP;
shape.fixedProps ← PutProp[shape.fixedProps, $PatchColorsInFile, patchInfo ];
};
Rope.Equal[fields[n].id, "normalVec", FALSE] => {
FOR i: NAT IN [0..tripleSeq.length) DO
patchInfo[i].xn ← tripleSeq[i].x;
patchInfo[i].yn ← tripleSeq[i].y;
patchInfo[i].zn ← tripleSeq[i].z;
ENDLOOP;
shape.fixedProps ← PutProp[shape.fixedProps, $PatchNormalsInFile, patchInfo ];
};
ENDCASE => {
key: ATOM ← Convert.AtomFromRope[fields[n].id]; -- special, put on proplist
shape.fixedProps ← PutProp[shape.fixedProps, key, tripleSeq ];
};
};
nats  => {             -- get the surface elements
surface: REF PtrPatchSequence;
surfels: REF NatTable ← NARROW[fields[n].sequence];
shape.numSurfaces ← surfels.length;
shape.surface ← NEW[ PtrPatchSequence[shape.numSurfaces] ];
surface ← NARROW[shape.surface, REF PtrPatchSequence];
surface.length ← shape.numSurfaces;
FOR i: NAT IN [0..shape.numSurfaces) DO
IF surface[i] = NIL THEN surface[i] ← NEW[PtrPatch];
surface[i].vtxPtr ← NEW[NatSequence[surfels[i].length]];
surface[i].nVtces ← surfels[i].length;
surface[i].type ← shape.class.type;
surface[i].oneSided ← NOT shape.insideVisible;
FOR j: NAT IN [0..surfels[i].length) DO
surface[i].vtxPtr[j] ← surfels[i][j] - firstVtxNo; -- counting from zero
ENDLOOP;
ENDLOOP;
};
ENDCASE => {
key: ATOM ← Convert.AtomFromRope[fields[n].id];  -- special, put on proplist
shape.fixedProps ← PutProp[shape.fixedProps, key, fields[n].sequence ];
};
ENDLOOP;
};
};
ReadVertexInfoSequence: PROC [
stream: IO.STREAM, keyword: ROPE, circularSearch: BOOLFALSE]
RETURNS [REF VertexInfoSequence, REF FieldSequence] ~ {
fields: REF FieldSequence;
nElements: INTEGER;           -- number of lines to convert
vIS: REF VertexInfoSequence;
types: ARRAY [0..5) OF G3dIO.FieldType ← [triple, triple, triple, real, pair];
ids: ARRAY [0..5) OF ROPE ← [
"xyzCoords", "normalVec", "rgbColor", "transmittance", "textureCoords"
];
line: G3dIO.Line ← G3dIO.FindKeyWord[stream, keyword, circularSearch];
IF G3dIO.NWordsInRope[line.rope] > 1
THEN fields ← G3dIO.InitializeFields[line]
ELSE {
fields ← NEW[FieldSequence[5]];
FOR n: NAT IN [0..5) DO
fields[n].id ← ids[n];
fields[n].type ← types[n];
ENDLOOP;
};
nElements ← G3dIO.NumberOfLinesToConvert[stream];
vIS ← NEW[VertexInfoSequence[nElements]];
FOR n: NAT IN [0..nElements) DO
vIS[n] ← NEW[VertexInfo];
line ← G3dIO.GetDataLine[stream];
FOR i: NAT IN [0..fields.length) DO
SELECT TRUE FROM
Rope.Equal[fields[i].id, "xyzCoords", FALSE] =>
[[vIS[n].coord.x, vIS[n].coord.y, vIS[n].coord.z]] ← G3dIO.GetTriple[line];
Rope.Equal[fields[i].id, "normalVec", FALSE] =>
[[vIS[n].shade.xn, vIS[n].shade.yn, vIS[n].shade.zn]] ←
               G3dIO.GetTriple[line];
Rope.Equal[fields[i].id, "rgbColor", FALSE] =>
[[vIS[n].shade.r, vIS[n].shade.g, vIS[n].shade.b]] ← G3dIO.GetTriple[line];
Rope.Equal[fields[i].id, "transmittance", FALSE] =>
vIS[n].shade.t ← G3dIO.GetReal[line];
Rope.Equal[fields[i].id, "textureCoords", FALSE] => {
txtr: REF Pair ← NEW[Pair ← G3dIO.GetPair[line]];
vIS[n].aux ← txtr;
};
ENDCASE => SELECT fields[i].type FROM
integer, real => [] ← G3dIO.GetWord[line];
triple => {
[] ← G3dIO.GetWord[line];
[] ← G3dIO.GetWord[line];
[] ← G3dIO.GetWord[line];
};
ENDCASE => NULL;
ENDLOOP;
ENDLOOP;
vIS.length ← nElements;
RETURN[vIS, fields];
};