VizerImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Bloomenthal, February 24, 1987 5:33:24 pm PST
DIRECTORY Args, Commander, Controls, Controls3d, Convert, Draw2d, Draw3d, FileNames, FS, Imager, ImagerFont, ImagerInterpress, IO, Matrix3d, Polygons3d, Render3d, Rope, RuntimeError, ThreeDBasics, ThreeDIO, ThreeDMisc, ThreeDScenes, Tilers, Vector3d, ViewerOps;
VizerImpl: CEDAR PROGRAM
IMPORTS Args, Commander, Controls, Controls3d, Convert, Draw2d, Draw3d, FileNames, FS, Imager, ImagerFont, ImagerInterpress, IO, Polygons3d, Render3d, Rope, RuntimeError, ThreeDMisc, ThreeDScenes, Tilers, Vector3d, ViewerOps
~ BEGIN
UserData:     TYPE ~ RECORD [o: OuterData, p: ProgramData];
LightInfo:    TYPE ~ RECORD [name: ROPE, position: Triple];
ROPE:      TYPE ~ Rope.ROPE;
Context:     TYPE ~ Imager.Context;
ClickProc:    TYPE ~ Controls.ClickProc;
OuterData:    TYPE ~ Controls.OuterData;
Triple:     TYPE ~ Vector3d.Triple;
TripleSequence:   TYPE ~ Vector3d.TripleSequence;
RenderStyle:    TYPE ~ Render3d.RenderStyle;
Option:     TYPE ~ {viewer, backFaces, faceNormals, pointNormals, labels, pendant,
         autoRender, lines, jaggies, texture, bump};
Options:     TYPE ~ ARRAY Option OF BOOL;
ProgramData:    TYPE ~ REF ProgramDataRep;
ProgramDataRep:   TYPE ~ RECORD [
file:        ROPENIL,
cmdOut:       IO.STREAMNIL,
graphics, outer:     Controls.Viewer ← NIL,
outerData:      OuterData ← NIL,
cam:        Controls3d.Camera ← NIL,
lastMoused:      Controls.Control ← NIL,
vernier:       Controls.Control ← NIL,
newPairs:       BOOLTRUE,
options:       Options ←
         [TRUE, TRUE, FALSE, FALSE, FALSE, FALSE,
         FALSE, FALSE, TRUE, FALSE, FALSE],
pairs:        Vector3d.PairSequence ← NIL,
faceCenters:      TripleSequence ← NIL,
faceNormals:      TripleSequence ← NIL,
view:        Matrix3d.Matrix ← NIL,
points:       TripleSequence ← NIL,
polygons:       REF ThreeDIO.NatTable ← NIL,
renderStyle:      RenderStyle ← smooth,
context3d:      Render3d.Context3d ← NIL
];
Vizer: Commander.CommandProc ~ {
errorMsg: ROPE;
p: ProgramData ← NEW[ProgramDataRep];
p.cmdOut ← cmd.out;
p.cam ← Controls3d.InitCamera[proc: CameraControl, data: p];
p.vernier ← Controls.NewControl["vernier", dial, p, 2.0, 0.0, 0.0, Vernier, FALSE];
p.context3d ← Render3d.InitContext3d[];
p.outer ← Controls.OuterViewer[
name: Rope.Concat["Vizer ", Args.GetRope[cmd]],
buttons: LIST[
["Viewer-On", Viewer, 2],
["BackFaces-On", BackFaces, 2],
["FaceNormals-Off", FaceNormals, 2],
["PointNormals-Off", PointNormals, 2],
["Labels-Off", Labels, 2],
["Pendant-Off", Pendant, 2],
["Render", Render, 1],
["Abort Render", AbortRender, 1],
["AutoRender-Off", AutoRender, 1],
["Smooth", CycleRenderStyle, 1],
["Lines-Off", Lines, 1],
["Jaggies-On", Jaggies, 1],
["Texture-Off", Texture, 1],
["Bump-Off", Texture, 1],
["New Shape", NewShape, 0],
["Add/Move Light", AddMoveLight, 0],
["Delete Light", DeleteLight, 0],
["List Lights", ListLights, 0],
["Shininess", Shininess, 0],
["IP Out", IPOut, 0],
["Render-IP-Out", RenderIPOut, 0]],
controls: LIST[
p.cam.xGlobal, p.cam.yGlobal, p.cam.zGlobal, p.cam.scale,
p.cam.hScreen, p.cam.vScreen, p.cam.fieldOfView, p.vernier],
graphicsHeight: 400,
graphicsShow: GraphicsShow,
typeScriptHeight: 18,
destroyProc: DestroyProc,
data: p];
p.outerData ← NARROW[p.outer.data];
p.graphics ← p.outerData.graphics;
IF Args.GetRope[cmd] # NIL THEN {
p.file ← FS.ExpandName[Args.GetRope[cmd]].fullFName;
IF (errorMsg ← ReadFile[p]) # NIL THEN RETURN[$Failure, errorMsg];
p.newPairs ← TRUE;
ViewerOps.PaintViewer[p.graphics, client, FALSE];
};
};
TSWrite: PROC [p: ProgramData, r: ROPE] ~ {Controls.TypeScriptWrite[p.outerData, r]};
ReadFile: PROC [p: ProgramData] RETURNS [errorMsg: ROPE] ~ {
Render3d.AddFileShape[p.context3d, "VizerShape", p.file
! FS.Error => {errorMsg ← error.explanation; CONTINUE}];
IF errorMsg = NIL THEN {
shape: REF ThreeDBasics.ShapeInstance ←
ThreeDScenes.FindShape[p.context3d.shapes, "VizerShape"];
p.polygons ← Render3d.PolygonsFromShape[shape];
p.points ← Render3d.PointsFromShape[shape, p.points];
p.faceCenters ← Polygons3d.CentersOfPolygons[p.polygons, p.points, p.faceCenters
! RuntimeError.BoundsFault => CONTINUE];
p.faceNormals ← Polygons3d.PolygonNormals[p.polygons, p.points, p.faceNormals
! RuntimeError.BoundsFault => CONTINUE];
IF p.faceNormals # NIL THEN
FOR n: NAT IN [0..p.faceNormals.length) DO
p.faceNormals[n] ← Vector3d.Negate[Vector3d.Normalize[p.faceNormals[n]]];
ENDLOOP;
IF p.faceNormals = NIL THEN TSWrite[p, "File too large, so no normals\n"];
Controls.SetSliderDialValue[p.cam.scale, ObjectScale[p]];
};
};
ObjectScale: PROC [p: ProgramData] RETURNS [REAL] ~ {
biggest: REAL ← 0.0;
IF p.points = NIL THEN RETURN[1.0];
FOR n: NAT IN [0..p.points.length) DO
t: Triple ~ p.points[n];
biggest ← MAX[biggest, ABS[t.x], ABS[t.y]];
ENDLOOP;
RETURN[IF biggest # 0.0 THEN 1.0/biggest ELSE 1.0];
};
NewShape: ClickProc ~ {
outerData: OuterData ~ NARROW[clientData];
p: ProgramData ~ NARROW[outerData.data];
rope: ROPE ← Controls.TypeScriptReadFileName[outerData];
IF rope # NIL THEN {
p.file ← FS.ExpandName[rope].fullFName;
IF (rope ← ReadFile[p]) # NIL
THEN Controls.TypeScriptWrite[outerData, rope]
ELSE ViewerOps.PaintViewer[p.graphics, client, FALSE];
};
};
Texture: ClickProc ~ {SetMap[NARROW[NARROW[clientData, OuterData].data], $Texture]};
Bump: ClickProc ~ {SetMap[NARROW[NARROW[clientData, OuterData].data], $Bump]};
SetMap: PROC [p: ProgramData, atom: ATOM] ~ {
IF p.options[bump] OR p.options[texture]
THEN {
p.options[bump] p.options[texture] ← FALSE;
Render3d.TurnOffAntiAliasing[p.context3d];
Controls.ButtonReLabel[p.outerData, "Texture-On", "Texture-Off"];
Controls.ButtonReLabel[p.outerData, "Bump-On", "Bump-Off"];
}
ELSE {
message: ROPENIL;
b : ROPE ~ IF atom = $Bump THEN "Bump" ELSE "Texture";
textureType: Render3d.TextureType ~ IF atom = $Bump THEN bump ELSE intensity;
shape: REF ThreeDBasics.ShapeInstance ← VizerShape[p];
IF shape = NIL THEN RETURN;
IF ThreeDScenes.GetShading[shape, $TextureMap] = NIL THEN {
name: ROPE ← Controls.TypeScriptReadFileName[p.outerData];
IF name = NIL THEN RETURN;
name ← FileNames.ResolveRelativePath[name];
Render3d.SetTextureMap[p.context3d, "VizerShape", name, textureType
! ThreeDScenes.Error => {message ← reason.explanation; CONTINUE}];
};
IF message # NIL THEN {
TSWrite[p, message];
RETURN;
};
IF atom = $Bump
THEN p.options[bump] ← TRUE
ELSE p.options[texture] ← TRUE;
Render3d.TurnOnAntiAliasing[p.context3d];
Controls.ButtonToggle[p.outerData, TRUE, Rope.Cat[b, "-On"], Rope.Cat[b, "-Off"]];
};
};
Jaggies: ClickProc ~ {
u: UserData ← UserDataFromClientData[clientData];
Tog[clientData, jaggies, "Jaggies", FALSE];
IF u.p.options[jaggies]
THEN Render3d.TurnOffAntiAliasing[u.p.context3d]
ELSE Render3d.TurnOnAntiAliasing[u.p.context3d];
};
VizerShape: PROC [p: ProgramData] RETURNS [REF ThreeDBasics.ShapeInstance] ~ {
RETURN[ThreeDScenes.FindShape[p.context3d.shapes, "VizerShape"]];
};
Vernier: Controls.ControlProc ~ {
p: ProgramData ~ NARROW[control.data];
o: OuterData ← NARROW[p.outer.data];
IF p.lastMoused # NIL THEN Controls.Vernier[control, p.lastMoused];
p.newPairs ← TRUE;
Controls3d.UpdateCamera[p.cam];
IF control.mouse.button = right THEN ViewerOps.PaintViewer[p.graphics, client, FALSE];
};
DestroyProc: Controls.DestroyProc ~ {
p: ProgramData ~ NARROW[NARROW[outerData, OuterData].data];
Render3d.NullifyThreeDContext[p.context3d];
};
Tog: PROC [clientData: REF ANY, option: Option, name: ROPE, repaint: BOOLTRUE] ~ {
outerData: OuterData ~ NARROW[clientData];
p: ProgramData ~ NARROW[outerData.data];
bool: BOOL ~ p.options[option] ← NOT p.options[option];
Controls.ButtonToggle[outerData, bool, Rope.Concat[name, "-On"], Rope.Concat[name, "-Off"]];
IF repaint THEN ViewerOps.PaintViewer[p.graphics, client, FALSE];
};
BackFaces: ClickProc ~ {Tog[clientData, backFaces, "BackFaces", mouseButton = blue]};
Labels:  ClickProc ~ {Tog[clientData, labels, "Labels", mouseButton = blue]};
Pendant: ClickProc ~ {Tog[clientData, pendant, "Pendant", mouseButton = blue]};
FaceNormals: ClickProc ~ {Tog[clientData, faceNormals, "FaceNormals", mouseButton = blue]};
PointNormals: ClickProc ~ {Tog[clientData, pointNormals, "PointNormals", mouseButton=blue]};
AutoRender: ClickProc ~ {Tog[clientData, autoRender, "AutoRender", mouseButton = blue]};
Viewer:  ClickProc ~ {Tog[clientData, viewer, "Viewer", mouseButton = blue]};
Lines: ClickProc ~ {
p: ProgramData ← NARROW[NARROW[clientData, OuterData].data];
shape: REF ThreeDBasics.ShapeInstance ← VizerShape[p];
Tog[clientData, lines, "Lines", mouseButton = blue];
p.context3d.lineDrawing ← p.options[lines];
IF p.options[lines]
THEN ThreeDScenes.PutShading[shape, $Type, $Lines]
ELSE SELECT p.renderStyle FROM
faceted => ThreeDScenes.PutShading[shape, $Type, $Faceted];
ENDCASE => ThreeDScenes.PutShading[shape, $Type, $Smooth];
};
UserDataFromClientData: PROC [clientData: REF ANY] RETURNS [u: UserData] ~ {
u.o ← NARROW[clientData];
u.p ← NARROW[u.o.data];
};
GetLightInfo: PROC [p: ProgramData] RETURNS [lightInfo: LightInfo] ~ {
reply: ROPE;
TSWrite[p, "<Light name> <x> <y> <z>: "];
reply Controls.TypeScriptRead[p.outerData];
IF reply = NIL
THEN TSWrite[p, " . . . aborted.\n"]
ELSE {
x, y, z: ROPE;
n0, n1: INT ← 0;
NextRope: PROC RETURNS [ROPE] ~ {
IF (n0 ← Rope.SkipOver[reply, n1, " \t"]) = Rope.Length[reply] THEN RETURN[NIL];
n1 ← Rope.SkipTo[reply, n0, " \t"];
RETURN[Rope.Substr[reply, n0, n1-n0]];
};
IF (lightInfo.name ← NextRope[]) = NIL THEN GOTO BadFormat;
IF (x ← NextRope[]) = NIL THEN GOTO BadFormat;
IF (y ← NextRope[]) = NIL THEN GOTO BadFormat;
IF (z ← NextRope[]) = NIL THEN GOTO BadFormat;
lightInfo.position ← [
Convert.RealFromRope[x ! Convert.Error => GOTO BadFormat],
Convert.RealFromRope[y ! Convert.Error => GOTO BadFormat],
Convert.RealFromRope[z ! Convert.Error => GOTO BadFormat]];
};
EXITS
BadFormat => {
TSWrite[p, "Bad Format\n"];
RETURN[[NIL, [0.0, 0.0, 0.0]]];
};
};
AddMoveLight: ClickProc ~ {
p: ProgramData ← NARROW[NARROW[clientData, OuterData].data];
lightInfo: LightInfo ~ GetLightInfo[p];
IF lightInfo.name # NIL
THEN [] ← ThreeDScenes.SetLight[p.context3d, lightInfo.name, lightInfo.position];
};
DeleteLight: ClickProc ~ {
name: ROPE;
u: UserData ← UserDataFromClientData[clientData];
TSWrite[u.p, "Light name: "];
name Controls.TypeScriptRead[u.o];
IF name # NIL THEN ThreeDScenes.DeleteLight[u.p.context3d, name];
};
ListLights: ClickProc ~ {
p: ProgramData ← NARROW[NARROW[clientData, OuterData].data];
IO.PutRope[p.cmdOut, "Lights: \n"];
FOR n: NAT IN [0..p.context3d.shapes.length) DO
shape: REF ThreeDBasics.ShapeInstance ← p.context3d.shapes[n];
IF shape.type = $Light
THEN IO.PutF[p.cmdOut, "%g located at [%g, %g, %g]\n",
IO.rope[shape.name],
IO.real[shape.location.x],
IO.real[shape.location.y],
IO.real[shape.location.z]];
ENDLOOP;
};
GetShininessOfShape: PROC [shape: REF ThreeDBasics.ShapeInstance] RETURNS [REAL] ~ {
refAny: REF ANY ← ThreeDScenes.GetShading[shape, $Shininess];
RETURN[IF refAny # NIL THEN NARROW[refAny, REF REAL]^ ELSE 0.0];
};
Shininess: ClickProc ~ {
ok: BOOLTRUE;
p: ProgramData ← NARROW[NARROW[clientData, OuterData].data];
TSWrite[p, IO.PutFR["Shininess (now %g): ", IO.real[GetShininessOfShape[VizerShape[p]]]]];
ThreeDMisc.SetShininess[p.context3d, "VizerShape", Convert.RealFromRope[Controls.TypeScriptRead[p.outerData]
! Convert.Error => {ok ← TRUE; CONTINUE}]];
IF NOT ok THEN TSWrite[p, "Bad Format\n"];
};
CycleRenderStyle: ClickProc ~ {
Roper: PROC [renderStyle: RenderStyle] RETURNS [ROPE] ~ {
RETURN[SELECT renderStyle FROM
smooth => "Smooth", shiny => "Shiny", faceted => "Faceted", ENDCASE => NIL];
};
u: UserData ~ UserDataFromClientData[clientData];
old: RenderStyle ~ u.p.renderStyle;
u.p.renderStyle ← IF old = shiny THEN RenderStyle.FIRST ELSE SUCC[old];
Controls.ButtonReLabel[u.o, Roper[old], Roper[u.p.renderStyle]];
IF u.p.renderStyle = shiny
THEN Tilers.PhongShadeAllPolygons[]
ELSE Tilers.PhongShadeOnlyHighLightedPolygons[];
Render3d.SetRenderStyle[u.p.context3d, "VizerShape", u.p.renderStyle];
};
AbortRender: ClickProc ~ {
p: ProgramData ← NARROW[NARROW[clientData, OuterData].data];
p.context3d.stopMe ← TRUE;
};
Render: ClickProc ~ {
p: ProgramData ← NARROW[NARROW[clientData, OuterData].data];
Render3d.Render[p.context3d, "VizerShape", p.cam];
p.context3d.stopMe ← FALSE;
};
RenderIPOut: ClickProc ~ {
u: UserData ← UserDataFromClientData[clientData];
filename: ROPE ~ Controls.TypeScriptReadFileName[u.o];
IF filename # NIL THEN ThreeDMisc.MakeInterpressPage[u.p.context3d, filename];
};
CameraControl: Controls3d.CameraProc ~ {
p: ProgramData ← NARROW[data];
p.lastMoused ← control;
p.newPairs ← TRUE;
IF control.mouse.button # right THEN RETURN;
IF p.options[viewer] THEN ViewerOps.PaintViewer[p.graphics, client, FALSE];
IF p.options[autoRender] THEN Render3d.Render[p.context3d, "VizerShape", p.cam];
};
GraphicsShow: Controls.GraphicsShow ~ {
p: ProgramData ~ NARROW[data];
o: Options ~ p.options;
Action: PROC ~ {
Controls3d.UpdateCamera[p.cam];
IF whatChanged = NIL THEN p.view ← Controls3d.InitContext[context, p.cam, p.view];
IF p.newPairs THEN p.pairs ← Draw3d.PolygonPairs[p.points, p.view, p.pairs];
p.newPairs ← FALSE;
DrawAction[p, context, whatChanged];
};
IF p.points # NIL AND p.polygons # NIL THEN {
IF p.polygons.length < 500 AND p.points.length < 1000
THEN Draw2d.DoWithBuffer[context, Action]
ELSE Action[];
};
};
DrawAction: PROC [p: ProgramData, context: Context, whatChanged: REF ANY] ~ {
o: Options ~ p.options;
PolyProc: PROC [i: NAT] ~ {
IF o[faceNormals] AND p.faceCenters # NIL AND p.faceNormals # NIL
THEN Draw3d.Vector[context, p.faceCenters[i], p.faceNormals[i], p.view, , 0.03];
IF o[pointNormals] THEN {
poly: REF ThreeDBasics.NatSequence ~ p.polygons[i];
shades: REF ThreeDBasics.ShadingSequence ~ p.context3d.shapes[1].shade;
FOR n: NAT IN [0..poly.length) DO
j: NAT ~ poly[n];
point: Triple ~ p.points[j];
shade: REF ThreeDBasics.ShadingValue ~ shades[j];
Draw3d.Vector[context, point, [shade.xn, shade.yn, shade.zn], p.view, , 0.03];
ENDLOOP;
};
Draw3d.Polygon[context, p.polygons[i], p.pairs];
};
IF o[backFaces]
THEN FOR n: NAT IN [0..p.polygons.length) DO PolyProc[n]; ENDLOOP
ELSE Polygons3d.ApplyToFrontFacingPolygons[
PolyProc, p.polygons, p.pairs, p.view, p.faceNormals];
IF o[labels] THEN Draw3d.LabelPairs[context, p.pairs];
IF o[pendant] THEN Draw3d.Pendant[context, p.view, .07, .7, .7, ["x", "y", "z", , , ]];
};
IPOut: ClickProc ~ {
outerData: OuterData ~ NARROW[clientData];
p: ProgramData ~ NARROW[outerData.data];
fileName: ROPE ← Controls.TypeScriptReadFileName[outerData];
IF fileName # NIL THEN {
ref: ImagerInterpress.Ref ← ImagerInterpress.Create[fileName];
ContextProc: PROC [context: Context] ~ {
metersPerPoint: REAL ~ .0254/72.0;
Imager.ScaleT[context, metersPerPoint];
Imager.SetStrokeWidth[context, 1.0];
Imager.SetStrokeEnd[context, round];
Imager.SetFont[
context, ImagerFont.Scale[ImagerFont.Find["xerox/pressfonts/helvetica-mrr"], 12.0]];
Imager.TranslateT[context, [0.0, 0.5*11.0*72.0]];
DrawAction[p, context, NIL];
};
ImagerInterpress.DoPage[ref, ContextProc];
ImagerInterpress.Close[ref];
};
};
Commander.Register["///Commands/Vizer", Vizer, "\nUsage: Vizer [FileName]"];
END.