G3dToolRenderImpl.mesa
Copyright Ó 1985, 1992 by Xerox Corporation. All rights reserved.
Bloomenthal, February 19, 1993 4:37 pm PST
Heckbert, August 9, 1988 5:51:51 pm PDT
Glassner, July 19, 1990 12:21:59 pm PDT
Shoemake, November 23, 1989 3:43:56 am PST
Wyvill, September 19, 1989 10:34:42 am PDT
Crow, November 15, 1990 1:12 pm PST
3d Tool: Rendering, Texturing, Lighting
DIRECTORY Controls, G2dBasic, G3dBasic, G3dControl, G3dLight, G3dMatrix, G3dRender, G3dShape, G3dTimeTrees, G3dTool, G3dVector, G3dView, Imager, IO, MessageWindow, Rope;
Imported Types
ROPE: TYPE ~ Rope.ROPE;
STREAM: TYPE ~ IO.STREAM;
ClickProc: TYPE ~ Controls.ClickProc;
Typescript: TYPE ~ Controls.Typescript;
Pair: TYPE ~ G2dBasic.Pair;
Triple: TYPE ~ G3dBasic.Triple;
RealSequence: TYPE ~ G3dBasic.RealSequence;
CameraControl: TYPE ~ G3dControl.CameraControl;
Light: TYPE ~ G3dLight.Light;
LightSequence: TYPE ~ G3dLight.LightSequence;
Context3d: TYPE ~ G3dRender.Context3d;
RGB: TYPE ~ G3dRender.RGB;
TextureStyle: TYPE ~ G3dRender.TextureStyle;
Tool: TYPE ~ G3dTool.Tool;
ViewProc: TYPE ~ G3dTool.ViewProc;
ShapeProc: TYPE ~ G3dShape.ShapeProc;
Rendering Ops
RenderButton: PUBLIC ClickProc ~ {G3dTool.Repaint[NARROW[clientData], $Render]};
RenderOpsButton: PUBLIC ClickProc ~ {};
t: Tool ← NARROW[clientData];
context3d: Context3d ← G3dTool.GetFirstActiveContext3d[t];
depthBuffering: BOOL ← FALSE;
IF G3dTool.Context3dOk[t] THEN {
choice: INT ← Controls.PopUpRequest[["Render Ops"], LIST[
-- 1 -- ["Change Display Mode", Rope.Cat["Mode now ",
G3dRender.RopeFromDisplayMode[context3d.displayMode]]],
-- 2 -- Controls.BoolRequest[t.antiAliasing, "Anti Aliasing"],
-- 3 -- Controls.BoolRequest[depthBuffering, "Depth Buffering"],
-- 4 -- Controls.BoolRequest[context3d.clear, "Enable Clear Before Render"],
-- 5 -- Controls.BoolRequest[t.normalCoding, "Normal Encoding"],
-- 6 -- ["Background", "r, g, b <0..1> background color"],
-- 7 -- ["Background Image", "AIS name of background picture"],
-- 8 -- ["Viewport", "Set Screen Viewport"],
-- 8 -- ["Render to AIS File(s)", "Render an arbitrarily sized image to ais file(s)"],
-- 9 -- ["Render to RGB File", "Render arbitrarily sized image to rgb (Abekas) file"],
-- 10 -- Controls.BoolRequest[t.autoRender, "Auto Rendering"],
-- 11 -- Controls.BoolRequest[t.drawAxes, "Axes"],
-- 12 -- Controls.BoolRequest[t.annotation, "Annotation"]]];
G3dTool.SetCursor[t, TRUE];
SELECT choice FROM
1 => context3d.displayMode ← SELECT Controls.PopUpRequest[["Render Ops"], LIST[
-- 1 -- ["Gray", "Set display mode to gray (8 bits/pixel, no color)"],
-- 2 -- ["Dither", "Set display mode to dither (8 bits/pixel, with color)"],
-- 3 -- ["Full Color", "Set display mode to full color (24 bits/pixel)"]]] FROM
1 => gray, 2 => dither, 3 => fullColor, ENDCASE => context3d.displayMode;
2 => SetAntiAliasing[t, t.antiAliasing ← NOT t.antiAliasing];
3 => SetDepthBuffering[t, NOT depthBuffering];
4 => EnableClear[t, NOT context3d.clear];
5 => SetNormalCoding[t, NOT t.normalCoding];
6 => SetBackground[t];
7 => SetBackgroundImage[t];
-- 8 => SetViewport[t];
8 => RenderImageToFile[t, ais];
9 => RenderImageToFile[t, rgb];
10 => t.autoRender ← NOT t.autoRender;
11 => IF (t.drawAxes ← NOT t.drawAxes)
THEN KillAxes[context3d] ELSE G3dRender.AddAxes[context3d,,,, 0];
12 => t.annotation ← NOT t.annotation;
ENDCASE;
G3dTool.SetCursor[t, FALSE];
IF choice = 12 THEN G3dTool.Repaint[t, $DrawOps];
};
};
Blink:
PROC [r:
ROPE] ~ {
MessageWindow.Append[Rope.Concat["\t\t", r], TRUE];
MessageWindow.Blink[];
};
KillAxes: PROC [context3d: Context3d] ~ {
FOR l: LIST OF ROPE ← LIST["x", "y", "z", "x-axis", "y-axis", "z-axis"], l.rest WHILE l # NIL DO
G3dRender.DeleteShape[context3d, l.first ! G3dRender.Error => CONTINUE];
ENDLOOP;
};
UpdateCamera:
PUBLIC
PROC [tool: Tool] ~ {
moves, rots: Triple;
c: Context3d ¬ tool.context3d;
[moves, rots] ¬ G3dView.FromEyeLookUp[c.eyePoint, c.lookAt, c.upDirection, c.fieldOfView];
G3dControl.UpdateControl[tool.camera, tool.camera.par.xMov, moves.x];
G3dControl.UpdateControl[tool.camera, tool.camera.par.yMov, moves.y];
G3dControl.UpdateControl[tool.camera, tool.camera.par.zMov, moves.z];
G3dControl.UpdateControl[tool.camera, tool.camera.par.xRot, rots.x];
G3dControl.UpdateControl[tool.camera, tool.camera.par.yRot, rots.y];
G3dControl.UpdateControl[tool.camera, tool.camera.par.zRot, rots.z];
G3dControl.UpdateControl[tool.camera, tool.camera.scale, 1.0];
G3dControl.UpdateControl[tool.camera, tool.camera.fieldOfView, c.fieldOfView];
};
RenderImageToFile: PROC [t: Tool, op: {ais, rgb}] ~ {
Fix: PROC [r: REAL] RETURNS [i: INT] ~ {i ← Real.Fix[r]};
context3d: Context3d ← G3dTool.GetFirstActiveContext3d[t];
v: Imager.Rectangle ← context3d.viewport;
w: INT ← IF op = rgb THEN 720 ELSE Fix[v.w];
h: INT ← IF op = rgb THEN 486 ELSE Fix[v.h];
prompt: ROPE ← IO.PutFR["<%g root> [width, height (defaults %g %g)]: ",
IO.rope[IF op = rgb THEN "RGB (Abekas)" ELSE "AIS"], IO.int[w], IO.int[h]];
reply: ROPE ← Controls.TypescriptRead[t.typescript, prompt];
IF reply # NIL THEN {
ENABLE IO.Error => {Blink["Bad Format"]; CONTINUE};
GetInt: PROC [default: INT] RETURNS [i: INT] ~ {
i ← default; i ← IO.GetInt[s ! IO.EndOfStream => CONTINUE];
};
s: STREAM ← IO.RIS[reply];
name: ROPE ← FileNames.ResolveRelativePath[IO.GetTokenRope[s, IO.IDProc].token];
width: INT ← GetInt[w];
height: INT ← GetInt[h];
n: INT ← Rope.FindBackward[name, "."]+1;
ext: ROPE ← IF n # 0 THEN Rope.Substr[name, n, Rope.Length[name]-n] ELSE NIL;
IF ext # NIL AND (Rope.Equal[ext, "ais", FALSE] OR Rope.Equal[ext, "rgb", FALSE])
THEN name ← Rope.Substr[name, 0, n-1];
IF op = rgb THEN name ← Rope.Concat[name, ".rgb"];
RenderToFile[context3d, t.camera, name, width, height, TRUE];
};
};
EnableClear:
PUBLIC
PROC [tool: Tool, on:
BOOL] ~ {};
Action: ViewProc ← {G3dRender.EnableClear[view.context3d, on]};
G3dTool.DoWithViews[tool, Action, FALSE];
};
SetBackground: PROC [t: Tool] ~ {
Action: ViewProc ← {G3dRender.SetBackgroundColor[view.context3d, rgb]};
rgb: RGB ← G3dTool.GetFirstActiveContext3d[t].backgroundColor;
reals: RealSequence ←
Controls.TypescriptReadValues[t.typescript,, LIST[["r", rgb.R], ["g", rgb.G], ["b", rgb.B]]];
IF reals # NIL THEN rgb ← [reals[0], reals[1], reals[2]];
EnableClear[t, TRUE];
G3dTool.DoWithViews[t, Action, FALSE];
};
SetBackgroundImage: PROC [t: Tool] ~ {
Action: ViewProc ← {G3dRender.SetBackgroundImage[view.context3d, filename]};
filename: ROPE ← Controls.TypescriptReadFileName[t.typescript];
EnableClear[t, TRUE];
IF filename # NIL THEN G3dTool.DoWithViews[t, Action, FALSE];
};
SetViewport: PROC [t: Tool] ~ {
IF G3dTool.Context3dOk[t] THEN {
r: REF Imager.Rectangle ← t.context3d.viewPort;
reals: RealSequence;
IF r = NIL THEN r ← NEW[Imager.Rectangle ← [0., 0., 640., 480.]];
reals ← Controls.TypescriptReadValues[
t.typescript,, LIST[["xmin", r.x], ["ymin", r.y], ["width", r.w], ["height", r.h]]];
IF reals # NIL AND reals.length = 4
THEN G3dRender.SetViewPort[t.context3d, [reals[0], reals[1], reals[2], reals[3]]]
ELSE G3dTool.TSWrite[t, " . . . aborted."];
};
};
AbortButton:
PUBLIC ClickProc ~ {};
Action: ViewProc ~ {G3dRender.AbortRender[view.context3d]};
G3dTool.DoWithViews[NARROW[clientData], Action, FALSE];
};
FiltersNeeded: TYPE ~ RECORD [antiAliasing, textureFiltering: BOOL ← FALSE];
FilteringNeeded: PROC [tool: Tool] RETURNS [f: FiltersNeeded] ~ {
IF tool.shapes # NIL THEN FOR n: NAT IN [0..tool.shapes.length) DO
r: G3dRender.RenderData ← G3dRender.RenderDataFrom[tool.shapes[n]];
FOR l: LIST OF G3dRender.TextureMap ← r.textures, l.rest WHILE l # NIL DO
IF l.first.style = bump THEN RETURN[[TRUE, TRUE]];
ENDLOOP;
IF r.transmittance > 0.0 THEN f.antiAliasing ← TRUE;
ENDLOOP;
};
Render: PUBLIC PROC [tool: Tool, context: Imager.Context, fork: BOOL ← TRUE] ~ {};
Action: ViewProc ~ {
IF G3dRender.IsRendering[view.context3d] THEN {
Blink["Already rendering!"];
RETURN[FALSE];
};
-- r: Imager.Rectangle ← tool.context3d.preferredViewPort;
-- IF ((r.x+r.w > 1024 OR r.y+r.h > 768) AND tooldisplayMode = gray) OR
-- ((r.x+r.w > 640 OR r.y+r.h > 480) AND tooldisplayMode # gray) THEN {
-- Blink["size too large; try RenderToFile <name>"];
-- RETURN;
-- };
RenderFromCamera[view.context3d, context, tool.camera, fork
! G3dRender.Error => {Blink[reason]; GOTO Bad}];
IF fork
THEN [] ← CedarProcess.Fork[WaitAndDoPostRendering, tool, [background, TRUE]]
ELSE PostRendering[tool];
EXITS Bad => NULL;
};
f: FiltersNeeded ← FilteringNeeded[tool];
IF tool.timing THEN G3dTool.StartTimer[tool];
IF f.antiAliasing AND NOT tool.antiAliasing THEN {
Blink["antiAliasing must be on"];
RETURN;
};
IF f.textureFiltering AND NOT tool.textureFiltering THEN {
Blink["texture filtering must be on"];
RETURN;
};
IF tool.normalCoding AND (NOT tool.antiAliasing) THEN {
Blink["To normal encode, antiAliasing must be on and background must be off"];
RETURN;
};
G3dTool.DoWithViews[tool, Action];
};
WaitAndDoPostRendering: CedarProcess.ForkableProc ~ {
G3dRender.WaitTilRenderDone[];
PostRendering[NARROW[data]];
};
PostRendering: PROC [tool: Tool] ~ {
IF tool.annotation THEN {
Print: PROC [rope: ROPE, p: Pair, color: Imager.Color ← NIL, font: ROPE ← NIL, size: REAL ← 14] ~ {
context3d: Imager.Context3d ← ImagerSmoothContext3d.FromTerminal[Terminal.Current[], TRUE];
IF font = NIL THEN font ← "helvetica-mrr";
Imager.SetFont[context3d, Imager.FindFontScaled[Rope.Concat["xerox/pressfonts/", font], size]];
IF color # NIL THEN Imager.SetColor[context3d, color];
Draw2d.Label[context3d, [p.x, ImagerBackdoor.GetBounds[context3d].h-p.y], rope];
};
CRStrip: PROC [in: ROPE] RETURNS [ROPE] ~ {
Translator: Rope.TranslatorType ~ {RETURN[IF old = '\n THEN Ascii.SP ELSE old]};
RETURN[Rope.Translate[in,,, Translator]];
};
h: NAT ← IF ColorDisplayManager.NextState[].resolution = standard THEN 480 ELSE 768;
Print[G3dControl.CameraMessage[tool.camera], [10, h-30], Imager.black];
Print[CRStrip[G3dLight.RopeFromLights[tool.lights]], [10, h-15], Imager.black];
};
IF tool.normalCoding THEN
G3dNormalCoding.EncodeAllNormals[G3dTool.GetFirstActiveContext3d[tool]];
IF tool.timing THEN G3dTool.PrintElapsedTime[tool, 0.0, "Rendering"];
};
SetRenderView:
PUBLIC
PROC [context3d: Context3d, camera: CameraControl] ~ {
G3dRender.SetView[
context3d, camera.eyePoint, camera.lookAt, camera.fieldOfView.value, 0, camera.up];
context3d.view ¬ G3dMatrix.CopyMatrix[camera.matrix, context3d.view];
context3d.scale ¬ camera.scale.value;
};
RenderFromCamera:
PUBLIC
PROC
[
context3d: Context3d,
context: Imager.Context,
camera: CameraControl,
fork: BOOL ¬ TRUE]
~ {};
IF G3dRender.IsRendering[context3d] THEN RETURN;
SetRenderView[context3d, camera];
G3dRender.PrepareShapes[context3d.shapes]; -- causes duplicated effort, being fixed
[] ← G3dRender.Render[context3d, context, fork];
};
RenderData: TYPE ~ REF RenderDataRep;
RenderDataRep: TYPE ~ RECORD [
context3d: Context3d,
fileName: ROPE,
width, height: NAT
];
DoRenderToFile: CedarProcess.ForkableProc ~ {
rd: RenderData ← NARROW[data];
c: Context3d ← G3dRender.Create[];
c.viewport ← [0, 0, rd.width, rd.height];
G3dRender.RenderToFile[c, rd.fileName];
};
RenderToFile:
PUBLIC
PROC [
context3d: Context3d,
camera: CameraControl,
fileName: ROPE,
width: NAT ¬ 1024,
height: NAT ¬ 768,
fork: BOOL ¬ TRUE]
~ {};
renderData: RenderData ← NEW[RenderDataRep ← [context3d, fileName, width, height]];
SetRenderView[context3d, camera];
IF NOT G3dRender.IsRendering[context3d] AND fork
THEN {
process: CedarProcess.Process ←
CedarProcess.Fork[DoRenderToFile, renderData, [background, TRUE]];
context3d.props ← Atom.PutPropOnList[context3d.props, $Process, process];
}
ELSE [] ← DoRenderToFile[renderData];
};
SetAntiAliasing:
PUBLIC
PROC [tool: Tool, antiAliasing:
BOOL] ~ {};
Action: ViewProc ← {G3dRender.SetAntiAliasing[view.context3d, antiAliasing]};
G3dTool.DoWithViews[tool, Action, FALSE];
};
SetDepthBuffering:
PUBLIC
PROC [tool: Tool, depthBuffering:
BOOL] ~ {};
A: ViewProc ← {G3dRenderWithPixels.DepthBuffering[view.context3d, depthBuffering]};
G3dTool.DoWithViews[tool, A, FALSE];
};
SetNormalCoding:
PUBLIC
PROC [tool: Tool, normalBuffering:
BOOL] ~ {};
A: ViewProc ← {G3dRenderWithPixels.NormalBuffering[view.context3d, normalBuffering]};
tool.normalCoding ← normalBuffering;
G3dTool.DoWithViews[tool, A, FALSE];
};
Texture Ops
TextureMap: TYPE ~ G3dRender.TextureMap;
TextureOpsButton:
PUBLIC ClickProc ~ {};
t: Tool ← NARROW[clientData];
choice: INT ← Controls.PopUpRequest[["Texture Ops"], LIST[
-- 1 -- ["Scale 2d Texture", "Scale texture coordinates of Shape (will prompt)"],
-- 2 -- ["Offset 2d Texture", "Offset texture coordinates of Shape (will prompt)"],
-- 3 -- ["Set Bump Factor", "Change Bump Scale Factor (default = 1.0)"],
-- 4 -- Controls.BoolRequest[t.textureFiltering, "Texture Filtering"],
-- 5 -- ["Set intensity texture map", "Read Texture from File, use for surface intensity"],
-- 6 -- ["Set color texture map", "Read Texture from File, use for surface color"],
-- 7 -- ["Set bump map", "Read Texture from File, use for bumps"],
-- 8 -- ["Remove Texture Map", "Remove texture from shape"]]];
G3dTool.SetCursor[t, TRUE];
SELECT choice FROM
1 => SetTextureScale[t];
2 => SetTextureOffset[t];
3 => SetBumpScale[t];
4 => ToggleTextureFiltering[t];
5 => SetMapping[t, intensity];
6 => SetMapping[t, color];
7 => SetMapping[t, bump];
8 => SetMapping[t, none];
ENDCASE;
G3dTool.SetCursor[t, FALSE];
};
SetTextureOffset: PROC [t: Tool] ~ {
choice: INT ← G3dTool.ChooseShape[t, "Set Texture Offset of Shape"];
IF choice # -2 THEN {
Action: ShapeProc ~ {G3dRender.OffsetTextureCoords[shape, off]};
off: Pair ← GetPairFromTypescript[t, "Offset <x> <y>: "];
IF off = [0, 0] THEN {G3dTool.TSWrite[t, " . . . aborted."]; RETURN};
G3dTool.DoWithSelectedShapes[t.shapes, Action, choice];
};
};
GetPairFromTypescript: PROC [t: Tool, prompt: ROPE] RETURNS [out: Pair ← [0, 0]] ~ {
ENABLE IO.EndOfStream, IO.Error => GOTO Bad;
reply: ROPE ← Controls.TypescriptRead[t.typescript, prompt];
IF reply # NIL THEN {
s: IO.STREAM ← IO.RIS[reply];
out ← [IO.GetReal[s], IO.GetReal[s]];
};
EXITS Bad => G3dTool.TSWrite[t, ". . . bad format.\n"];
};
DoWithTextureMaps: PROC [s: G3dShape.Shape, proc: PROC [m: TextureMap]] ~ {
r: G3dRender.RenderData ← G3dRender.RenderDataFrom[s];
FOR l: LIST OF TextureMap ← r.textures, l.rest WHILE l # NIL DO proc[l.first]; ENDLOOP;
};
SetTextureScale: PROC [t: Tool] ~ {
Action: ShapeProc ~ {DoWithTextureMaps[shape, Proc]};
Proc: PROC [m: TextureMap] ~ {m.scale ← [r[0], r[1]]};
r: RealSequence;
v: Pair ← [1.0, 1.0];
choice: INT ← G3dTool.ChooseShape[t, "Set Texture Scale of Shape"];
IF choice = -2 THEN RETURN;
IF choice # -1 THEN {
d: G3dRender.RenderData ← G3dRender.RenderDataFrom[t.shapes[choice]];
IF d.textures # NIL THEN v ← d.textures.first.scale;
};
r ← Controls.TypescriptReadValues[t.typescript,, LIST[["xScale", v.x], ["yScale", v.y]]];
IF r = NIL THEN {G3dTool.TSWrite[t, " . . . aborted."]; RETURN};
G3dTool.DoWithSelectedShapes[t.shapes, Action, choice];
};
SetBumpScale: PROC [t: Tool] ~ {
Action: ShapeProc ~ {DoWithTextureMaps[shape, Proc]};
Proc: PROC [m: TextureMap] ~ {m.bumpHeight ← scale};
scale: REAL ← 1.0;
choice: INT ← G3dTool.ChooseShape[t, "Set Bump Scale of Shape"];
IF choice = -2 THEN RETURN;
IF choice # -1 THEN {
d: G3dRender.RenderData ← G3dRender.RenderDataFrom[t.shapes[choice]];
IF d.textures # NIL THEN scale ← d.textures.first.bumpHeight;
};
scale ← Controls.TypescriptReadValue[t.typescript, "Bump scale factor", scale];
G3dTool.DoWithSelectedShapes[t.shapes, Action, choice];
};
ToggleTextureFiltering: PROC [t: Tool] ~ {
Action: ShapeProc ~ {DoWithTextureMaps[shape, Proc]};
Proc: PROC [m: TextureMap] ~ {m.filter ← t.textureFiltering};
t.textureFiltering ← NOT t.textureFiltering;
FOR n: NAT IN [0..t.shapes.length) DO [] ← Action[t.shapes[n]]; ENDLOOP;
};
SetMapping: PROC [t: Tool, textureStyle: TextureStyle] ~ {
Action: ShapeProc ~ {
IF textureStyle # none AND NOT shape.vertices.valid.texture
THEN G3dTool.TSWrite[t, Rope.Cat["Operation not performed for ", FileNames.GetShortName[shape.name], " (no texture coordinates!)"]]
ELSE {
error: ROPE ←
G3dRender.SetTextureMap[shape, textureName, textureStyle, t.textureFiltering];
IF error # NIL THEN G3dTool.TSWrite[t, error];
};
};
textureName: ROPE;
header: ROPE ← SELECT textureStyle FROM
intensity => "Set intensity texture map for shape",
color => "Set color texture map for shape",
bump => "Set bump map for shape",
ENDCASE => "Remove all texture from shape";
choice: INT ← G3dTool.ChooseShape[t, header];
IF choice = -2 THEN RETURN;
IF textureStyle # none THEN {
prompt: ROPE ← NIL;
IF choice # -1 THEN {
prompt: ROPE ← NIL;
textures: LIST OF TextureMap ← G3dRender.RenderDataFrom[t.shapes[choice]].textures;
FOR l: LIST OF TextureMap ← textures, l.rest WHILE l # NIL DO
IF l.first.name # NIL THEN prompt ← Rope.Cat[prompt, ", ", l.first.name];
ENDLOOP;
IF prompt # NIL THEN prompt ← Rope.Cat["(currently", prompt, ")"];
};
IF prompt = NIL THEN prompt ← "filename: ";
IF (textureName ← Controls.TypescriptRead[t.typescript, prompt]) = NIL THEN {
G3dTool.TSWrite[t, " . . . aborted."];
RETURN;
};
};
G3dTool.DoWithSelectedShapes[t.shapes, Action, choice];
};