G3dToolControlImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Bloomenthal, November 8, 1992 2:10 pm PST
3d Tool: User Interaction
DIRECTORY Commander, Controls, G2dBasic, G2dVector, G3dBasic, G3dControl, G3dLight, G3dMatrix, G3dPlane, G3dShape, G3dTimeTrees, G3dTool, G3dVector, ImagerSample, Real, ViewerClasses, ViewerOps;
G3dToolControlImpl: CEDAR PROGRAM
IMPORTS G2dVector, G3dControl, G3dMatrix, G3dPlane, G3dShape, G3dTool, G3dVector, ViewerOps
EXPORTS G3dTool
~ BEGIN
Types
ControlProc:    TYPE ~ Controls.ControlProc;
MouseProc:    TYPE ~ Controls.MouseProc;
Box2d:     TYPE ~ G2dBasic.Box;
IntegerPair:    TYPE ~ G3dBasic.IntegerPair;
Pair:      TYPE ~ G3dBasic.Pair;
Triple:     TYPE ~ G3dBasic.Triple;
CameraControl:   TYPE ~ G3dControl.CameraControl;
Light:      TYPE ~ G3dLight.Light;
Matrix:     TYPE ~ G3dMatrix.Matrix;
Shape:     TYPE ~ G3dShape.Shape;
Client:     TYPE ~ G3dTool.Client;
ScreenSequence:   TYPE ~ G3dTool.ScreenSequence;
Tool:      TYPE ~ G3dTool.Tool;
View:      TYPE ~ G3dTool.View;
ViewProc:    TYPE ~ G3dTool.ViewProc;
TTNode:     TYPE ~ G3dTimeTrees.Node;
User Events
Mouse: PUBLIC MouseProc ~ {
SaveState: PROC ~ {
Save: ViewProc ~ {G3dControl.SaveState[t.camera, view.camera]};
G3dTool.DoWithViews[t, Save];
};
t: Tool ¬ NARROW[clientData];
v: View ¬ G3dTool.GetView[viewer, t];
whatChanged: REF ANY ¬ NIL;
mousePos: Pair ¬ [mouse.pos.x, mouse.pos.y];
IF v = NIL THEN RETURN;
G3dTool.SetActiveTool[t];
IF mouse.state # up THEN SELECT TRUE FROM
mouse.pos.x IN [0..20] AND mouse.pos.y IN [0..20] => { -- button (non-graphical)
arcBall updated the camera on last mouse up (mouse-trap prevents slip-off)
IF t.camera.use = view THEN SaveState[];
IF mouse.controlKey
THEN
FOR l: LIST OF NAT ¬ t.activeViews, l.rest WHILE l # NIL DO
IF l.first = v.index THEN EXIT;
REPEAT FINISHED => t.activeViews ¬ CONS[v.index, t.activeViews];
ENDLOOP
ELSE t.activeViews ¬ LIST[v.index];
SetControllerToView[t];
whatChanged ¬ $View;
};
t.client.mouse # NIL OR -- clients supercede selection or querying
(t = G3dTool.GetActiveTool[] AND G3dTool.GetActiveClients[] # NIL) => {
IF t.client.mouse # NIL THEN t.client.mouse[mouse, viewer, t.client.data];
IF t = G3dTool.GetActiveTool[] THEN
FOR c: LIST OF Client ¬ G3dTool.GetActiveClients[], c.rest WHILE c # NIL DO
IF c.first.mouse # NIL THEN c.first.mouse[mouse, viewer, c.first.data];
ENDLOOP;
};
mouse.controlKey AND mouse.shiftKey => { -- control+shift: move Sprite
SELECT mouse.state FROM
down => {
Compute plane perpendicular to view direction & passing through sprite: find 3d
vector that points along z-axis after the view transform. Thus, transform z-axis
by the inverse of view transformation. The view transform includes the viewport.
To transform a vector, premultiply by matrix's inverse (i.e., inverse of inverse).
n: G3dBasic.Quad ¬ G3dMatrix.TransformH[[0., 0., 1.], v.camera.matrix];
p: G3dPlane.Plane ¬ G3dPlane.FromPointAndNormal[t.sprite, [n.x, n.y, n.z], TRUE];
t.spritePlane ¬ [p.x, p.y, p.z, p.w];
};
held => t.sprite ¬
G3dMatrix.TripleFromScreenAndPlane[[mouse.pos.x, mouse.pos.y], t.spritePlane, v.camera.matrix];
ENDCASE;
IF t.drawSprite THEN whatChanged ¬ $Sprite;
};
mouse.shiftKey AND NOT mouse.controlKey => -- shift only: query vertex
IF t.queryVertices AND v.screens # NIL THEN {
sp: G3dTool.ShapePoint ¬ G3dTool.ScreenPick[t, viewer, mouse.pos];
IF sp # [] THEN {t.selectedShape ¬ sp.shape; t.selectedVertex ¬ sp.point};
whatChanged ¬ $Vertex;
};
ENDCASE => { -- select light or shape
IF t.camera.use = view THEN SaveState[];
{ -- what is all this?
hitLight: Light ¬ NIL;
hitShape: Shape ¬ NIL;
IF mouse.controlKey
THEN {
IF t.hitList # NIL AND (t.hitList.rest = NIL OR t.selectedShape < 0)
THEN hitShape ¬ t.shapes[t.hitList.first]
ELSE FOR s: LIST OF NAT ¬ t.hitList, s.rest WHILE s # NIL DO
IF t.shapes[s.first] # t.shapes[t.selectedShape] THEN LOOP;
hitShape ¬ t.shapes[IF s.rest # NIL THEN s.rest.first ELSE t.hitList.first];
EXIT;
ENDLOOP;
}
ELSE {
InBox: PROC [p: Pair, b: Box2d] RETURNS [BOOL] ~ {
RETURN[p.x IN [b.min.x..b.max.x] AND p.y IN [b.min.y..b.max.y]];
};
min: REAL ¬ Real.LargestNumber;
t.hitList ¬ NIL;
t.selectedShape ¬ t.selectedLight ¬ -1;
FOR n: NAT IN [0..v.screens.length) DO
IF NOT v.screens[n].extentValid THEN G3dShape.SetExtent[v.screens[n]];
IF InBox[mousePos, v.screens[n].extent] THEN t.hitList ¬ CONS[n, t.hitList];
ENDLOOP;
FOR s: LIST OF NAT ¬ t.hitList, s.rest WHILE s # NIL DO
screens: ScreenSequence ¬ v.screens[s.first];
p: IntegerPair ¬ screens[G3dControl.ScreenPick[screens, mouse.pos]].intPos;
d: IntegerPair ¬ [mouse.pos.x-p.x, mouse.pos.y-p.y];
sq: INTEGER ¬ d.x*d.x+d.y*d.y;
IF sq < min THEN {min ¬ sq; hitShape ¬ t.shapes[s.first]};
ENDLOOP;
IF t.lights # NIL THEN FOR i: INTEGER IN [0..t.lights.length) DO
d: REAL ¬ G2dVector.SquareDistance[t.lights[i].screen, mousePos];
IF d < min THEN {min ¬ d; hitLight ¬ t.lights[i]};
ENDLOOP;
};
IF hitShape # NIL THEN {
whatChanged ¬ $SelectShape;
IF mouse.button = right AND hitShape.hierarchyData # NIL THEN hitShape ¬
NARROW[G3dTool.ParentObjectNode[NARROW[hitShape.hierarchyData]].object];
FOR n: NAT IN [0..t.shapes.length) DO t.shapes[n].selected ¬ FALSE; ENDLOOP;
SelectShape[t, hitShape, mouse.button]; -- set .selected of s and/or children
};
IF hitLight # NIL THEN {
whatChanged ¬ $SelectLight;
SelectLight[t, hitLight];
};
};
};
IF mouse.state # up AND whatChanged # NIL THEN G3dTool.Repaint[t, whatChanged];
};
Controller: PUBLIC ControlProc ~ {
t: Tool ¬ NARROW[clientData];
G3dTool.SetActiveTool[t];
IF t.camera.use # view AND control = t.camera.fieldOfView
THEN SetControllerToView[t] -- probably want to control the view
ELSE {
s: Shape;
l: Light;
reset: BOOL ¬ control.mouse.state = up AND control.whatChanged = $ArcBallReset;
IF control.mouse.state = down
THEN {
SaveController[t]; -- enable undo and shape transform accumulation
IF control = t.camera.arcBall.rotate.control OR
control = t.camera.arcBall.translate.control THEN RETURN;
}
ELSE IF reset THEN RestoreController[t]; -- undo
SELECT TRUE FROM
control.mouse.state = up AND NOT reset => {
IF t.camera.use = shape THEN SetCameraToIdentity[t.camera];
IF t.camera.use = view THEN {
G3dTool.Repaint[t, $Pose];
IF t.autoRender THEN G3dTool.Render[t];
};
};
control.mouse.state # up OR reset OR control.whatChanged = $TypedIn =>
SELECT t.camera.use FROM
view => G3dTool.Repaint[t, $Camera]; -- draw active view with new transform
shape => IF (s ¬ G3dTool.GetSelectedShape[t]) # NIL THEN { -- control shape(s)
n: TTNode ¬ IF s.hierarchyData#NIL THEN NARROW[s.hierarchyData] ELSE NIL;
m: Matrix ¬ IF n # NIL THEN n.localTransform ELSE s.matrix;
IF NOT reset THEN [] ¬ IF t.camera.arcBall.originBody
THEN G3dMatrix.Mul[t.camera.matrix, t.camera.miscMatrix, m]
ELSE G3dMatrix.Mul[t.camera.miscMatrix, t.camera.matrix, m];
IF n # NIL THEN G3dTool.PropagateShapeMatrices[n];
G3dTool.InvalidateVertexScreens[t, selectedOnly];
G3dTool.Repaint[t, $Pose];
};
light => IF (l ¬ G3dTool.GetSelectedLight[t]) # NIL THEN {
IF NOT reset THEN {
m: Matrix ¬ t.camera.matrix;
l.direction ¬ G3dMatrix.TransformVec[t.camera.miscVector, m];
l.position ¬ G3dVector.Add[t.camera.miscPosition, [m[3][0],m[3][1],m[3][2]]];
};
G3dTool.Repaint[t, $Pose];
};
ENDCASE;
ENDCASE;
};
IF t.client.camera # NIL THEN t.client.camera[control, t.client.data];
};
SetControllerToView: PROC [t: Tool] ~ {
Restore: ViewProc ~ {G3dControl.RestoreState[t.camera, view.camera]};
t.camera.use ¬ view;
ClearArcBall[t.camera.arcBall];
G3dTool.DoWithViews[t, Restore];
};
RestoreController: PROC [t: Tool] ~ {
ClearArcBall[t.camera.arcBall];
SELECT t.camera.use FROM
view => G3dControl.RestoreState[t.camera, t.camera.save];
shape => {
m: Matrix ¬ GetSelectedShapeTransform[t];
IF m # NIL THEN [] ¬ G3dMatrix.CopyMatrix[t.camera.miscMatrix, m];
SetCameraToIdentity[t.camera];
};
light => {
l: Light ¬ G3dTool.GetSelectedLight[t];
IF l # NIL THEN l.direction ¬ t.camera.miscVector;
IF l # NIL THEN l.position ¬ t.camera.miscPosition;
SetCameraToIdentity[t.camera];
};
ENDCASE;
};
SaveController: PROC [t: Tool] ~ {
SELECT t.camera.use FROM
view => G3dControl.SaveState[t.camera, t.camera.save];
shape => t.camera.miscMatrix ¬
G3dMatrix.CopyMatrix[GetSelectedShapeTransform[t], t.camera.miscMatrix];
light => {
l: Light ¬ G3dTool.GetSelectedLight[t];
IF l # NIL THEN t.camera.miscVector ¬ l.direction;
IF l # NIL THEN t.camera.miscPosition ¬ l.position;
};
ENDCASE;
};
GetSelectedShapeTransform: PROC [t: Tool] RETURNS [m: Matrix] ~ {
s: Shape ¬ G3dTool.GetSelectedShape[t];
IF s # NIL THEN m ¬ IF s.hierarchyData # NIL
THEN NARROW[s.hierarchyData, TTNode].localTransform ELSE s.matrix;
};
SelectLight: PROC [t: Tool, l: Light] ~ {
t.camera.use ¬ light;
IF t.lights # NIL THEN
FOR i: INTEGER IN [0..t.lights.length) DO
IF t.lights[i] = l THEN {t.selectedLight ¬ i; EXIT};
ENDLOOP;
t.camera.miscPosition ¬ l.position;
t.camera.miscVector ¬ l.direction;
SetCameraToIdentity[t.camera];
};
SelectShape: PUBLIC PROC [t: Tool, s: Shape, mb: Controls.MouseButton ¬ none] ~ {
node: TTNode ¬ IF s.hierarchyData # NIL THEN NARROW[s.hierarchyData] ELSE NIL;
m: Matrix ¬ IF node # NIL THEN node.localTransform ELSE s.matrix;
IF node # NIL
THEN {
traversal: G3dTool.Traversal ¬ IF mb = left THEN selfOnly ELSE selfAndChildren;
IF m = NIL THEN m ¬ node.localTransform ¬ G3dMatrix.Identity[];
G3dTool.SelectNodeShapes[t.timeTree.activeNode ¬ t.focusNode ¬ node, traversal];
}
ELSE {
s.selected ¬ TRUE;
t.focusNode ¬ NIL;
FOR n: NAT IN [0..t.shapes.length) DO
IF t.shapes[n] = s THEN {t.selectedShape ¬ n; EXIT};
ENDLOOP;
};
t.camera.use ¬ shape;
t.camera.miscMatrix ¬ G3dMatrix.CopyMatrix[m, t.camera.miscMatrix];
SetCameraToIdentity[t.camera];
};
SetCameraToIdentity: PUBLIC PROC [camera: CameraControl] ~ {
G3dControl.SetTripleControls[camera.par.xRot, camera.par.yRot, camera.par.zRot, []];
G3dControl.SetTripleControls[camera.par.xMov, camera.par.yMov, camera.par.zMov, []];
G3dControl.UpdateControl[camera, camera.scale, 1.0];
};
ClearArcBall: PROC [arcBall: G3dControl.ArcBall] ~ {
ViewerOps.PaintViewer[arcBall.rotate.control.viewer, client, FALSE, $ClearArc];
ViewerOps.PaintViewer[arcBall.translate.control.viewer, client, FALSE, $ClearArc];
};
END.