<<>> <> <> <> <<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; <> <> <> 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]; }; }; <> <> ForkRender: CedarProcess.ForkableProc ~ { t: Tool ¬ NARROW[data]; <> 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!"]; }; <> <<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>> <> <<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]; <> 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 ~ { <> 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 "]; <> <> <> <> }; }; 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?)"] }; <> 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] ~ { <> <> 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]; }; <> Blink: PROC [msg: ROPE] ~ { MessageWindow.Append[msg]; MessageWindow.Blink[]; }; END.