G3dRenderImpl.mesa
Copyright Ó 1985, 1989 by Xerox Corporation. All rights reserved.
Bloomenthal, April 15, 1989 4:50:32 pm PDT
Heckbert, August 9, 1988 4:50:32 pm PDT
Crow, April 21, 1989 11:48:17 am PDT
DIRECTORY Atom, CedarProcess, Commander, FS, G3dBasic, G3dMappedAndSolidTexture, G3dMatrix, G3dRender, G3dRenderWithPixels, G3dShadeClipXfm, G3dShape, G3dSortandDisplay, G3dView, ImagerColorFns, ImagerColorMap, IO, NamedColors, Real, Rope, RuntimeError, SafeStorage, ViewerClasses, ViewerIO, ViewerOps;
G3dRenderImpl: CEDAR MONITOR
IMPORTS Atom, CedarProcess, Commander, FS, G3dMappedAndSolidTexture, G3dMatrix, G3dRender, G3dRenderWithPixels, G3dShadeClipXfm, G3dShape, G3dSortandDisplay, G3dView, ImagerColorFns, ImagerColorMap, IO, NamedColors, Rope, RuntimeError, SafeStorage, ViewerIO, ViewerOps
EXPORTS G3dRender
~ BEGIN
Errors
Error:     PUBLIC SIGNAL [code: ATOM, reason: ROPE] = CODE;
Types
Miscellaneous Types
PropList:    TYPE ~ Atom.PropList;
STREAM:    TYPE ~ IO.STREAM;
ROPE:     TYPE ~ Rope.ROPE;
Process:    TYPE ~ CedarProcess.Process;
Imported Sequences
NatSequence:   TYPE ~ G3dBasic.NatSequence;
NatSequenceRep:  TYPE ~ G3dBasic.NatSequenceRep;
PairSequence:  TYPE ~ G3dBasic.PairSequence;
TripleSequence:  TYPE ~ G3dBasic.TripleSequence;
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
NatTable:    TYPE ~ G3dBasic.NatTable;
NatTableRep:   TYPE ~ G3dBasic.NatTableRep;
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;
Renamed Procedures
Transform: PROC [p: Triple, mat: Matrix] RETURNS [Triple] ~ G3dMatrix.Transform;
TransformVec: PROC [vec: Triple, mat: Matrix] RETURNS [Triple] ~ G3dMatrix.TransformVec;
GetProp: PROC [propList: PropList, prop: REF ANY] RETURNS [REF ANY]
~ Atom.GetPropFromList;
PutProp: PROC [propList: PropList, prop: REF ANY, val: REF ANY] RETURNS [PropList]
~ Atom.PutPropOnList;
Global Variables
registeredDisplayTypes:  PropList ← NIL;   -- keeps active display types
registeredSurfaceTypes:  PropList ← NIL;   -- keeps active surface types
registeredShadingClasses: PropList ← NIL;   -- keeps active shading classes
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 G3dRender.Error[$Unimplemented, "Unregistered display type"]
};
LoadDisplayClass: PUBLIC PROC [context: Context, type: ATOM] ~ {
class: REF ContextClass ← NARROW[GetProp[registeredDisplayTypes, type]];
IF class = NIL THEN G3dRender.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 {
G3dRender.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 => {
G3dRender.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 G3dRender.Error[$Unimplemented, "Unregistered surface type"];
};
LoadShapeClass: PUBLIC PROC [shape: Shape, type: ATOM ← $ConvexPolygon] ~{
shapeClass: REF ShapeClass;
class: REF ShapeClass ← NARROW[GetProp[registeredSurfaceTypes, type]];
IF class = NIL THEN G3dRender.Error[$Unimplemented, "Unregistered surface type"];
shapeClass ← ShapeClassFrom[shape];
IF shapeClass = NIL
THEN shapeClass ← NEW[ShapeClass ← class^]
ELSE {     -- inherit previous procedures if no replacements
new: REF ShapeClass ← NEW[ShapeClass ← class^];
IF new.validate = NIL THEN new.validate ← shapeClass.validate;
IF new.display = NIL THEN new.display ← shapeClass.display;
IF new.displayPatch = NIL THEN new.displayPatch ← shapeClass.displayPatch;
IF new.doBeforeFrame = NIL THEN new.doBeforeFrame ← shapeClass.doBeforeFrame;
shapeClass ← new;
};
IF ShadingClassFrom[shape] = NIL THEN 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
};
RegisterShadingClass: PUBLIC PROC [class: ShadingClass, type: ATOM] ~ {
registeredShadingClasses PutProp[registeredShadingClasses, type, NEW[ShadingClass 𡤌lass]];
};
GetShadingClass: PUBLIC PROC [type: ATOM] RETURNS [class: ShadingClass] ~ {
ref: REF ShadingClass ← NARROW[GetProp[registeredShadingClasses, type]];
IF ref # NIL
THEN class ← ref^
ELSE G3dRender.Error[$Unimplemented, "Unregistered shading class"];
};
LoadShadingClass: PUBLIC PROC [shape: Shape, type: ATOM ← $Default ] ~ {
shadingClass: REF ShadingClass;
class: REF ShadingClass ← NARROW[GetProp[registeredShadingClasses, type]];
IF class = NIL THEN G3dRender.Error[$Unimplemented, "Unregistered shading class"];
shadingClass ← ShadingClassFrom[shape];
IF shadingClass = NIL
THEN shadingClass ← NEW[ShadingClass ← class^]
ELSE {
new: REF ShadingClass ← NEW[ShadingClass ← class^];
IF new.renderMethod = NIL THEN new.renderMethod ← shadingClass.renderMethod;
new.color ← shadingClass.color;
new.shininess ← shadingClass.shininess;
new.transmittance ← shadingClass.transmittance;
IF new.texture = NIL THEN new.texture ← shadingClass.texture;
new.textureScale ← shadingClass.textureScale;
new.bumpScale ← shadingClass.bumpScale;
IF new.cnvrtVtx = NIL THEN new.cnvrtVtx ← shadingClass.cnvrtVtx;
IF new.getColor = NIL THEN new.getColor ← shadingClass.getColor;
IF new.shadeVtx = NIL THEN new.shadeVtx ← shadingClass.shadeVtx;
shadingClass ← new;
};
NARROW[shape.renderData, REF RenderData].shadingProps ← NIL;  -- WHY???
};
InitStandardShapeClasses: PROC[] ~ {  -- register procedures for basic surface types
standardClass: ShapeClass ← [
type: $Light,
display: NIL,
displayPatch: NIL
];
defaultShadingClass: ShadingClass ← [  -- procs for standard shading (no texture)
type: $Default,
renderMethod: $Faceted,
shadeVtx: G3dShadeClipXfm.ShadeVtx
];
G3dRender.RegisterShapeClass[standardClass, $Light]; -- placeholder class for light source
standardClass.type ← $ConvexPolygon;
standardClass.validate ← G3dSortandDisplay.ValidatePolyhedron;
standardClass.displayPatch ← G3dSortandDisplay.OutputPolygon;
G3dRender.RegisterShapeClass[standardClass, $ConvexPolygon]; -- ConvexPolygon procs
standardClass.type ← $RopeShape;
standardClass.validate ← G3dSortandDisplay.ValidateRopeShape;
standardClass.displayPatch ← G3dSortandDisplay.RopeDisplay;
G3dRender.RegisterShapeClass[standardClass, $RopeShape]; -- RopeShape procs
G3dRender.RegisterShadingClass[defaultShadingClass, $Default];
defaultShadingClass.type ← $NoShading;
defaultShadingClass.renderMethod ← $Faceted;
defaultShadingClass.shadeVtx ← G3dShadeClipXfm.NoShadeVtx;
G3dRender.RegisterShadingClass[defaultShadingClass, $NoShading];
};
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[BOOLEANFALSE];
RETURN[context];
};
InitializeRawColorDisplayContext: PUBLIC PROC [
antiAliasing: BOOLTRUE,
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]];
SetView[context, [3.0, -10.0, 3.0], []];
};
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 ← G3dRender.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]^ ];
G3dRender.RenderDataFrom[dstCtx.shapes[i]].shadingClass ← NEW[
ShadingClass ← G3dRender.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: REALIF 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;
};
Lighting
SetAmbientLight: PUBLIC PROC [context: Context, rgb: RGB] ~ {
IF context # NIL THEN {
ref: REF RGBNEW[RGB ← rgb];
context.environment ← PutProp[context.environment, $AmbientLight, ref];
context.changed ← TRUE;
};
};
NameAmbientLight: PUBLIC PROC [context: Context, color: ROPE] ~ {
clr: RGB ← ImagerColorFns.RGBFromHSL[NamedColors.RopeToHSL[color]];
SetAmbientLight[context, clr];
};
GetAmbientLight: PUBLIC PROC [context: Context] RETURNS [rgb: RGB] ~ {
IF context # NIL THEN {
refAny: REF ANY ← GetProp[context.environment, $AmbientLight];
rgb ← IF refAny = NIL
THEN [0.0, 0.0, 0.0]
ELSE NARROW[refAny, REF RGB]^;
};
};
AddLight: PUBLIC PROC [context: Context, name: ROPE, position: Triple, color: RGB [1,1,1]] ~ {
light: Shape ← FindShape[context, name];
IF light = NIL THEN {
light ← NEW [ShapeRep];    -- name not used before, make new light
light.name ← name;
LoadShapeClass[light, $Light];      -- load light class structures
AddShape[context, light];
};
light.position ← position;
light.sphereExtent ← [
center: [0.0, 0.0, 0.0],
radius: 2 * 93000000.0 * 1609.344 -- twice solar distance in meters
];
ShadingClassFrom[light].color ← color;
light.props ← PutProp[light.props, $Hidden, $ok]; -- hide from display routines
G3dShape.ComputeMatrix[light];      -- make sure matrix is valid
context.changed ← TRUE;
};
DeleteLight: PUBLIC PROC [context: Context, name: ROPE] ~ {
DeleteShape[context, name];
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: ROPENIL] ~ {
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];
};
KillBackground: 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;
DeleteShape[context, shape.name ! G3dRender.Error => CONTINUE];
context.shapes ← G3dShape.AddToShapeSequence[context.shapes, shape];
};
AddShapeFromFile: PUBLIC PROC [
context: Context,
shapeName: ROPE,
fileName: ROPE,
position: Triple ← [0., 0., 0.]]
~ {
shape, cloneShape: Shape ← NIL;
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 G3dShape.CloneShape[shape, cloneShape]
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];
};
ShapeFromRope: PUBLIC PROC [
name: ROPENIL,
message: ROPE,
color: ROPENIL,
size: REAL ← 0.5,
font: ROPENIL]
RETURNS [Shape] ~ {
shape: Shape ← NEW[ShapeRep];
renderData: REF RenderData ← NEW[RenderData];
renderData.fixedProps ← PutProp[renderData.fixedProps, $RopeMessage, message];
renderData.fixedProps ← PutProp[renderData.fixedProps, $RopeFont, font];
shape.renderData ← renderData;
LoadShapeClass[shape, $RopeShape];
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[NatTableRep[1]];
shape.surfaces[0] ← NEW[NatSequenceRep[3]];
shape.surfaces[0][0] ← shape.surfaces[0][2] ← 0; shape.surfaces[0][1] ← 1;
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].fixedProps ← PutProp[
NARROW[shape.renderData, REF RenderData].fixedProps, $RopeMessage, newMessage
];
};
DeleteShape: PUBLIC PROC [context: Context, shapeName: ROPE] ~ {
found: BOOLFALSE;
IF context.shapes = NIL THEN {
SIGNAL G3dRender.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 G3dRender.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
IF NARROW[context.shapes[n].renderData, REF RenderData].class.type # $Light
THEN DeleteShape[context, context.shapes[n].name ! G3dRender.Error => CONTINUE];
ENDLOOP;
};
SetRenderStyle: PUBLIC PROC [shape: Shape, renderStyle: RenderStyle] ~ {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade # NIL THEN shade.renderMethod ← NEW[RenderStyle ← renderStyle];
};
SetColor: PUBLIC PROC [shape: Shape, color: RGB] ~ {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade # NIL THEN shade.color ← color;
};
SetShininess: PUBLIC PROC [shape: Shape, shininess: REAL] ~ {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade # NIL THEN shade.shininess ← shininess;
};
SetTransmittance: PUBLIC PROC [shape: Shape, transmittance: REAL] ~ {
shade: REF ShadingClass ← ShadingClassFrom[shape];
IF shade # NIL THEN shade.transmittance ← transmittance;
};
SetInvisible: PUBLIC PROC [shape: Shape] ~ {
NARROW[shape.renderData, REF RenderData].props ← PutProp[
NARROW[shape.renderData, REF RenderData].props, $Hidden, $ok
];
};
SetVisible: PUBLIC PROC [shape: Shape] ~ {
NARROW[shape.renderData, REF RenderData].props ← Atom.RemPropFromList[
NARROW[shape.renderData, REF RenderData].props, $Hidden
];
};
ShowBackfaces: PUBLIC PROC [shape: Shape] ~ {
IF shape # NIL THEN shape.showBackfaces ← TRUE;
};
HideBackfaces: PUBLIC PROC [shape: Shape] ~ {
IF shape # NIL THEN shape.showBackfaces ← FALSE;
};
RenderDataFrom: PUBLIC PROC [shape: Shape] RETURNS [REF RenderData] ~ {
data: REF RenderData;
IF shape.renderData = NIL THEN shape.renderData ← NEW[RenderData];
data ← NARROW[shape.renderData];
RETURN[data];
};
ShapeClassFrom: PUBLIC PROC [shape: Shape] RETURNS [REF ShapeClass] ~ {
Return the shapeClass for this shape.
data: REF RenderData ← RenderDataFrom[shape];
IF data.class = NIL THEN data.class ← NEW[ShapeClass];
RETURN[data.class];
};
ShadingClassFrom: PUBLIC PROC [shape: Shape] RETURNS [REF ShadingClass] ~ {
Return the shadingClass for this shape.
data: REF RenderData ← RenderDataFrom[shape];
IF data.shadingClass = NIL THEN data.shadingClass ← NEW[ShadingClass];
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[data.patch];
};
Textures
SetTextureMap: PUBLIC PROC [
context: Context,
shapeName: ROPE,
aisName: ROPE,
textureStyle: TextureStyle ← intensity,
textureFiltering: BOOLFALSE]
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 G3dRender.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] ~ {
ps: PairSequence ← NARROW[GetProp[
NARROW[shape.renderData, REF RenderData].shadingProps,
$AuxiliaryVtxData
]];
FOR i: NAT IN [0..ps.length) DO
p: Pair ← ps[i];
ps[i] ← [p.x+offset.x, p.y+offset.y];
ENDLOOP;
};
GetTexture: PUBLIC PROC [shape: Shape] RETURNS [PairSequence] ~ {
RETURN[NARROW[GetProp[
NARROW[shape.renderData, REF RenderData].shadingProps,
$AuxiliaryVtxData
]]];
};
GetTextureInfo: PUBLIC PROC [s: Shape] RETURNS [textureInfo: LIST OF TextureInfo] ~ {
filtered: BOOLFALSE;
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 G3dRender.TextureMap => {
ref: REF ANY ← GetProp[texture.props, $FileName];
name: ROPEIF 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 G3dRender.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];
};
ScaleTexture: PUBLIC PROC [shape: Shape, scale: Pair] ~ {
textures: PairSequence ← GetTexture[shape];
IF textures # NIL THEN {
FOR n: NAT IN [0..textures.length) DO
texture: Pair ← textures[n];
textures[n] ← [texture.x*scale.x, texture.y*scale.y];
ENDLOOP;
SceneUtilities.SetTexture[shape, textures];
};
};
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
renderDone: CONDITION;
StartLog: PUBLIC PROC [context: Context] RETURNS[IO.STREAM] ~ {
log: IO.STREAMNARROW[GetProp[context.props, $Log]];
IF log = NIL THEN {
[out: log] ← ViewerIO.CreateViewerStreams[
name: "ThreeDWorld.log",
backingFile: PrependWorkingDirectory[context, "ThreeDWorld.log"]
];
context.props ← PutProp[context.props, $Log, log];
};
RETURN[ log ];
};
FlushLog: PUBLIC PROC [context: Context] ~ {
log: IO.STREAMNARROW[GetProp[context.props, $Log]];
IF log # NIL THEN IO.Flush[log];
};
CloseLog: PUBLIC PROC [context: Context] ~ {
log: IO.STREAMNARROW[GetProp[context.props, $Log]];
IF log # NIL THEN IO.Close[log];
context.props ← Atom.RemPropFromList[context.props, $Log]
};
Render: PUBLIC PROC [context: Context, fork: BOOLTRUE] ~ {
IF fork
THEN {
process: Process ← CedarProcess.Fork[ReallyRender, context, [background, TRUE]];
context.props ← PutProp[context.props, $Process, process];
}
ELSE [] ← ReallyRender[context];
};
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];
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: BOOLTRUE] ~ {
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.
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: ROPENARROW[GetProp[context.props, $WDir]];
IF wDir = NIL
THEN RETURN[file]
ELSE IF file = NIL OR (Rope.Index[s1: file, s2: "/"] > 0 AND Rope.Index[s1: file, s2: "["] > 0)  
THEN file ← Rope.Cat[wDir, file];  -- if first char not / or [then prepend wDir
RETURN[file];
};
TackOnExtension: PUBLIC PROC[file, extension: ROPE] RETURNS[ROPE] ~ {
cp: FS.ComponentPositions; fullFName, fName: ROPE;
[fullFName, cp, ] ← FS.ExpandName[file];
IF cp.ext.length = 0
THEN {
fName ← Rope.Substr[ fullFName, 0, cp.ext.start];
RETURN[ Rope.Cat[fName, ".", extension] ];
}
ELSE RETURN[ file ];
};
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"];
};
InitStandardShapeClasses[];
END.