RemoteMakeFrame:
PUBLIC
PROC[context:
REF ThreeDScenes.Context, numForksHint:
NAT ← 0] ~ {
Uses compute server to parcel out rendering to other processors
bufferContext: REF ThreeDScenes.Context;
log: IO.STREAM ← NARROW[ Atom.GetPropFromList[context.props, $Log] ];
shape: REF ThreeDScenes.ShapeSequence ← context.shapes;
numShapes: NAT ← shape.length - context.lights.length;
shapeOrder: REF NatSequence ← NEW[NatSequence[numShapes]];
startTime: REAL ← CurrentTime[];
IF log =
NIL
THEN log ← ThreeDMisc.StartLog[context];
-- open log file if not yet done
Get Everything (including light centroids) into eyespace
FOR i:
NAT
IN [0.. shape.length)
DO
IF Atom.GetPropFromList[shape[i].props, $Hidden] =
NIL
OR shape[i].type = $Light
THEN
IF shape[i].vtcesInValid
THEN {
shape[i].clipState ← ThreeDScenes.XfmToEyeSpace[context, shape[i]];
IF shape[i].clipState # out
THEN ThreeDScenes.XfmToDisplay[context, shape[i] ];
};
ENDLOOP;
Sort Objects and Parcel out to servers
numShapes ← SortAndForkProcs[context, shapeOrder, startTime, log, numForksHint];
Prepare for display
ThreeDScenes.FillViewPort[context, [0.0, 0.0, 0.0] ]; -- clear all pixel bits
ThreeDScenes.FillInBackGround[context]; -- load background
bufferContext ← ThreeDScenes.Create[
-- get buffer
width: Real.FixC[context.viewPort.w],
height: Real.FixC[context.viewPort.h],
renderMode: $FullClr,
alpha: TRUE
];
Display shapes back to front as soon as they complete
FOR i:
NAT
DECREASING
IN [0..numShapes)
DO
procList:
LIST
OF CedarProcess.Process ←
NARROW[ Atom.GetPropFromList[
context.shapes[shapeOrder[i]].props,
$RemoteProc
] ];
baseName: Rope.ROPE ← context.shapes[shapeOrder[i]].name;
baseScreenExtent: ThreeDScenes.Box ← context.shapes[shapeOrder[i]].screenExtent;
j: NAT ← 0;
FOR procs:
LIST
OF CedarProcess.Process ← procList, procs.rest
UNTIL procs =
NIL
DO
shapeName: Rope.ROPE ← Rope.Cat[baseName, Convert.RopeFromCard[j]];
results: REF;
msg: Rope.ROPE;
status: CedarProcess.Status;
[status, results] ← CedarProcess.Join[procs.first]; -- await termination
msg ← NARROW[results, Rope.ROPE];
SELECT status
FROM
done => {
doneTime: REAL ← CurrentTime[];
context.shapes[shapeOrder[i]].name ← shapeName;
DisplayRemoteResult[ context, bufferContext, context.shapes[shapeOrder[i]], log ];
msg ← Rope.Cat[msg, shapeName, " matted in ", ElapsedTime[doneTime] ];
log.PutRope[Rope.Cat[msg, " done at ", ElapsedTime[startTime], "\n" ] ];
};
ENDCASE => SIGNAL ThreeDScenes.Error[[$Condition, "Forked process failed"]];
j ← j + 1;
ENDLOOP;
context.shapes[shapeOrder[i]].name ← baseName;
context.shapes[shapeOrder[i]].screenExtent ← baseScreenExtent
ENDLOOP;
log.PutRope[Rope.Cat[" All done at ", ElapsedTime[startTime], "\n\n" ] ];
ThreeDMisc.FlushLog[context];
};
SortAndForkProcs:
PROC[context:
REF ThreeDScenes.Context, shapeOrder:
REF NatSequence,
startTime:
REAL, log:
IO.
STREAM, numForksHint:
NAT]
RETURNS [
NAT] ~ {
Sort Objects
costFactor: REF IntSequence ← NEW[IntSequence[context.shapes.length]];
totalCost, averageCost: INT ← 0;
shape: REF ThreeDScenes.ShapeSequence ← context.shapes;
shapesFound: NAT ← 0;
numShapes: NAT ← context.shapes.length - context.lights.length;
FOR i:
NAT
IN [0..context.shapes.length)
DO
IF shape[i] #
NIL
AND Atom.GetPropFromList[shape[i].props, $Hidden] =
NIL
THEN
IF shape[i].clipState # out
AND shape[i].surface #
NIL
THEN {
Estimate relative cost of scan conversion (untamed heuristics here)
costFactor[i] ← INT[(shape[i].screenExtent.right - shape[i].screenExtent.left)]
* (shape[i].screenExtent.top - shape[i].screenExtent.bottom);
IF ThreeDScenes.GetShading[shape[i], $Transmittance] #
NIL
AND shape[i].insideVisible
THEN costFactor[i] ← costFactor[i] * 2;
IF ThreeDScenes.GetShading[ shape[i], $TextureMap ] #
NIL
THEN costFactor[i] ← costFactor[i] * 2;
IF ThreeDScenes.GetShading[ shape[i], $ShadingProcs ] #
NIL
THEN costFactor[i] ← costFactor[i] * 2; -- may be much higher
Bubble sort shapes, numShapes assumed quite small
FOR j:
NAT
DECREASING
IN [0..shapesFound)
DO
IF shape[i].centroid.ez < shape[shapeOrder[j]].centroid.ez
THEN { shapeOrder[j+1] ← shapeOrder[j]; shapeOrder[j] ← i; }
ELSE { shapeOrder[j+1] ← i; EXIT; };
ENDLOOP;
IF shapesFound = 0 THEN shapeOrder[0] ← i;
shapesFound ← shapesFound + 1;
};
ENDLOOP;
log.PutRope[Rope.Cat["\nObject transform and sort: ", ElapsedTime[startTime], "\n" ]];
Find average scan conversion cost
FOR i: NAT IN [0..shapesFound) DO totalCost ← totalCost + costFactor[shapeOrder[i]]; ENDLOOP;
averageCost ← totalCost / shapesFound;
IF shapesFound < numForksHint
-- allows division of single-object scenes
THEN averageCost ← averageCost / (numForksHint / shapesFound); -- note DIV fn
Parcel out shapes to servers
FOR i:
NAT
DECREASING
IN [0..shapesFound)
DO
-- do back to front
wholeWindow: Imager.Rectangle ← context.window;
wholeViewPort: Imager.Rectangle ← context.viewPort;
baseName: Rope.ROPE ← context.shapes[shapeOrder[i]].name;
screenExtent: ThreeDScenes.Box ← context.shapes[shapeOrder[i]].screenExtent;
procList: LIST OF CedarProcess.Process ← NIL;
imageCount: NAT ← Real.RoundC[ Real.Float[ costFactor[shapeOrder[i]] ] / averageCost ];
IF imageCount < 1 THEN imageCount ← 1;
FOR j:
NAT
IN [0..imageCount)
DO
cmd: Rope.ROPE;
Set Window to clip object apart
viewPortSize: REAL ← MAX[wholeViewPort.w, wholeViewPort.h];
vuPtWindow: Imager.Rectangle ← [
x: MAX[ 0.0, wholeViewPort.h - wholeViewPort.w ] / viewPortSize,
y: MAX[ 0.0, wholeViewPort.w - wholeViewPort.h ] / viewPortSize,
w: 1.0,
h: 1.0
];
left: REAL ← screenExtent.left
+ (Real.Float[j] / imageCount) * (screenExtent.right - screenExtent.left);
right: REAL ← screenExtent.left
+ (Real.Float[j+1] / imageCount) * (screenExtent.right - screenExtent.left);
top: REAL ← screenExtent.top;
bottom: REAL ← screenExtent.bottom;
left ← left - 1; -- add one pixel border on left
right ← right + 1; -- add one pixel border on right
ThreeDScenes.SetWindow[ context, [
x: (2.0 * left / viewPortSize) - 1.0 + vuPtWindow.x, -- window range is -1.0 — +1.0
y: (2.0 * bottom / viewPortSize) - 1.0 + vuPtWindow.y,
w: 2.0 * (right - left) / viewPortSize,
h: 2.0 * (top - bottom) / viewPortSize
] ];
ThreeDScenes.SetViewPort[ context, [left, bottom, right - left, top - bottom]];
context.shapes[shapeOrder[i]].name ← Rope.Cat[ baseName, Convert.RopeFromCard[j] ];
Build Command rope
cmd ← RopeFromShapeInstance[ context, context.shapes[shapeOrder[i]] ];
Fork new Process
procList ← CONS[ CedarProcess.Fork[StartRemote, cmd ], procList]; -- fork process
ENDLOOP;
context.shapes[shapeOrder[i]].props ← Atom.PutPropOnList[
-- store proc refs with shape
context.shapes[shapeOrder[i]].props,
$RemoteProc,
procList -- leave list of proc refs here
];
context.shapes[shapeOrder[i]].name ← baseName;
ThreeDScenes.SetWindow[ context, wholeWindow];
ThreeDScenes.SetViewPort[ context, wholeViewPort];
ENDLOOP;
RETURN[shapesFound];
};
DisplayRemoteResult:
PROC[context, bufferContext:
REF ThreeDScenes.Context,
shape:
REF ThreeDScenes.ShapeInstance, log:
IO.
STREAM] ~ {
width, height: NAT;
xOffset: NAT ← shape.screenExtent.left -1;
yOffset: NAT ← shape.screenExtent.bottom;
[width, height] ← AISAnimation.GetAIS[
-- read image into buffer
context: bufferContext,
fileRoot: ThreeDMisc.PrependWorkingDirectory[context, Rope.Cat[shape.name, ".ais"] ],
xOffset: xOffset,
yOffset: yOffset,
center: FALSE
! RuntimeError
.UNCAUGHT => {
log.PutRope[Rope.Cat[" Failed to load - ", shape.name, "\n"] ];
GO TO GiveUp;
}
];
shape.screenExtent.left ← shape.screenExtent.left + width -2; -- for next slice (minus border)
Pixels.Copy[
-- write over previous objects, etc.
destination: context.display,
source: bufferContext.display,
destArea: [xOffset+1, yOffset, width-2, height],
srcArea: [xOffset+1, yOffset, width-2, height],
op: $WriteOver
];
EXITS GiveUp => NULL
};
RopeFromShapeInstance:
PROC[ context:
REF ThreeDScenes.Context,
shape:
REF ThreeDScenes.ShapeInstance ]
RETURNS[ Rope.
ROPE ] ~ {
Vec3toRope:
PROC[ r1, r2, r3:
REAL]
RETURNS[Rope.
ROPE] ~ {
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;
rope ← Rope.Cat[ " ", Convert.RopeFromReal[r1], " ", Convert.RopeFromReal[r2], " " ];
rope ← Rope.Cat[ rope, Convert.RopeFromReal[r3], " ", Convert.RopeFromReal[r4], " " ];
RETURN[ rope ];
};
ref: REF ANY ← NIL;
shadingType: ATOM ← NIL;
color: REF RGB ← NIL;
shininess, transmittance: REF REAL ← NIL;
texture: REF TextureMaps.TextureMap ← NIL;
getVtxProc: ThreeDScenes.VtxToRealSeqProc ← NIL;
getColorProc: ScanConvert.GetColorProc ← NIL;
xfm: Matrix3d.Matrix ← shape.position;
clip: ARRAY ThreeDScenes.SixSides OF Vector3d.Quad ← context.clippingPlanes;
cmd: Rope.
ROPE ←
" ";
File Name and Surface Type
cmd ← Rope.Cat[cmd, " Shape: ", shape.fileName, " ", Atom.GetPName[shape.type]];
IF shape.insideVisible
THEN cmd ← Rope.Cat[cmd, " TRUE "]
ELSE cmd ← Rope.Cat[cmd, " FALSE "];
Color
color ← NARROW[ThreeDScenes.GetShading[ shape, $Color ], REF RGB];
shadingType ← NARROW[ThreeDScenes.GetShading[ shape, $Type ], ATOM];
IF shadingType = $Smooth
AND color #
NIL
THEN cmd ← Rope.Cat[cmd, " SmoothColor: ", Vec3toRope[color.R, color.G, color.B] ];
IF shadingType = $Faceted
AND color #
NIL
THEN cmd ← Rope.Cat[cmd, " FacetedColor: ", Vec3toRope[color.R, color.G, color.B] ];
Transmittance
transmittance ← NARROW[ThreeDScenes.GetShading[ shape, $Transmittance ], REF REAL ];
IF transmittance #
NIL
THEN cmd ← Rope.Cat[cmd, " Transmittance: ", Convert.RopeFromReal[transmittance^] ];
Shininess
shininess ← NARROW[ThreeDScenes.GetShading[ shape, $Shininess ], REF REAL ];
IF shininess #
NIL
THEN cmd ← Rope.Cat[cmd, " Shininess: ",
Convert.RopeFromReal[shininess^] ];
Mapped Texture
texture ← NARROW[ ThreeDScenes.GetShading[ shape, $TextureMap ],
REF TextureMaps.TextureMap ];
IF texture #
NIL
THEN {
fileName: Rope.ROPE ← NARROW[ Atom.GetPropFromList[texture.props, $FileName] ];
coordType: ATOM ← NARROW[ Atom.GetPropFromList[texture.props, $CoordType] ];
coords: REF ← Atom.GetPropFromList[texture.props, $Coords];
IF coordType = NIL THEN coordType ← $NoCoords;
cmd ← Rope.Cat[cmd, " MapTexture: ", fileName, " ", Atom.GetPName[texture.type] ];
cmd ← Rope.Cat[cmd, " ", Atom.GetPName[coordType], " " ];
SELECT coordType
FROM
$FromVtxNos, $FromNormals => {
argList: LIST OF REAL ← NARROW[coords];
WHILE argList #
NIL
DO
cmd ← Rope.Cat[cmd, Convert.RopeFromReal[argList.first], " " ];
argList ← argList.rest;
ENDLOOP;
};
$File => cmd ← Rope.Cat[cmd, " ", NARROW[coords, Rope.ROPE] ];
ENDCASE => SIGNAL ThreeDScenes.Error[[$MisMatch, "Bad texture coordinate type"]];
};
Solid Texture
ref ← ThreeDScenes.GetShading[ shape, $ShadingProcs];
IF ref #
NIL
THEN {
procName: Rope.ROPE;
[getVtxProc, getColorProc] ← NARROW[ ref, REF ThreeDScenes.ShadingProcs ]^;
IF getColorProc #
NIL
THEN {
procName ← SolidTextures.ProcToRope[getColorProc];
cmd ← Rope.Cat[cmd, " SolidTexture: ", procName, " " ];
};
};
Light Source
FOR i:
NAT
IN [0..context.lights.length)
DO
color ← NARROW[ThreeDScenes.GetShading[ context.lights[i], $Color ], REF RGB];
cmd ← Rope.Cat[cmd, " Light: ", context.lights[i].name,
Vec3toRope[
context.lights[i].location.x, context.lights[i].location.y, context.lights[i].location.z ],
Vec3toRope[color.R, color.G, color.B]
];
ENDLOOP;
Shape Transform
cmd ← Rope.Cat[cmd, " ShapeXfm: ",
Vec4toRope[xfm[0][0], xfm[0][1], xfm[0][2], xfm[0][3]],
Vec4toRope[xfm[1][0], xfm[1][1], xfm[1][2], xfm[1][3]] ];
cmd ← Rope.Cat[cmd,
Vec4toRope[xfm[2][0], xfm[2][1], xfm[2][2], xfm[2][3]],
Vec4toRope[xfm[3][0], xfm[3][1], xfm[3][2], xfm[3][3]] ];
View Parameters
cmd ← Rope.Cat[cmd, " View: ",
Vec3toRope[context.eyePoint.x, context.eyePoint.y, context.eyePoint.z],
Vec3toRope[context.ptOfInterest.x, context.ptOfInterest.y, context.ptOfInterest.z],
Vec3toRope[context.upDirection.x, context.upDirection.y, context.upDirection.z]
];
cmd ← Rope.Cat[cmd,
Vec4toRope[ context.rollAngle, context.fieldOfView, context.hitherLimit, context.yonLimit ]
];
Window
cmd ← Rope.Cat[cmd, " Window: ",
Vec4toRope[context.window.x, context.window.y, context.window.w, context.window.h]
];
ViewPort
cmd ← Rope.Cat[cmd, " ViewPort: ",
-- ViewPor
t
Vec4toRope[context.viewPort.x, context.viewPort.y, context.viewPort.w, context.viewPort.h] ];
AIS result file name root
cmd ← Rope.Cat[cmd, " ResultFile: ",
ThreeDMisc.PrependWorkingDirectory[context, Rope.Cat[shape.name, ".ais"] ] ];
RETURN[cmd];
};