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
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];
};