<> <> <> <> 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 <> 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; <> 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; <> 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; <> 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] ~ { <> output: ROPE _ NIL; 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]; }; <> CreateDefaultContext: PUBLIC PROC[] RETURNS [REF Context] ~ { <> 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: ROPE _ NARROW[ 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] ~ { <> 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; <> 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.STREAM _ NARROW[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.STREAM _ NARROW[GetProp[context.props, $Log]]; IF log # NIL THEN IO.Flush[log]; }; CloseLog: PUBLIC PROC [context: REF Context] ~ { log: IO.STREAM _ NARROW[GetProp[context.props, $Log]]; IF log # NIL THEN IO.Close[log]; context.props _ Atom.RemPropFromList[context.props, $Log] }; <> ForcePrioritySort: PUBLIC PROC[context: REF Context, on: BOOLEAN _ TRUE] ~{ 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; <> context.window _ NIL; -- resizing viewport forces update of window context.displayInValid _ TRUE; }; <> SetAmbientLight: PUBLIC PROC [context: REF Context, color: ROPE] ~ { ambientColor: REF RGB _ NEW[ 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 RGB _ NEW[ RGB _ color ]; context.props _ PutProp[context.props, $BackGround, bkgrdColor]; -- set color }; GetBackgroundColor: PUBLIC PROC [context: REF Context] RETURNS [color: RGB] ~ { <> 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 RGB _ NEW[ 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; }; <> SaveOnFile: PUBLIC PROC[context: REF Context, fileName: ROPE] ~ { wDir: ROPE _ PrependWorkingDirectory[context, NIL]; output: IO.STREAM _ FS.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.STREAM _ FS.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.STREAM _ NARROW[ 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: BOOLEAN _ FALSE; log: IO.STREAM _ NARROW[GetProp[context.props, $Log]]; startTime: REAL _ CurrentTime[]; WHILE NOT done DO keyWd: ROPE _ GetRope[ input ! IO.EndOfStream => EXIT ]; SELECT TRUE FROM <> 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 RGB _ NEW[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 ]; }; <> Rope.Equal[ "ReadShape:", keyWd, FALSE] => { -- read in new shape data. name: ROPE _ GetRope[input]; fileName: ROPE _ GetRope[input]; type: ATOM _ IO.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: REAL _ IO.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]]; <> 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: REAL _ IO.GetReal[input]; SetShiny[context, shape.name, shininess]; }; Rope.Equal[ "Transmittance:", keyWd, FALSE] => { transmittance: REAL _ IO.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"]]; }; <> 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: RGB _ NARROW[ref, REF RGB]^; IO.PutRope[ output, Rope.Cat[ "BackgroundColor: ", Vec3toRope[color.R, color.G, color.B], "\n" ] ]; }; <> 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 ANY _ NIL; xfm: Xfm3D _ shape.position; <> 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]; <> 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]; }; <> { 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: 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" ] ]; }; <> IF shape.shadingClass.texture # NIL THEN { tmpTxtr: LORA _ NIL; 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: ATOM _ NARROW[ 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 REAL _ NARROW[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 <> }; <<>> <> 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]; <> [] _ shape.shadingClass.loadShapeAux[ NIL, shape, textures ]; -- now load aux array <> <> }; 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] ~ { <> s: G3dIO.Shape _ G3dIO.ShapeFromFile[fileName]; <> shape.insideVisible _ s.insideVisible; shape.fixedProps _ s.props; <> 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; <> { 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: BOOL _ FALSE, xyz: BOOL _ TRUE, normal, color, trans, texture, polyClr: BOOL _ FALSE] ~ { shape: REF ShapeInstance _ FindShape[ context, shapeName ]; stream: IO.STREAM _ FS.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: ROPE _ IF shape.class.type = $ConvexPolygon THEN "Polygons" ELSE "Patches"; insideVisible: ROPE _ IF shape.insideVisible THEN " InsideVisible" ELSE NIL; <
> stream.PutRope[ Rope.Cat[fileName, ".shape\n\n"] ]; -- write title on first line stream.PutRope[ Rope.Cat["SurfaceType ~ ", surfaceType, insideVisible, "\n\n"] ]; -- surface <> 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: LORA _ LIST[ 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 ]; <> 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: BOOL _ TRUE] ~ { 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; }; }; <> 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.ROPE _ NIL, surface: REF NatTable, vertices, normals: REF TripleSequence _ NIL, colors: REF RGBSequence _ NIL, trnsmttnce: REF RealSequence _ NIL, txtrCoord: REF PairSequence _ NIL, insideVisible, faceted: BOOL _ FALSE, 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.ROPE _ NIL, message: Rope.ROPE, color: Rope.ROPE _ NIL, size: REAL _ 0.5, font: Rope.ROPE _ NIL ] 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] ]; <> 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: BOOLEAN _ FALSE; 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: ROPE _ NIL ] RETURNS[REF ShapeInstance] ~ { shape: REF ShapeInstance _ FindShape[ context, shapeName ]; RETURN[ CopyShapeDirect[shape, newName] ]; }; CopyShapeDirect: PUBLIC PROC[ shape: REF ShapeInstance, newName: ROPE _ NIL ] RETURNS[REF ShapeInstance] ~ { <> 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 }; <> <<- 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] ~ { <> 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]; }; <> 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. .. <> SetTexture: PUBLIC PROC [shape: REF ShapeInstance, textures: REF PairSequence] ~ { ThreeDBasics.LoadShadingClass[shape, $MappedAndSolidTexture]; -- need this line? <> shape.shadingProps _ Atom.PutPropOnList[shape.shadingProps, $AuxiliaryVtxData, textures]; <> <> }; ReadShape: PUBLIC PROC[shape: REF ShapeInstance, fileName: ROPE] ~ { stream: IO.STREAM _ FS.StreamOpen[ fileName: fileName ]; surfaceType: ATOM; firstVtxNo: NAT _ 0; -- some files count from 0 some from 1 surfaceInfo: ROPE; <> 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 ]; <<>> <> { 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; }; <> { keyWord: ROPE _ IF 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: BOOL _ FALSE] 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]; };