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;
G3dToolRenderImpl: CEDAR PROGRAM
IMPORTS Controls, G3dControl, G3dLight, G3dMatrix, G3dRender, G3dVector, G3dView, IO, MessageWindow, Rope
EXPORTS G3dTool
~ BEGIN
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: BOOLFALSE;
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 ROPELIST["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: ROPEIO.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: STREAMIO.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: ROPEIF 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: BOOLFALSE];
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: BOOLTRUE] ~ {};
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: NATIF 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
];
hiQ: BOOLTRUE;
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.STREAMIO.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: ROPESELECT 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: ROPENIL;
IF choice # -1 THEN {
prompt: ROPENIL;
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];
};
Temporary Ops
SceneOpsButton: PUBLIC ClickProc ~ {};
AnimateOpsButton: PUBLIC ClickProc ~ {};
TTNode: TYPE ~ G3dTimeTrees.Node;
TransformAndPropagateNode: PUBLIC PROC [node: TTNode, matrix: G3dMatrix.Matrix, order: G3dMatrix.TransformOrder] ~ {};
Lighting Ops
ChooseLight: PROC [lights: LightSequence, header: ROPE] RETURNS [choice: INT ¬ 0] ~ {
l: LIST OF Controls.Request ¬ NIL;
IF lights = NIL THEN RETURN;
FOR n: NAT IN [1..lights.length] DO l ¬ CONS[[lights[lights.length-n].name], l]; ENDLOOP;
choice ¬ Controls.PopUpRequest[[header], l];
};
GetSelectedLight: PUBLIC PROC [t: Tool] RETURNS [l: Light] ~ {
IF t.selectedLight # -1 AND t.lights # NIL THEN l ¬ t.lights[t.selectedLight];
};
LightOptions: PUBLIC PROC [
lights: LightSequence,
eyeView: Triple,
ts: Typescript]
RETURNS [LightSequence]
~ {
AddLight: PROC [type: {infinite, local}] ~ {
ENABLE IO.Error, IO.EndOfStream => {Blink["Bad Format"]; CONTINUE};
adjective: ROPE ¬ IF type = local THEN "local" ELSE "infinite";
s: IO.STREAM ¬ IO.RIS[Controls.TypescriptRead[ts,
Rope.Concat[adjective, " light <name> <position: x,y,z> [color: r,g,b]: "]]];
name: ROPE ¬ IO.GetTokenRope[s, IO.IDProc ! IO.EndOfStream => GOTO Eof].token;
position: Triple ¬ [IO.GetReal[s], IO.GetReal[s], IO.GetReal[s]];
vector: Triple ¬ G3dVector.Negate[G3dVector.Unit[position]];
r, g, b: REAL ¬ 1.0;
r ¬ IO.GetReal[s ! IO.EndOfStream => CONTINUE];
g ¬ IO.GetReal[s ! IO.EndOfStream => {b ¬ g ¬ r; CONTINUE}];
b ¬ IO.GetReal[s ! IO.EndOfStream => {b ¬ g; CONTINUE}];
lights ¬ G3dLight.AddLight[lights, name, position, vector, [r, g, b]];
EXITS Eof => NULL;
};
IF lights = NIL THEN lights ¬ NEW[G3dLight.LightSequenceRep[1]];
SELECT Controls.PopUpRequest[["Light Options"], LIST[
-- 1 -- [IO.PutFR1["Display lights (presently %g)",
IO.rope[IF lights.drawLights THEN "on" ELSE "off"]], "List lights"],
-- 2 -- ["List the lights", "List lights"],
-- 3 -- ["Delete a light", "Delete a light"],
-- 4 -- ["Add/Move a local light", "Add a local light"],
-- 5 -- ["Add/Move an infinite light", "Add a light at infinity"],
-- 6 -- ["Set ambient light", "Adjust ambient lighting (will prompt)"],
-- 7 -- ["Set shadow attenuation", "0 for no attenuation, 1 for total"]]] FROM
1 => lights.drawLights ¬ NOT lights.drawLights;
2 => IF lights.length > 3
THEN G3dLight.AnnotateLightsToViewer[lights, "LightList.tioga"]
ELSE Controls.TypescriptWrite[ts, G3dLight.RopeFromLights[lights]];
3 => {
n: INT ¬ ChooseLight[lights, "Delete Light"]-1;
IF n >= 0 THEN G3dLight.DeleteLight[lights, lights[n].name];
};
4 => AddLight[local];
5 => AddLight[infinite];
6 => {
rgb: RGB ¬ [0.7, 0.7, 0.7];
r: G3dBasic.RealSequence ¬
Controls.TypescriptReadValues[ts,, LIST[["r", rgb.R], ["g", rgb.G], ["b", rgb.B]]];
lights.ambient ¬ [r[0], r[1], r[2]];
};
7 =>
lights.shadowDarken ¬ Controls.GetReal[ts, "shadow darkening: ", lights.shadowDarken];
ENDCASE;
RETURN[lights];
};
LightOpsButton: PUBLIC ClickProc ~ {
t: Tool ¬ NARROW[clientData];
eyeView: Triple ¬ G3dControl.EyeViewFromCameraControl[t.camera];
t.lights ¬ LightOptions[t.lights, eyeView, t.typescript];
t.drawLights ¬ t.lights.drawLights;
};
END.