G3dRenderImpl.mesa
Copyright Ó 1985, 1989 by Xerox Corporation. All rights reserved.
Bloomenthal, September 10, 1989 1:48:21 pm PDT
Heckbert, August 9, 1988 4:50:32 pm PDT
Crow, September 21, 1989 4:19:55 pm PDT
Glassner, July 19, 1989 12:03:35 pm PDT
DIRECTORY Atom, BasicTime, CedarProcess, Commander, Convert, FS, G3dBasic, G3dColorDisplaySupport, G3dMappedAndSolidTexture, G3dMatrix, G3dLight, G3dRender, G3dRenderWithPixels, G3dShade, G3dShape, G3dSortandDisplay, G3dVector, G3dView, ImagerColorFns, ImagerColorMap, IO, NamedColors, Real, Rope, RuntimeError, SafeStorage, ViewerClasses, ViewerIO, ViewerOps;
G3dRenderImpl:
CEDAR
MONITOR
IMPORTS Atom, BasicTime, CedarProcess, Commander, Convert, FS, G3dColorDisplaySupport, G3dMappedAndSolidTexture, G3dLight, G3dMatrix, G3dRenderWithPixels, G3dShade, G3dShape, G3dSortandDisplay, G3dVector, G3dView, ImagerColorFns, ImagerColorMap, IO, NamedColors, Real, Rope, RuntimeError, SafeStorage, ViewerIO, ViewerOps
EXPORTS G3dRender
Types
Miscellaneous Types
PropList: TYPE ~ Atom.PropList;
STREAM: TYPE ~ IO.STREAM;
ROPE: TYPE ~ Rope.ROPE;
Process: TYPE ~ CedarProcess.Process;
Light: TYPE ~ G3dLight.Light;
LightRep: TYPE ~ G3dLight.LightRep;
Imported Sequences
NatSequence: TYPE ~ G3dBasic.NatSequence;
NatSequenceRep: TYPE ~ G3dBasic.NatSequenceRep;
PairSequence: TYPE ~ G3dBasic.PairSequence;
TripleSequence: TYPE ~ G3dBasic.TripleSequence;
LightSequence: TYPE ~ G3dLight.LightSequence;
Pixel Definitions
RGB: TYPE ~ G3dRender.RGB;
Geometric Definitions
Pair: TYPE ~ G3dBasic.Pair;
Triple: TYPE ~ G3dBasic.Triple;
Quad: TYPE ~ G3dBasic.Quad; -- RECORD [x, y, z, w: REAL];
Matrix: TYPE ~ G3dMatrix.Matrix; -- REF 4 by 4 ARRAY OF REAL
Image Definitions
SampleMap: TYPE ~ G3dRender.SampleMap;
PixelMap: TYPE ~ G3dRender.PixelMap;
Box: TYPE ~ G3dRender.Box; -- [min, max: Pair]
Rectangle: TYPE ~ G3dRender.Rectangle; -- RECORD [x, y, w, h: REAL]
Polygon Definitions
SurfaceSequence: TYPE ~ G3dBasic.SurfaceSequence;
SurfaceSequenceRep: TYPE ~ G3dBasic.SurfaceSequenceRep;
Texture Definitions
TextureFunction: TYPE ~ G3dRender.TextureFunction;
TextureMap: TYPE ~ G3dRender.TextureMap;
SummedTexture: TYPE ~ G3dRender.SummedTexture;
TextureStyle: TYPE ~ G3dRender.TextureStyle;
TextureInfo: TYPE ~ G3dRender.TextureInfo;
Control Point Definitions
CtlPoint: TYPE ~ G3dRender.CtlPoint;
Shape Definitions
Shape: TYPE ~ G3dShape.Shape;
ShapeRep: TYPE ~ G3dShape.ShapeRep;
ShapeSequence: TYPE ~ G3dShape.ShapeSequence;
ShapeSequenceRep: TYPE ~ G3dShape.ShapeSequenceRep;
Vertex: TYPE ~ G3dShape.Vertex;
VertexRep: TYPE ~ G3dShape.VertexRep;
VertexSequenceRep: TYPE ~ G3dShape.VertexSequenceRep;
PatchSequence: TYPE ~ G3dRender.PatchSequence;
ShapeClass: TYPE ~ G3dRender.ShapeClass;
ShadingClass: TYPE ~ G3dRender.ShadingClass;
ShapeProc: TYPE ~ G3dRender.ShapeProc;
RenderStyle: TYPE ~ G3dRender.RenderStyle;
Context Definition
Context: TYPE ~ G3dRender.Context;
ContextRep: TYPE ~ G3dRender.ContextRep;
ContextClass: TYPE ~ G3dRender.ContextClass;
RenderData: TYPE ~ G3dRender.RenderData;
DisplayMode: TYPE ~ G3dRender.DisplayMode;
Class Registration and Installation
RegisterDisplayClass:
PUBLIC
PROC [class: ContextClass, type:
ATOM] ~ {
registeredDisplayTypes ← PutProp[registeredDisplayTypes, type, NEW[ContextClass ← class]];
};
GetDisplayClass:
PUBLIC
PROC [type:
ATOM]
RETURNS [class: ContextClass] ~ {
refClass: REF ContextClass ← NARROW[GetProp[registeredDisplayTypes, type]];
IF refClass #
NIL
THEN class ← refClass^
ELSE Error[$Unimplemented, "Unregistered display type"]
};
LoadDisplayClass:
PUBLIC
PROC [context: Context, type:
ATOM] ~ {
class: REF ContextClass ← NARROW[GetProp[registeredDisplayTypes, type]];
IF class = NIL THEN Error[$Unimplemented, "Unregistered display type"];
context.class ← NEW[ContextClass ← class^];
context.pixels ← NIL; --this and next line for saving VM
context.displayProps ← Atom.RemPropFromList[context.displayProps, $FullDisplayMemory];
context.class.setUpDisplayType[context]; -- maps in pixels, sets up color map
IF GetProp[context.props, $BufferContext] #
NIL
THEN {
Error[$Warning, "Dropping Buffer context"]; -- insufficient info to update
context.props ← Atom.RemPropFromList[context.props, $BufferContext];
};
WITH GetProp[context.props, $BackGround]
SELECT
FROM
backGrdCtx: Context => {
Error[$Warning, "Dropping BackGround context"]; -- no info for update
context.props ← Atom.RemPropFromList[context.props, $BackGround];
};
ENDCASE;
};
RegisterShapeClass:
PUBLIC
PROC [class: ShapeClass, type:
ATOM] ~ {
registeredSurfaceTypes ← PutProp[registeredSurfaceTypes, type, NEW[ShapeClass ← class]];
};
GetShapeClass:
PUBLIC
PROC [type:
ATOM]
RETURNS [class: ShapeClass] ~ {
refClass: REF ShapeClass ← NARROW[GetProp[registeredSurfaceTypes, type]];
IF refClass #
NIL
THEN class ← refClass^
ELSE Error[$Unimplemented, "Unregistered surface type"];
};
LoadShapeClass:
PUBLIC
PROC [shape: Shape, type:
ATOM] ~{
renderData: REF RenderData;
class: REF ShapeClass ← NARROW[GetProp[registeredSurfaceTypes, type]];
IF class = NIL THEN Error[$Unimplemented, "Unregistered surface type"];
renderData ← RenderDataFrom[shape];
IF renderData.class =
NIL
THEN renderData.class ← NEW[ShapeClass ← class^]
ELSE {
-- inherit previous procedures if no replacements
new: REF ShapeClass ← NEW[ShapeClass ← class^];
IF new.validate = NIL THEN new.validate ← renderData.class.validate;
IF new.display = NIL THEN new.display ← renderData.class.display;
IF new.displayPatch = NIL THEN new.displayPatch ← renderData.class.displayPatch;
IF new.doBeforeFrame =
NIL
THEN new.doBeforeFrame ← renderData.class.doBeforeFrame;
renderData.class ← new;
};
IF ShadingClassFrom[shape] = NIL THEN G3dShade.LoadShadingClass[shape]; -- load default class
shape.renderValid ← FALSE; -- in case we're changing the type on an existing shape
shape.screenValid ← FALSE;
shape.props ← Atom.RemPropFromList[shape.props, $LinesList]; -- list may not be valid
shape.type ← type;
};
InitStandardShapeClasses:
PROC[] ~ {
-- register procedures for basic surface types
standardClass: ShapeClass ← [
type: $Light,
validate: G3dSortandDisplay.DummyValidate,
display: NIL,
displayPatch: NIL
];
RegisterShapeClass[standardClass, $Light]; -- placeholder class for light source
standardClass.type ← $ConvexPolygon;
standardClass.validate ← G3dSortandDisplay.ValidatePolyhedron;
standardClass.displayPatch ← G3dSortandDisplay.OutputPolygon;
RegisterShapeClass[standardClass, $ConvexPolygon]; -- ConvexPolygon procs
Non-convex polygon type, $Poly, registered from G3dStandardPatchProcs.mesa
standardClass.type ← $RopeShape;
standardClass.displayPatch ← G3dSortandDisplay.RopeDisplay;
RegisterShapeClass[standardClass, $RopeShape]; -- RopeShape procs
};
Context
Create:
PUBLIC
PROC []
RETURNS [Context] ~ {
context: Context ← NEW[ContextRep];
wDir: ROPE ← Commander.PrependWorkingDir[" "]; -- add needed space (wierdness)
wDir ← Rope.Substr[base: wDir, len: Rope.Length[wDir] - 1]; -- drop space
context.props ← PutProp[context.props, $WDir, wDir]; -- keep directory
context.eyeSpaceXfm ← G3dMatrix.Identity[]; -- can't do this in initialization, so do it here
context.stopMe ← NEW[BOOLEAN ← FALSE];
RETURN[context];
};
CreateUndisplayedContext:
PUBLIC
PROC [
oldContext: Context ← NIL,
width: NAT ← 1024,
height: NAT ← 768,
displayMode: DisplayMode ← fullColor,
keepLog: BOOLEAN ← FALSE ]
RETURNS [context: Context] ~ {
Return a Context with memory allocated for the indicated number of pixels. If oldContext is not NIL then copy existing scene, background, lights, etc. from it, otherwise initialize context to standard defaults and set RGB background to [0.2, 0.2, 0.7].
context ← Create[];
context.preferredRenderMode ← $Pixels;
IF oldContext # NIL THEN CopyContextData[context, oldContext];
context.viewer ← NIL;
context.viewPort ←
NEW[
-- set viewport directly to define pixelMap size
Rectangle ← [ x: 0.0, y: 0.0, w: Real.Float[width], h: Real.Float[height] ]
];
context.preferredViewPort ← context.viewPort^;
SELECT displayMode
FROM
gray => LoadDisplayClass[context, $GrayInVM];
dither => LoadDisplayClass[context, $PseudoColorInVM];
fullColor => LoadDisplayClass[context, $FullColorInVM];
ENDCASE; -- can't get here without type error
IF keepLog THEN [] ← StartLog[context];
context.changed ← TRUE; -- ensure screen coordinates get updated
IF oldContext = NIL THEN SetBackgroundColor[context, [0.2, 0.2, 0.7]];
};
InitializeRawColorDisplayContext:
PUBLIC
PROC [
antiAliasing: BOOL ← TRUE,
background: RGB ← [0.2, 0.2, 0.7],
displayMode: DisplayMode ← gray]
RETURNS [context: Context]
~ {
context ← Create[];
context.preferredRenderMode ← $Pixels;
LoadDisplayClass[context, AtomFromDisplayMode[displayMode]];
G3dRenderWithPixels.BufferRendering[context, FALSE]; -- , displayType = $FullColor];
SetAntiAliasing[context, antiAliasing];
SetBackgroundColor[context, background];
AddLight[context, "Light0", [-100.0, -200.0, 20.0]]; -- Jules' desired defaults
SetView[context, [0.0, -10.0, 0.0], []];
};
StoreImage:
PUBLIC
PROC [
context: Context,
fileName: ROPE,
sequenceNo: NAT ← LAST[NAT],
log: BOOL ← FALSE,
abekasFormat: BOOL ← FALSE]
~ {
cp: FS.ComponentPositions;
fullFName: ROPE;
IF log
OR sequenceNo #
LAST[
NAT]
THEN {
out:
STREAM ←
FS.StreamOpen[
fileName: GetLogFileName[PrependWorkingDirectory[context, fileName]],
accessOptions: $append,
wDir: NARROW[GetProp[context.props, $WDir] ]
];
IO.PutRope[out, Rope.Cat[
" Frame no. ", Convert.RopeFromInt[sequenceNo], " written at ",
Convert.RopeFromTime[from: BasicTime.Now[], start: months, end: seconds], "\n"]];
IO.Close[out];
};
[fullFName, cp] ← FS.ExpandName[fileName];
IF abekasFormat
THEN fullFName ← Rope.Replace[fullFName, cp.ext.start, cp.ext.length, "rgb"]
ELSE
IF Rope.Equal[Rope.Substr[fullFName, cp.ext.start, cp.ext.length], "rgb",
FALSE]
THEN abekasFormat ← TRUE;
fileName ← PrependWorkingDirectory[context, fileName];
IF sequenceNo # LAST[NAT] THEN fileName ← PasteInSequenceNo[fileName, sequenceNo];
IF abekasFormat
THEN G3dColorDisplaySupport.PutRGB[context, fileName]
ELSE G3dColorDisplaySupport.PutAIS[context, fileName];
};
CloseDisplay:
PUBLIC
PROC [context: Context] ~ {
context.pixels ← NIL; -- throw away buffer bits
context.displayProps ← Atom.RemPropFromList[context.displayProps, $FullDisplayMemory];
ImagerColorMap.SetStandardColorMap[context.terminal
-- restore standard color map
! RuntimeError.BoundsFault => CONTINUE];
};
CloseColorViewers:
PUBLIC
PROC ~ {
EnumProc: ViewerOps.EnumProc ~ {
IF v.column = color
THEN {
ViewerOps.CloseViewer[v];
ViewerOps.ChangeColumn[v, left];
};
};
ViewerOps.EnumerateViewers[EnumProc];
};
KillUntitledColorViewers:
PUBLIC
PROC ~ {
EnumProc: ViewerOps.EnumProc ~ {
IF v.column = color AND v.name = NIL THEN ViewerOps.DestroyViewer[v];
};
ViewerOps.EnumerateViewers[EnumProc];
};
GetTmpContext:
PUBLIC
PROC [srcCtx: Context]
RETURNS[dstCtx: Context] ~ {
copies context so temporary modifications can be made (don't change shapes however)
dstCtx ← Create[];
CopyContextData[dstCtx, srcCtx];
dstCtx.pixels ← srcCtx.pixels;
};
CopyContextData:
PUBLIC
PROC [dstCtx, srcCtx: Context] ~ {
copies REFed data to dstCtx to insulate srcCtx from changes (except for shapes)
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.changed ← srcCtx.changed;
dstCtx.eyePoint ← srcCtx.eyePoint;
dstCtx.lookAt ← srcCtx.lookAt;
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[ Rectangle ← srcCtx.viewPort^]
ELSE NIL;
dstCtx.preferredViewPort ← srcCtx.preferredViewPort;
dstCtx.screenExtent ← srcCtx.screenExtent;
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: Context] ~ {
copies shape data to dstCtx to insulate srcCtx shapes from changes
dstCtx.shapes ← NEW[ ShapeSequenceRep[srcCtx.shapes.length] ];
FOR i:
NAT
IN [0..srcCtx.shapes.length)
DO
dstCtx.shapes[i] ← NEW[ ShapeRep ← srcCtx.shapes[i]^ ];
RenderDataFrom[dstCtx.shapes[i]].shadingClass ←
NEW[
ShadingClass ← RenderDataFrom[srcCtx.shapes[i]].shadingClass^
];
ENDLOOP;
dstCtx.shapes.length ← srcCtx.shapes.length;
};
View
SetViewFromParameters:
PUBLIC
PROC [
context: Context,
fieldOfView: REAL ← 40.0,
scale: REAL ← 1.0,
moves, rotates: Triple ← []]
~ {
fov: REAL ← IF fieldOfView = 0.0 THEN 40.0 ELSE fieldOfView;
eyePoint, lookAt, upDirection: Triple;
[eyePoint, lookAt, upDirection] ← G3dView.FromScaleMovesRots[scale, moves, rotates];
SetView[context, eyePoint, lookAt, fov, 0.0, upDirection];
};
SetView:
PUBLIC
PROC [
context: Context,
eyePoint: Triple,
lookAt: Triple,
fieldOfView: REAL ← 40.0,
rollAngle: REAL ← 0.0,
upDirection: Triple ← [0., 0., 1.],
hitherLimit: REAL ← .01,
yonLimit: REAL ← 1000.0]
~ {
context.eyePoint ← eyePoint;
context.lookAt ← lookAt;
context.fieldOfView ← fieldOfView;
context.rollAngle ← rollAngle;
context.upDirection ← upDirection;
context.hitherLimit ← hitherLimit;
context.yonLimit ← yonLimit;
context.changed ← TRUE;
};
SetViewPort:
PUBLIC
PROC [context: Context, size: Rectangle] ~{
IF size.w <= 0.0 OR size.h <= 0.0 THEN SIGNAL Error[$MisMatch, "Null rectangle"];
context.preferredViewPort ← size;
context.viewPort ← NIL; -- computed by ValidateView GetViewportFromViewer
context.window ← NIL; -- resizing viewport forces update of window
context.displayInValid ← TRUE;
};
SetWindow:
PUBLIC
PROC[context: Context, size: Rectangle] ~{
context.window ← NEW[ Rectangle ← size ];
context.changed ← TRUE;
};
Background
NameBackgroundColor:
PUBLIC
PROC [context: Context, color:
ROPE] ~ {
Set background color using color naming scheme
bkgrdColor: RGB ← ImagerColorFns.RGBFromHSL[NamedColors.RopeToHSL[color]];
SetBackgroundColor[context, bkgrdColor]; -- set color
};
SetBackgroundColor:
PUBLIC
PROC [context: Context, color:
RGB] ~ {
Set background color
context.props ← PutProp[context.props, $BackGround, NEW[RGB ← color]];
};
GetBackgroundColor:
PUBLIC
PROC [context: Context]
RETURNS [rgb:
RGB] ~ {
Return the background for context.
WITH GetProp[context.props, $BackGround]
SELECT
FROM
x: REF RGB => rgb ← x^;
ENDCASE;
};
SetBackgroundImage:
PUBLIC
PROC [context: Context, aisFile:
ROPE] ~ {
Use named AIS file as background image for scene instead of solid color
bkGrdCtx: Context ← Create[];
bkGrdCtx.depthBuffering ← context.depthBuffering;
bkGrdCtx.antiAliasing ← context.antiAliasing;
bkGrdCtx.class ← context.class;
bkGrdCtx.props ← PutProp[bkGrdCtx.props, $BackGrdImage, aisFile];
SetBackgroundContext[context, bkGrdCtx];
};
GetBackgroundImage:
PUBLIC
PROC [context: Context]
RETURNS [r:
ROPE ←
NIL] ~ {
Return the name of the background image (NIL if none).
WITH GetProp[context.props, $BackGround]
SELECT
FROM
bkGrdCtx: Context => {
name: REF ANY ← GetProp[bkGrdCtx.props, $BackGrdImage];
IF name # NIL THEN r ← NARROW[name];
};
ENDCASE;
};
SetBackgroundContext:
PUBLIC
PROC [context, bkGrdCtx: Context] ~ {
Use named 3d context as background image for scene (contexts may be stacked arbitrarily).
context.props ← PutProp[context.props, $BackGround, bkGrdCtx];
};
EnableClear:
PUBLIC
PROC [context: Context, on:
BOOL] ~ {
IF on
THEN context.props ← Atom.RemPropFromList[context.props, $DisableClear]
ELSE context.props ← Atom.PutPropOnList[context.props, $DisableClear, $DoIt];
};
DontMatteBackground:
PUBLIC
PROC [context: Context] ~ {
Remove the background, images will be rendered with no background. Use for images to be matted over others using alpha channel
context.props ← Atom.RemPropFromList[context.props, $BackGround];
};
Shapes
AddShape:
PUBLIC
PROC [context: Context, shape: Shape] ~ {
IF shape = NIL THEN RETURN;
IF shape.renderData = NIL THEN shape.renderData ← NEW[RenderData]; -- JB
DeleteShape[context, shape.name ! Error => CONTINUE];
context.shapes ← G3dShape.AddToShapeSequence[context.shapes, shape];
};
AddShapeFromFile:
PUBLIC
PROC [
context: Context,
shapeName: ROPE,
fileName: ROPE,
position: Triple ← [0., 0., 0.]]
~ {
shape, copyShape: Shape ← NIL;
fileName ← PrependWorkingDirectory[context, fileName];
IF context.shapes #
NIL
THEN
FOR i:
NAT
IN [0..context.shapes.length)
DO
IF Rope.Equal[fileName, context.shapes[i].fileName]
-- same data as another shape?
THEN copyShape ← context.shapes[i];
ENDLOOP;
IF copyShape #
NIL
-- save data reads if previously read
THEN shape ← G3dShape.CopyShape[copyShape]
ELSE shape ← G3dShape.ShapeFromFile[fileName];
shape.name ← shapeName;
shape.fileName ← fileName;
AddShape[context, shape];
G3dShape.TransformShape[shape: shape, translate: position];
};
FindShape:
PUBLIC
PROC [context: Context, shapeName:
ROPE]
RETURNS [shape: Shape ← NIL]
~ {
IF context #
NIL
THEN
shape ← G3dShape.FindShape[context.shapes, shapeName ! G3dShape.Error => CONTINUE];
IF shape = NIL THEN SIGNAL Error[$MisMatch, Rope.Cat[shapeName, " - not found"]];
};
ShapeFromRope:
PUBLIC
PROC [
name: ROPE ← NIL,
message: ROPE,
color: ROPE ← NIL,
size: REAL ← 0.5,
font: ROPE ← NIL]
RETURNS [Shape] ~ {
shape: Shape ← NEW[ShapeRep ← [name: name, matrix: G3dMatrix.Identity[]]];
renderData: REF RenderData ← NEW[RenderData];
renderData.props ← PutProp[renderData.props, $RopeMessage, message];
renderData.props ← PutProp[renderData.props, $RopeFont, font];
shape.renderData ← renderData;
LoadShapeClass[shape, $RopeShape];
G3dShade.LoadShadingClass[shape, $NoShading];
renderData.shadingClass.color ← ImagerColorFns.RGBFromHSL[NamedColors.RopeToHSL[color]];
Build dummy patch structure to pass through ShapeUtilities.ShapePatchToPatch
shape.vertices ← NEW[VertexSequenceRep[2]];
shape.vertices[0] ← NEW[VertexRep];
shape.vertices[1] ← NEW[VertexRep]; shape.vertices[1].point.z ← size;
shape.vertices.length ← 2;
shape.surfaces ← NEW[SurfaceSequenceRep[1]];
shape.surfaces[0].vertices ← NEW[NatSequenceRep[3]];
shape.surfaces[0].vertices[0] ← shape.surfaces[0].vertices[2] ← 0;
shape.surfaces[0].vertices[1] ← 1;
shape.surfaces[0].vertices.length ← 3;
shape.surfaces.length ← 1;
RETURN[shape];
};
ChangeRopeMessage:
PUBLIC
PROC
[context:
Context,
shapeName:
ROPE,
newMessage:
ROPE]
~ {
shape: Shape ← FindShape[context, shapeName];
NARROW[shape.renderData,
REF RenderData].props ← PutProp[
NARROW[shape.renderData, REF RenderData].props, $RopeMessage, newMessage
];
};
DeleteShape:
PUBLIC
PROC [context: Context, shapeName:
ROPE] ~ {
found: BOOL ← FALSE;
IF context.shapes =
NIL
THEN {
SIGNAL Error[$MisMatch, "No shapes to delete from"]; RETURN[];
};
FOR i:
CARDINAL
IN [0..context.shapes.length)
DO
IF
NOT found
THEN found ← Rope.Equal[shapeName, context.shapes[i].name, FALSE]
ELSE context.shapes[i-1] ← context.shapes[i];
ENDLOOP;
IF found
THEN {
context.shapes.length ← context.shapes.length - 1;
context.shapes[context.shapes.length] ← NIL;
}
ELSE SIGNAL Error[$MisMatch, Rope.Cat["Can't delete", shapeName, "- not there"]];
};
DeleteAllShapes:
PUBLIC
PROC [context: Context] ~ {
IF context #
NIL
THEN {
FOR n: NAT IN [0..context.shapes.length) DO context.shapes[n] ← NIL; ENDLOOP;
context.shapes ← NIL;
};
};
SetRenderStyle:
PUBLIC
PROC [shape: Shape, renderStyle: RenderStyle] ~ {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade = NIL THEN RETURN;
shade.renderMethod ← NEW[RenderStyle ← renderStyle];
shape.renderValid ← FALSE;
};
SetColor:
PUBLIC
PROC [shape: Shape, color:
RGB] ~ {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade = NIL THEN RETURN;
shade.color ← color;
shape.renderValid ← FALSE;
};
SetDiffuse:
PUBLIC
PROC [shape: Shape, diffuseReflectivity:
REAL] ~ {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade = NIL THEN RETURN;
shade.diffuseReflectivity ← diffuseReflectivity;
shape.renderValid ← FALSE;
SetSpecular:
PUBLIC
PROC [shape: Shape, specularReflectivity:
REAL] ~ {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade = NIL THEN RETURN;
shade.specularReflectivity ← specularReflectivity;
shape.renderValid ← FALSE;
SetMetallicity:
PUBLIC
PROC [shape: Shape, metallicity:
REAL] ~ {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade = NIL THEN RETURN;
shade.metallicity ← metallicity;
shape.renderValid ← FALSE;
SetShininess:
PUBLIC
PROC [shape: Shape, shininess:
REAL] ~ {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade = NIL THEN RETURN;
shade.shininess ← shininess;
shape.renderValid ← FALSE;
SetTransmittance:
PUBLIC
PROC [shape: Shape, transmittance:
REAL] ~ {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade = NIL THEN RETURN;
shade.transmittance ← transmittance;
shape.renderValid ← FALSE;
};
SetInvisible:
PUBLIC
PROC [shape: Shape] ~ {
IF shape #
NIL
THEN shape.visible ←
FALSE};
SetVisible:
PUBLIC
PROC [shape: Shape] ~ {
IF shape #
NIL
THEN shape.visible ←
TRUE};
ShowBackfaces:
PUBLIC
PROC [shape: Shape] ~ {
IF shape = NIL THEN RETURN;
shape.showBackfaces ← TRUE;
RenderDataFrom[shape].patch ← NIL;
};
HideBackfaces:
PUBLIC
PROC [shape: Shape] ~ {
IF shape = NIL THEN RETURN;
shape.showBackfaces ← FALSE;
RenderDataFrom[shape].patch ← NIL;
};
RenderDataFrom:
PUBLIC
PROC [shape: Shape]
RETURNS [data:
REF RenderData] ~ {
IF shape = NIL THEN RETURN[NIL];
IF shape.renderData = NIL THEN shape.renderData ← NEW[RenderData];
data ← NARROW[shape.renderData];
};
ShapeClassFrom:
PUBLIC
PROC [shape: Shape]
RETURNS [
REF ShapeClass] ~ {
Return the shapeClass for this shape.
data: REF RenderData;
IF shape = NIL THEN RETURN[NIL];
data ← RenderDataFrom[shape];
IF data.class = NIL THEN LoadShapeClass[shape, shape.type];
RETURN[data.class];
};
ShadingClassFrom:
PUBLIC
PROC [shape: Shape]
RETURNS [
REF ShadingClass] ~ {
Return the shadingClass for this shape.
data: REF RenderData;
IF shape = NIL THEN RETURN[NIL];
data ← RenderDataFrom[shape];
IF data.shadingClass = NIL THEN G3dShade.LoadShadingClass[shape];
RETURN[data.shadingClass];
};
PatchesFrom:
PUBLIC
PROC [shape: Shape]
RETURNS [PatchSequence] ~ {
Get shape.renderData.patch, the patch-by-patch description, of the surface, conveniently.
data: REF RenderData ← RenderDataFrom[shape];
RETURN[IF data # NIL THEN data.patch ELSE NIL];
};
AddAxes:
PUBLIC
PROC [
context: Context,
origin: Triple ← [0, 0, 0],
size: REAL ← 1.0,
scale: Triple ← [1, 1, 1],
nReticles: NAT ← 20]
~ {
Don't create shapes, just transform axes endpoints and draw smooth lines to Imager context?
AddAxis:
PROC [name:
ROPE, p, v0, v1: Triple] ~ {
AddName: PROC [name: ROPE, position: Triple] ~ {
s: Shape ← ShapeFromRope[name, name, "white"];
s.position ← position;
AddShape[context, s];
};
SetRect:
PROC [id, n0, n1, n2, n3:
NAT] ~ {
poly: G3dShape.Surface ← s.surfaces[id] ← [NIL, NEW[NatSequenceRep[4]]];
poly.vertices.length ← 4;
poly.vertices[0] ← n0;
poly.vertices[1] ← n1;
poly.vertices[2] ← n2;
poly.vertices[3] ← n3;
};
SetVertex: PROC [id: NAT, p: Triple] ~ {s.vertices[id] ← NEW[VertexRep ← [point: p]]};
s: Shape ← NEW[ShapeRep ← [name: Rope.Cat[name, "-axis"], matrix: G3dMatrix.Identity[]]];
SetRenderStyle[s, lines];
s.surfaces ← NEW[SurfaceSequenceRep[4]]; s.surfaces.length ← 4;
s.vertices ← NEW[VertexSequenceRep[8]]; s.vertices.length ← 8;
SetVertex[0, origin];
SetVertex[1, G3dVector.Add[origin, v0]];
SetVertex[2, G3dVector.Add[G3dVector.Add[origin, v0], v1]];
SetVertex[3, G3dVector.Add[origin, v1]];
SetVertex[4, p];
SetVertex[5, G3dVector.Add[p, v0]];
SetVertex[6, G3dVector.Add[G3dVector.Add[p, v0], v1]];
SetVertex[7, G3dVector.Add[p, v1]];
SetRect[0, 0, 1, 5, 4];
SetRect[1, 1, 2, 6, 5];
SetRect[2, 2, 3, 7, 6];
SetRect[3, 3, 0, 4, 7];
FOR n: NAT IN [0..nReticles) DO
p1: Triple ← G3dVector.Interp[REAL[n]/REAL[nReticles], origin, direction];
p2: Triple ← G3dVector.Add[p1, offset];
s.vertices[2*n] ← NEW[VertexRep ← [point: p1]];
s.vertices[2*n+1] ← NEW[VertexRep ← [point: p2]];
s.surfaces[n].vertices ← NewPoly[2*n, 2*n+1];
ENDLOOP;
SetRenderStyle[s, lines];
AddShape[context, s];
AddName[name, direction];
};
AddAxis["X", [origin.x+size, origin.y, origin.z], [0.0, 0.01, 0.0], [0.0, 0.0, 0.01]];
AddAxis["Y", [origin.x, origin.y+size, origin.z], [0.0, 0.0, 0.01], [0.01, 0.0, 0.0]];
AddAxis["Z", [origin.x, origin.y, origin.z+size], [0.01, 0.0, 0.0], [0.0, 0.01, 0.0]];
};
Textures
SetTextureMap:
PUBLIC
PROC [
context: Context,
shapeName: ROPE,
aisName: ROPE,
textureStyle: TextureStyle ← intensity,
textureFiltering: BOOL ← FALSE]
RETURNS [error: ROPE]
~ {
a: ATOM ~ AtomFromTextureStyle[textureStyle];
IF shapeName = NIL THEN RETURN;
Kill current texture map (if any) before adding new one, so only one texture at a time.
G3dMappedAndSolidTexture.RemoveAllTexture[context, shapeName];
IF textureStyle = none
THEN G3dMappedAndSolidTexture.RemoveAllTexture[context, shapeName]
ELSE
IF aisName #
NIL
THEN {
ENABLE Error => {error ← reason; CONTINUE};
m: REF TextureMap ← G3dMappedAndSolidTexture.TextureFromAIS[context, aisName, a];
shape: Shape ← FindShape[context, shapeName];
Disable the bizarre uv wraparound code in G3dMappedAndSolidTexture.AdjustTexture:
IF shape # NIL THEN SetTextureRange[shape, [1000., 1000.]];
G3dMappedAndSolidTexture.AddMappedTexture[context, shapeName, m];
IF textureFiltering
THEN G3dMappedAndSolidTexture.SumAllMappedTextures[context, shapeName];
};
};
OffsetTextureCoords:
PUBLIC
PROC [shape: Shape, offset: Pair] ~ {
FOR n:
NAT
IN [0..shape.vertices.length)
DO
v: Vertex ← shape.vertices[n];
v.texture ← [v.texture.x+offset.x, v.texture.y+offset.y];
ENDLOOP;
};
GetTextureInfo:
PUBLIC
PROC [s: Shape]
RETURNS [textureInfo:
LIST
OF TextureInfo] ~ {
filtered: BOOL ← FALSE;
IF s = NIL OR ShadingClassFrom[s] = NIL THEN RETURN;
FOR l:
LIST
OF
REF
ANY ← ShadingClassFrom[s].texture, l.rest
WHILE l #
NIL
DO
WITH l.first
SELECT
FROM
texture:
REF TextureMap => {
ref: REF ANY ← GetProp[texture.props, $FileName];
name: ROPE ← IF ref # NIL THEN NARROW[ref] ELSE NIL;
type: TextureStyle ←
SELECT texture.type
FROM
$Intensity => intensity,
$Color => color,
$Bump => bump,
ENDCASE => none;
WITH texture.pixels
SELECT
FROM
x: PixelMap => filtered ← FALSE;
x: REF SummedTexture => filtered ← TRUE;
ENDCASE;
textureInfo ← CONS[[name, type, filtered], textureInfo];
};
txtrFn:
REF TextureFunction =>
-- solid or other function-based texture
textureInfo ← CONS[[NIL, function, FALSE], textureInfo];
ENDCASE;
ENDLOOP;
};
ScaleTexture:
PUBLIC
PROC [context: Context, shape: Shape, scale: Pair] ~ {
G3dMappedAndSolidTexture.ScaleTxtrCoords[context, shape.name, 1.0, scale.x, scale.y];
};
GetTextureScale:
PUBLIC
PROC [shape: Shape]
RETURNS [scale: Pair ← [1.0, 1.0]] ~ {
IF shape #
NIL
THEN {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade # NIL THEN scale ← shade.textureScale;
};
};
SetTextureScale:
PUBLIC
PROC [shape: Shape, scale: Pair ← [1.0, 1.0]] ~ {
IF shape #
NIL
THEN {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade # NIL THEN shade.textureScale ← scale;
};
};
SetTextureRange:
PUBLIC
PROC [shape: Shape, textureRange: Pair] ~ {
IF shape #
NIL
THEN
NARROW[shape.renderData,
REF RenderData].shadingProps ← PutProp[
NARROW[shape.renderData, REF RenderData].shadingProps,
$TxtrCoordRange, NEW[Pair ← textureRange]
];
};
SetTextureFiltering:
PUBLIC
PROC [context: Context, shape: Shape, on:
BOOL] ~ {
IF shape #
NIL
THEN {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade.texture = NIL THEN RETURN;
IF on
THEN G3dMappedAndSolidTexture.SumAllMappedTextures[context, shape.name]
ELSE
FOR l:
LIST
OF TextureInfo ← GetTextureInfo[shape], l.rest
WHILE l #
NIL
DO
[] ← SetTextureMap[context, shape.name, l.first.name, l.first.type];
ENDLOOP;
};
};
GetBumpScale:
PUBLIC
PROC [shape: Shape]
RETURNS [scale:
REAL ← 1.0] ~ {
IF shape #
NIL
THEN {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade # NIL THEN scale ← shade.bumpScale;
};
};
SetBumpScale:
PUBLIC
PROC [shape: Shape, scale:
REAL ← 1.0] ~ {
IF shape #
NIL
THEN {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade # NIL THEN shade.bumpScale ← scale;
};
};
Rendering Control
StartLog:
PUBLIC
PROC [context: 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: Context] ~ {
log: IO.STREAM ← NARROW[GetProp[context.props, $Log]];
IF log # NIL THEN IO.Flush[log];
};
CloseLog:
PUBLIC
PROC [context: Context] ~ {
log: IO.STREAM ← NARROW[GetProp[context.props, $Log]];
IF log # NIL THEN IO.Close[log];
context.props ← Atom.RemPropFromList[context.props, $Log]
};
Render:
PUBLIC
PROC [context: Context, fork:
BOOL ←
TRUE] ~ {
IF fork
THEN {
process: Process ← CedarProcess.Fork[ReallyRender, context, [background, TRUE]];
context.props ← PutProp[context.props, $Process, process];
}
ELSE [] ← ReallyRender[context];
};
RendertoAISFile:
PUBLIC
PROC [context: Context, name:
ROPE, fork:
BOOL ←
TRUE] ~ {
IF fork
THEN {
process: Process ← CedarProcess.Fork[ReallyRender, context, [background, TRUE]];
context.props ← PutProp[context.props, $Process, process];
}
ELSE [] ← ReallyRender[context];
StoreImage[context, name ]; -- store resulting image
};
WaitTilRenderDone:
PUBLIC
ENTRY
PROC ~ {
ENABLE UNWIND => NULL;
WAIT renderDone;
};
NotRendering:
PUBLIC
PROC [context: Context]
RETURNS [b:
BOOL] ~ {
process: Process ← NARROW[GetProp[context.props, $Process]];
RETURN[process = NIL OR CedarProcess.GetStatus[process] # busy];
};
BroadcastRenderDone:
ENTRY
PROC ~ {
BROADCAST renderDone;
};
ReallyRender: CedarProcess.ForkableProc ~ {
context: Context ← NARROW[data];
IF context.shapes # NIL THEN context.class.render[context ! ABORTED => CONTINUE];
BroadcastRenderDone[];
If providing management alpha and depth buffers for three-d compositing:
SurfaceRender.ShowShapes[context ! ABORTED => CONTINUE];
};
AbortRender:
PUBLIC
PROC [context: Context] ~ {
IF context # NIL THEN context.stopMe^ ← TRUE;
};
GetDisplayMode:
PUBLIC
PROC [context: Context]
RETURNS [DisplayMode] ~ {
RETURN[
SELECT context.class.displayType
FROM
$PseudoColor => dither, $Gray => gray, ENDCASE => fullColor];
};
SetAntiAliasing:
PUBLIC
PROC [context: Context, on:
BOOL ←
TRUE] ~ {
This will draw images using the alpha buffer; all texture and transparency are enabled.
G3dRenderWithPixels.AntiAliasing[context, on];
};
AntiAliasingNeeded:
PUBLIC
PROC [context: Context]
RETURNS [
BOOL] ~ {
Determine if anti-aliasing is needed to render the shapes in their respective shading modes.
For example, anti-aliasing is necessary for bump-mapping, transparency, solid texture.
IF context.shapes #
NIL
THEN
FOR n:
NAT
IN [0..context.shapes.length)
DO
FOR
l:
LIST
OF
TextureInfo
←
GetTextureInfo[context.shapes[n]],
l.rest
WHILE
l
#
NIL
DO
IF l.first.type = bump OR l.first.type = function THEN RETURN[TRUE];
ENDLOOP;
IF ShadingClassFrom[context.shapes[n]].transmittance > 0.0 THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
GetBuffer:
PUBLIC
PROC [context: Context, type:
ATOM]
RETURNS [sm: SampleMap ←
NIL]
~ {
r: REF ← GetProp[context.displayProps, type];
IF r # NIL AND context.pixels # NIL THEN sm ← context.pixels[NARROW[r, REF NAT]^]
};
GetAlphaBuffer:
PUBLIC
PROC [context: Context]
RETURNS [SampleMap] ~ {
RETURN[GetBuffer[context, $Alpha]];
};
GetDepthBuffer:
PUBLIC
PROC [context: Context]
RETURNS [SampleMap] ~ {
RETURN[GetBuffer[context, $Depth]];
};
Local Utilities and Miscellany
Sqr:
PROCEDURE [number:
REAL]
RETURNS [
REAL] ~
INLINE {
RETURN[number*number]; };
GetRenderData:
PROCEDURE [context: Context, shapeName:
ROPE]
RETURNS [
REF RenderData]
~ {
data: REF RenderData ← NIL;
IF context #
NIL
THEN {
shape: Shape ← FindShape[context, shapeName];
IF shape #
NIL
THEN
IF shape.renderData #
NIL
THEN data ←
NARROW[
shape.renderData
! SafeStorage.NarrowRefFault => CONTINUE
];
};
RETURN[data];
};
PrependWorkingDirectory:
PUBLIC
PROC [context: 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 ];
};
GetLogFileName:
PUBLIC
PROC[fileRoot:
ROPE]
RETURNS[
ROPE] ~ {
gets name of log file for updating animation status
cp: FS.ComponentPositions;
fullFName, fName: ROPE;
[fullFName, cp] ← FS.ExpandName[fileRoot];
fName ← Rope.Substr[fullFName, 0, IF cp.ext.length = 0 THEN cp.ext.start ELSE cp.ext.start-1];
RETURN[Rope.Cat[fName, ".", "log"]];
};
PasteInSequenceNo:
PUBLIC
PROC[fileRoot:
ROPE, number:
NAT]
RETURNS[
ROPE] ~ {
adds sequence number before file name extension for animations
num: ROPE ← Convert.RopeFromInt[number, 10, FALSE];
SELECT Rope.Length[num]
FROM
1 => num ← Rope.Cat["00", num];
2 => num ← Rope.Cat["0", num];
3 => {};
ENDCASE => SIGNAL Error[$MisMatch, "Sequence # out of range"];
RETURN[ PasteInLabel[fileRoot, num ] ];
};
PasteInLabel:
PUBLIC
PROC[fileRoot:
ROPE, label:
ROPE]
RETURNS[
ROPE] ~ {
puts arbitrary label before file name extension, for adding "-red, -grn, -blu" etc.
cp: FS.ComponentPositions; fullFName, fName, ext: ROPE;
[fullFName, cp, ] ← FS.ExpandName[fileRoot];
IF cp.ext.length = 0
THEN {
fName ← Rope.Substr[ fullFName, 0, cp.ext.start]; -- name for filling in numbers
ext ← "ais";
}
ELSE {
fName ← Rope.Substr[ fullFName, 0, cp.ext.start-1]; -- drop "." before ext
ext ← Rope.Substr[ fullFName, cp.ext.start, cp.ext.length];
};
RETURN[ Rope.Cat[fName, label, ".", ext] ];
};
IntersectRectangles:
PUBLIC
PROC [rect1, rect2: Rectangle]
RETURNS [Rectangle] ~ {
intersection: Rectangle;
intersection.x ← MAX[rect1.x, rect2.x];
intersection.y ← MAX[rect1.y, rect2.y];
intersection.w ← MIN[rect1.w - (intersection.x - rect1.x), rect2.w - (intersection.x - rect2.x)];
intersection.h ← MIN[rect1.h - (intersection.y - rect1.y), rect2.h - (intersection.y - rect2.y)];
RETURN [intersection];
};
AtomFromTextureStyle:
PUBLIC
PROC [textureStyle: TextureStyle]
RETURNS [a:
ATOM] ~ {
a ← SELECT textureStyle FROM bump => $Bump, color => $Color, ENDCASE => $Intensity;
};
AtomFromDisplayMode:
PUBLIC
PROC [displayMode: DisplayMode]
RETURNS [a:
ATOM] ~ {
a ← SELECT displayMode FROM fullColor=>$FullColor, dither=>$PseudoColor, ENDCASE=>$Gray;
};
RopeFromDisplayMode:
PUBLIC
PROC [displayMode: DisplayMode]
RETURNS [
ROPE] ~ {
RETURN[
SELECT displayMode
FROM
gray => "Gray",
dither => "Dither",
fullColor => "FullColor",
ENDCASE => NIL];
};
RopeFromRenderStyle:
PUBLIC
PROC [renderStyle: RenderStyle]
RETURNS [
ROPE] ~ {
RETURN[
SELECT renderStyle
FROM
faceted => "Faceted",
smooth => "Smooth",
lines => "Lines",
shadedLines => "ShadedLines",
hiddenLines => "HiddenLines",
ENDCASE => NIL];
};
RopeFromTextureStyle:
PUBLIC
PROC [textureStyle: TextureStyle]
RETURNS [
ROPE] ~ {
RETURN[
SELECT textureStyle
FROM
intensity => "Intensity",
color => "Color",
bump => "Bump",
ENDCASE => "None"];
};