G3dToolRIBImpl.mesa
Copyright © 1991 by Xerox Corporation. All rights reserved.
Bloomenthal, March 8, 1993 4:41 pm PST
3d Tool: Renderman Rendering
DIRECTORY CedarProcess, CommanderOps, Controls, CtMap, CtViewer, FS, G3dBasic, G3dLight, G3dMatrix, G3dRender, G3dShape, G3dTool, G3dVector, G3dView, Imager, ImagerColor, ImagerPixel, ImagerPixelArray, ImagerSample, IO, MessageWindow, Real, RefText, Rope, SF, UnixSpawnTCP, UnixSysCalls, UnixTypes, UXStrings, ViewerClasses;
G3dToolRIBImpl: CEDAR PROGRAM
IMPORTS CedarProcess, CommanderOps, Controls, CtMap, CtViewer, FS, G3dMatrix, G3dRender, G3dTool, G3dVector, G3dView, Imager, ImagerColor, ImagerPixel, ImagerPixelArray, ImagerSample, IO, MessageWindow, Real, RefText, Rope, SF, UnixSpawnTCP, UnixSysCalls, UXStrings
EXPORTS G3dTool
~ BEGIN
Triple:   TYPE ~ G3dBasic.Triple;
SpawnData:  TYPE ~ G3dTool.SpawnData;
Tool:    TYPE ~ G3dTool.Tool;
WriteProc:  TYPE ~ G3dTool.WriteProc;
SampleMap:  TYPE ~ ImagerSample.SampleMap;
SampleBuffer: TYPE ~ ImagerSample.SampleBuffer;
FD:    TYPE ~ UnixTypes.FileDescriptor;
CHARPtr:   TYPE ~ UnixTypes.CHARPtr;
ROPE:    TYPE ~ Rope.ROPE;
RIB From Render Tool
Note: the Unix display driver written in C is in /usr/local/prman/etc/dspyinst/;
after editing d¬cedar.c, then, in a Shell window: make install
Render: PUBLIC PROC [tool: Tool, fork: BOOL ¬ TRUE] ~ {
IF tool.shapes = NIL OR tool.shapes.length = 0
THEN Blink["no shapes to render!"]
ELSE [] ¬ CedarProcess.Fork[ForkRender, tool, [normal, TRUE]];
};
WriteRIBtoFile: PUBLIC PROC [tool: Tool, fileName: ROPE] ~ {
IF fileName # NIL THEN WriteRIBtoStream[tool, FS.StreamOpen[fileName, $create]];
};
WriteRIBtoStream: PUBLIC PROC [tool: Tool, out: IO.STREAM] ~ {
IF out = NIL
THEN Blink["can't create file"]
ELSE {
Write: WriteProc ~ {IO.PutRope[out, rope]};
ToolToRIB[tool, Write];
IO.Close[out];
};
};
ToolToRIB: PROC [t: Tool, write: WriteProc] ~ {
moves: Triple ¬ [t.camera.par.xMov.value, t.camera.par.yMov.value, t.camera.par.zMov.value];
rots: Triple ¬ [t.camera.par.xRot.value, t.camera.par.yRot.value, t.camera.par.zRot.value];
scale: REAL ¬ t.camera.scale.value;
fov: REAL ¬ t.camera.fieldOfView.value;
lights: G3dLight.LightSequence ¬ t.lights;
comment: ROPE ¬ IO.PutFR1["(from %g)", IO.rope[t.name]];
imageName: ROPE ¬ IF t.ribMode = file THEN Rope.Concat[t.directory, "Temp.tiff"] ELSE NIL;
ToRIB[imageName, t.frameSize, t.color, scale, fov, moves, rots, t.shapes, lights, write, comment];
};
DoCommand: PROC [t: Tool, command, status: ROPE] ~ {
Controls.TypescriptWrite[t.typescript, status];
[] ¬ CommanderOps.DoCommand[command, t.cmd];
};
RenderOpsButton: PUBLIC ViewerClasses.ClickProc ~ {
t: Tool ¬ NARROW[clientData];
IF mouseButton = red
THEN Render[t]
ELSE {
choice: INT ¬ Controls.PopUpRequest[["Render Ops"], LIST[
-- 1 -- ["Size", IO.PutFR["now %g by %g", IO.int[t.frameSize.x],IO.int[t.frameSize.y]]],
-- 2 -- IF t.color THEN ["Color", "change to BW"] ELSE ["BW", "change to color"],
-- 3 -- IF t.ribMode = file
  THEN ["FS", "change to Spawn"]
  ELSE ["Spawn", "change to File System"]]];
G3dTool.SetCursor[t, TRUE];
SELECT choice FROM
1 => t.frameSize ¬ Controls.GetIntegerPair[t.typescript, "frame size", t.frameSize];
2 => t.color ¬ NOT t.color;
3 => t.ribMode ¬ IF t.ribMode = file THEN spawn ELSE file;
ENDCASE;
G3dTool.SetCursor[t, FALSE];
};
};
RenderButton: PUBLIC ViewerClasses.ClickProc ~ {Render[NARROW[clientData]]};
Rendering
ForkRender: CedarProcess.ForkableProc ~ {
t: Tool ¬ NARROW[data];
We note, dispiritedly, the following timings for a 5000 point, 5000 polygon shape:
 Transmission of RIB data to the spawned Unix render command: 1:30
 Execution of spawned render command: 1:25
  Total render time using Spawn: 2:55
 Writing the RIB file: 0:25
 Execution of render command using RIB file: 1:50
  Total render time not using Spawn: 2:15
However, using Spawn has the advantage of seeing the image as it is rendered.
G3dTool.StartTimer[t];
IF t.ribMode = spawn
THEN RenderViaUnixSpawn[t]
ELSE RenderViaFileSystem[t];
};
RenderViaFileSystem: PROC [t: Tool] ~ {
command: ROPE ¬ IO.PutFR1["render %gTemp.rib", IO.rope[t.directory]];
Controls.TypescriptWrite[t.typescript, "writing RIB... "];
WriteRIBtoFile[t, IO.PutFR1["%gTemp.rib", IO.rope[t.directory]]];
t.unixSpawn ¬ UnixSpawnTCP.Spawn[command, t.cmd.out, t.cmd.out, Done, ReadOut, t];
Controls.TypescriptWrite[t.typescript, "rendering... "];
};
RenderViaUnixSpawn: PROC [t: Tool] ~ {
Write: WriteProc ~ {
IF t.unixSpawn # NIL
THEN UnixSpawnTCP.Write[t.unixSpawn, rope]
ELSE RETURN[FALSE];
};
Controls.TypescriptWrite[t.typescript, "sending RIB... "];
IF (t.unixSpawn ¬ UnixSpawnTCP.Spawn["render", t.cmd.out, t.cmd.out, Done, ReadOut, t])=NIL
THEN Blink["can't spawn!"]
ELSE ToolToRIB[t, Write];
Controls.TypescriptWrite[t.typescript, "done!"];
};
ReadOut presumes this format (generated by /usr/local/prman/etc/dspyinst/d¬cedar.c):
1 ASCII integer (starting x location of window)
1 ASCII integer (starting y location of window)
1 ASCII integer (width of window)
1 ASCII integer (height of window)
1 carriage-return
width*height groups of:
1 byte (red)
1 byte (green)
1 byte (blue)
ViewerData: TYPE ~ RECORD [x, y, w, h: INTEGER, bwMap: SampleMap, size: SF.Vec, t: Tool];
Update: CtViewer.ViewerProc ~ { -- can not be nested
u: REF ViewerData ¬ NARROW[clientData];
ImagerSample.BasicTransfer[maps[0].map, u.bwMap, [u.y, u.x], [0, 0], [u.h, u.w]];
affectedRegion ¬ [[u.y, u.x], [u.y+u.h, u.x+u.w]];
};
ShowColor: CtViewer.ViewerProc ~ { -- can not be nested
u: REF ViewerData ¬ NARROW[clientData];
context: Imager.Context ¬ CtBasic.ContextFromSampleMaps[maps];
pm: ImagerPixel.PixelMap ¬ ImagerPixel.MakePixelMap[u.t.rMap, u.t.gMap, u.t.bMap];
pa: ImagerPixelArray.PixelArray ¬
ImagerPixelArray.FromPixelMap[pm, [[0, 0], u.size], [down, right]];
op: ImagerColor.ColorOperator ¬ ImagerColor.NewColorOperatorRGB[255];
Imager.DrawSampledColor[context, pa, op];
affectedRegion.max ¬ u.size;
};
ReadOut: CedarProcess.ForkableProc ~ {
With thanks to Michael Plass.
Done: ERROR = CODE;
CheckMap: PROC [map: SampleMap] RETURNS [ret: SampleMap] ~ {
IF (ret ¬ map) = NIL OR ImagerSample.GetSize[ret] # u.size THEN {
IF map # NIL THEN ImagerSample.ReleaseScratchMap[map];
ret ¬ ImagerSample.ObtainScratchMap[[[0, 0], u.size], 8];
};
};
windowS: IO.STREAM;
u: REF ViewerData ¬ NEW[ViewerData];
s: REF SpawnData ¬ NARROW[data];
t: Tool ¬ u.t ¬ NARROW[s.clientData];
x, y, w, h, nBytes, charsIndex: INTEGER ¬ 0;
v: ViewerClasses.Viewer ¬ CtViewer.currentViewer
¬ CtViewer.GetViewer[Rope.Concat[t.name, ": Image"], right, t.frameSize.y, TRUE];
window: REF TEXT ¬ RefText.ObtainScratch[100];
chars: CHARPtr ¬ UXStrings.ObtainScratch[1024];
rLine, gLine, bLine, bwLine: SampleBuffer;
u.size ¬ SF.Min[[v.ch, v.cw], [t.frameSize.y, t.frameSize.x]];
u.bwMap ¬ ImagerSample.ObtainScratchMap[[[0, 0], [100, 100]], 8];
bwLine ¬ ImagerSample.ObtainScratchSamples[100];
IF t.color THEN {
rLine ¬ ImagerSample.ObtainScratchSamples[100];
gLine ¬ ImagerSample.ObtainScratchSamples[100];
bLine ¬ ImagerSample.ObtainScratchSamples[100];
t.rMap ¬ CheckMap[t.rMap];
t.gMap ¬ CheckMap[t.gMap];
t.bMap ¬ CheckMap[t.bMap];
};
CtMap.Gamma[];
DO TRUSTED {
ENABLE Done => EXIT;
Next: UNSAFE PROC RETURNS [char: CHAR] ~ UNCHECKED {
IF charsIndex >= nBytes THEN {
IF (nBytes ¬ UnixSysCalls.Read[s.fdOut, chars, 1023]) <= 0 THEN ERROR Done;
charsIndex ¬ 0;
};
char ¬ chars[charsIndex];
charsIndex ¬ charsIndex+1;
};
ch: CHAR ¬ 0c;
window.length ¬ 0;
WHILE (ch ¬ Next[]) # '\n DO
window ¬ RefText.AppendChar[window, ch];
ENDLOOP;
windowS ¬ IO.TIS[window];
x ¬ u.x ¬ IO.GetInt[windowS];
y ¬ u.y ¬ IO.GetInt[windowS];
IF (w ¬ IO.GetInt[windowS])+x > v.cw THEN w ¬ v.cw-x;
IF (h ¬ IO.GetInt[windowS])+y > v.ch THEN h ¬ v.ch-y;
IF x >= v.cw THEN LOOP;
IF y >= v.ch THEN EXIT;
IF w+x > v.cw THEN w ¬ v.cw-x;
IF h+y > v.ch THEN h ¬ v.ch-y;
u.w ¬ w;
u.h ¬ h;
FOR j: INT IN [0..h) DO
FOR i: INT IN [0..w) DO
r: BYTE ~ ORD[Next[]];
g: BYTE ~ ORD[Next[]];
b: BYTE ~ ORD[Next[]];
IF t.color THEN {rLine[i] ¬ r; gLine[i] ¬ g; bLine[i] ¬ b};
bwLine[i] ¬ MIN[255, MAX[0, Real.Round[.3*REAL[r]+.59*REAL[g]+.11*REAL[b]]]];
ENDLOOP;
ImagerSample.PutSamples[u.bwMap, [j, 0],, bwLine, 0, w];
IF t.color THEN {
ImagerSample.PutSamples[t.rMap, [y+j, x],, rLine, 0, w];
ImagerSample.PutSamples[t.gMap, [y+j, x],, gLine, 0, w];
ImagerSample.PutSamples[t.bMap, [y+j, x],, bLine, 0, w];
};
ENDLOOP;
CtViewer.DoWithViewer[v, Update, u];
CedarProcess.CheckAbort[];
};
ENDLOOP;
IF t.color THEN {CtMap.Dither[]; CtViewer.DoWithViewer[v, ShowColor, t]};
RefText.ReleaseScratch[window];
UXStrings.ReleaseScratch[chars];
FOR l: LIST OF SampleBuffer ¬ LIST[rLine, gLine, bLine, bwLine], l.rest WHILE l # NIL DO
IF l.first # NIL THEN ImagerSample.ReleaseScratchSamples[l.first];
ENDLOOP;
ImagerSample.ReleaseScratchMap[u.bwMap];
IF t.ribMode = file THEN {
DoCommand[t, IO.PutFR["! tiffcopy -none -noalpha %gTemp.tiff %gTemp.dump", IO.rope[t.directory], IO.rope[t.directory]], "tiffcopy... "];
DoCommand[t, IO.PutFLR["Ct DumpToAIS %gTemp.dump %gTemp -gbr -size %g %g", LIST[IO.rope[t.directory], IO.rope[t.directory], IO.int[t.frameSize.x], IO.int[t.frameSize.y]]], "to AIS... "];
DoCommand[t, IO.PutFR["Ct View %gTemp%g -right",
IO.rope[t.directory], IO.rope[IF t.color THEN NIL ELSE "-red.ais"]], "display "];
Could try these Unix commands:
tiffcopy -noalpha Temp.tiff
source /import/sandpiper/top/enable; readtif Temp.tiff | writeais -dir AIS
mv AIS/r.ais Temp-red.ais; mv AIS/g.ais Temp-grn.ais; mv AIS/b.ais Temp-blu.ais
};
};
Done: UnixSpawnTCP.FinishProc ~ {
t: Tool ¬ NARROW[clientData];
G3dTool.TSWrite[t, IO.PutFR1["(%g secs)", IO.real[G3dTool.ElapsedTime[t]]]];
UnixSpawnTCP.Close[t.unixSpawn];
t.unixSpawn ¬ NIL;
IF status = $NotFound THEN Blink["can't render (Renderman unavailable on this machine?)"]
};
Renderman Format
MatrixFromCamera: PUBLIC PROC [scale: REAL, moves, rotates: Triple, leftHanded: BOOL]
RETURNS [m: G3dMatrix.Matrix]
~ { -- camera transformation for Renderman renderer, courtesy Dan McCoy, PIXAR
from, at, up, camx, camy, camz: Triple;
m ¬ G3dMatrix.Identity[];
[from, at, up] ¬ G3dView.FromScaleMovesRots[scale, moves, rotates];
camz ¬ G3dVector.Unit[G3dVector.Sub[at, from]];
camy ¬ G3dVector.Unit[up];
IF leftHanded
THEN {
camx ¬ G3dVector.Unit[G3dVector.Cross[camy, camz]];
camy ¬ G3dVector.Unit[G3dVector.Cross[camz, camx]];
}
ELSE {
camx ¬ G3dVector.Unit[G3dVector.Cross[camz, camy]];
camy ¬ G3dVector.Unit[G3dVector.Cross[camx, camz]];
};
m[0] ¬ [scale*camx.x, scale*camy.x, scale*camz.x, 0.0];
m[1] ¬ [scale*camx.y, scale*camy.y, scale*camz.y, 0.0];
m[2] ¬ [scale*camx.z, scale*camy.z, scale*camz.z, 0.0];
m[3] ¬ [-G3dVector.Dot[from,camx],-G3dVector.Dot[from,camy],-G3dVector.Dot[from,camz],1];
};
ToRIB: PUBLIC PROC [
imageName: ROPE ¬ NIL,
frameSize: G3dBasic.IntegerPair,
color: BOOL,
scale, fieldOfView: REAL,
moves, rotates: Triple,
shapes: G3dShape.ShapeSequence,
lights: G3dLight.LightSequence,
writer: WriteProc,
comment: ROPE ¬ NIL]
~ {
abort: ERROR = CODE;
Inner: PROC ~ {
Write: PROC [rope: ROPE] ~ {IF NOT writer[rope] THEN ERROR abort};
WriteMatrix: PROC [m: G3dMatrix.Matrix, concat: BOOL] ~ {
Write[IO.PutFR1["%gTransform [\n", IO.rope[IF concat THEN "Concat" ELSE NIL]]];
FOR i: INTEGER IN [0..4) DO
Write[IO.PutFLR["\t\t%g %g %g %g%g\n", LIST[
IO.real[m[i][0]], IO.real[m[i][1]], IO.real[m[i][2]], IO.real[m[i][3]],
IO.rope[IF i = 3 THEN "]" ELSE NIL]]]];
ENDLOOP;
};
WriteShape: PROC [s: G3dShape.Shape] ~ {
Renderman does not do well with non-planar polygons, so we triangulate here.
Current Renderman does not accept a vertex index array greater than 32K in length.
GetStop: PROC [startPoly: INTEGER] RETURNS [stopPoly, startVert, stopVert: INTEGER] ~ {
num: INTEGER ¬ 0;
startVert ¬ stopVert ¬ s.surfaces[startPoly].vertices[0];
FOR stopPoly ¬ startPoly, stopPoly+1 WHILE stopPoly < INTEGER[s.surfaces.length] DO
p: G3dBasic.NatSequence ¬ s.surfaces[stopPoly].vertices;
IF (num ¬ num+3*(p.length-2)-- incr by # triangle vertices --) > 30000 THEN EXIT;
FOR i: INTEGER IN [0..p.length) DO -- find vertex id bounds
id: INTEGER ¬ p[i];
IF id < startVert THEN startVert ¬ id ELSE IF id > stopVert THEN stopVert ¬ id;
ENDLOOP;
ENDLOOP;
stopPoly ¬ stopPoly-1; -- range is [startPoly..stopPoly]
};
WriteSubShape: PROC [startPoly, stopPoly, startVert, stopVert: INTEGER] ~ {
WritePoints: PROC [i0, i1: INTEGER, op: {pos, nrml, color}] ~ {
Write[SELECT op FROM pos => "\"P\" [", nrml => "\"N\" [", ENDCASE => "\"Cs\" ["];
FOR i: INTEGER IN [i0..i1] DO
v: G3dShape.Vertex ¬ s.vertices[i];
t: Triple ¬ SELECT op FROM pos => v.point, nrml => v.normal, ENDCASE => v.color;
Write[IO.PutFR["%g %g %g ", IO.real[t.x], IO.real[t.y], IO.real[t.z]]];
ENDLOOP;
Write["]\n"];
};
Write["AttributeBegin\n"];
Write[IO.PutFR1["Sides %g\n", IO.int[IF s.showBackfaces THEN 2 ELSE 1]]];
Write["ShadingRate 1\n"];
Write["ShadingInterpolation \"constant\"\n"];
Write["Color [1.0 1.0 1.0]\n"];
Write["Surface \"metal\" \"Ks\" 0.8 \"Ka\" 0.2 \"roughness\" 0.5\n"];
Write[IO.PutFR["Opacity [%g %g %g]\n",
IO.real[opacity], IO.real[opacity], IO.real[opacity]]];
WriteMatrix[s.matrix, FALSE];
Write["PointsPolygons\n"];
Write["["];
FOR i: INTEGER IN [startPoly..stopPoly] DO
FOR j: INT IN [0..s.surfaces[i].vertices.length-2) DO Write["3 "]; ENDLOOP; -- triangle
ENDLOOP;
Write["]\n["];
FOR i: INTEGER IN [startPoly..stopPoly] DO -- vertex index array (< 30000)
p: G3dBasic.NatSequence ¬ s.surfaces[i].vertices;
FOR j: INTEGER IN [1..p.length-2] DO -- triangulate
Write[IO.PutFR["%g %g %g ", -- vertices referenced from 0, direction reversed:
IO.int[p[j+1]-startVert],
IO.int[p[j]-startVert],
IO.int[p[0]-startVert]]];
ENDLOOP;
ENDLOOP;
Write["]\n"];
WritePoints[startVert, stopVert, pos];
WritePoints[startVert, stopVert, nrml];
IF color AND hasColor THEN WritePoints[startVert, stopVert, color];
Write["AttributeEnd\n"];
};
renderData: G3dRender.RenderData ¬ G3dRender.RenderDataFrom[s];
opacity: REAL ¬ 1.0-renderData.transmittance;
hasColor: BOOL ¬ FALSE;
startPoly, stopPoly, startVert, stopVert: INTEGER ¬ 0;
FOR i: INTEGER IN [0..s.vertices.length) DO
v: G3dShape.Vertex ¬ s.vertices[i];
IF v.color.x NOT IN[.99..1.01] OR v.color.y NOT IN[.99..1.01] OR v.color.z NOT IN[.99..1.01]
THEN {hasColor ¬ TRUE; EXIT};
ENDLOOP;
Write[IO.PutFR["# %g: %g polygons, %g vertices\n",
IO.rope[s.name], IO.int[s.surfaces.length], IO.int[s.vertices.length]]];
DO
[stopPoly, startVert, stopVert] ¬ GetStop[startPoly];
WriteSubShape[startPoly, stopPoly, startVert, stopVert];
IF stopPoly = INTEGER[s.surfaces.length]-1 THEN EXIT;
startPoly ¬ stopPoly+1;
ENDLOOP;
};
WriteLight: PROC [l: G3dLight.Light, id: INTEGER] ~ {
to: Triple ¬ G3dVector.Add[l.position, l.direction];
Write[IO.PutFLR["LightSource \"distantlight\" %g \"lightcolor\" [%g %g %g] \"intensity\" [1.0] \"from\" [%g %g %g] \"to\" [%g %g %g]\n", LIST[IO.int[id], IO.real[l.color.R], IO.real[l.color.G], IO.real[l.color.B], IO.real[l.position.x], IO.real[l.position.y], IO.real[l.position.z], IO.real[to.x], IO.real[to.y], IO.real[to.z]]]];
};
Write[IO.PutFR1["# RIB data %g\n", IO.rope[comment]]];
Write["PixelSamples 2 2\n"];
IF imageName = NIL
THEN Write["Display \"framebuffer\" \"cedar\" \"rgba\"\n"]
ELSE Write[IO.PutFR1["Display \"%g\" \"file\" \"rgba\"\n", IO.rope[imageName]]];
Write["ScreenWindow -1.33333 1.33333 -1 1\n"];
Write[IO.PutFR["Format %g %g 1\n", IO.int[frameSize.x], IO.int[frameSize.y]]];
Write["Identity\n"];
Write[IO.PutFR1["Projection \"perspective\" \"fov\" %g\n", IO.real[fieldOfView]]];
WriteMatrix[MatrixFromCamera[scale, moves, rotates, TRUE], TRUE];
Write["WorldBegin\n"];
IF lights # NIL THEN {
Write[IO.PutFR["LightSource \"ambientlight\" 1 \"lightcolor\" [%g %g %g] \"intensity\" 1.0\n", IO.real[lights.ambient.R], IO.real[lights.ambient.G], IO.real[lights.ambient.B]]];
FOR i: INT IN [0..lights.length) DO WriteLight[lights[i], i+2]; ENDLOOP;
};
IF shapes # NIL THEN FOR i: INT IN [0..shapes.length) DO WriteShape[shapes[i]]; ENDLOOP;
Write["WorldEnd\n"];
};
Inner[ ! abort => CONTINUE];
};
Miscellany
Blink: PROC [msg: ROPE] ~ {
MessageWindow.Append[msg];
MessageWindow.Blink[];
};
END.