From 79ab675f32332230953c2ad855cd6249d7225eb6 Mon Sep 17 00:00:00 2001 From: Mark Mine Date: Mon, 15 Jan 2001 06:40:14 +0000 Subject: [PATCH] *** empty log message *** --- direct/src/directutil/DirectCameraControl.py | 9 +- direct/src/directutil/DirectManipulation.py | 274 +++++++++++-------- direct/src/directutil/DirectSession.py | 19 +- 3 files changed, 172 insertions(+), 130 deletions(-) diff --git a/direct/src/directutil/DirectCameraControl.py b/direct/src/directutil/DirectCameraControl.py index 99c82ff115..a7b7d318d7 100644 --- a/direct/src/directutil/DirectCameraControl.py +++ b/direct/src/directutil/DirectCameraControl.py @@ -47,13 +47,10 @@ class DirectCameraControl(PandaObject): ] def mouseFlyStart(self): - # Record starting mouse positions - self.initMouseX = direct.dr.mouseX - self.initMouseY = direct.dr.mouseY # Record undo point direct.pushUndo([direct.camera]) # Where are we in the display region? - if ((abs(self.initMouseX) < 0.9) & (abs(self.initMouseY) < 0.9)): + if ((abs(direct.dr.mouseX) < 0.9) & (abs(direct.dr.mouseY) < 0.9)): # MOUSE IS IN CENTRAL REGION # Hide the marker for this kind of motion self.coaMarker.hide() @@ -119,7 +116,7 @@ class DirectCameraControl(PandaObject): self.spawnXZTranslateOrHPanYZoom() # END MOUSE IN CENTRAL REGION else: - if ((abs(self.initMouseX) > 0.9) & (abs(self.initMouseY) > 0.9)): + if ((abs(direct.dr.mouseX) > 0.9) & (abs(direct.dr.mouseY) > 0.9)): # Mouse is in corners, spawn roll task self.spawnMouseRollTask() else: @@ -239,7 +236,7 @@ class DirectCameraControl(PandaObject): taskMgr.spawnTaskNamed(t, 'manipulateCamera') def mouseRotateTask(self, state): - # If moving within frame, ignore motion perpendicular to edge + # If moving outside of center, ignore motion perpendicular to edge if ((state.constrainedDir == 'y') & (abs(direct.dr.mouseX) > 0.9)): deltaX = 0 deltaY = direct.dr.mouseDeltaY diff --git a/direct/src/directutil/DirectManipulation.py b/direct/src/directutil/DirectManipulation.py index f987ba5255..122fcafcc9 100644 --- a/direct/src/directutil/DirectManipulation.py +++ b/direct/src/directutil/DirectManipulation.py @@ -20,9 +20,10 @@ class DirectManipulationControl(PandaObject): self.lastCrankAngle = 0 self.fSetCoa = 0 self.fHitInit = 1 + self.fScaleInit = 1 self.fWidgetTop = 0 self.fFreeManip = 1 - self.fScaling = 1 + self.fScaling = 0 self.unpickable = UNPICKABLE self.mode = None self.actionEvents = [ @@ -61,14 +62,12 @@ class DirectManipulationControl(PandaObject): self.constraint = None # Check to see if we are moving the object # We are moving the object if we either wait long enough - """ taskMgr.spawnTaskNamed( Task.doLater(MANIPULATION_MOVE_DELAY, Task.Task(self.switchToMoveMode), 'manip-move-wait'), 'manip-move-wait') - """ - # Begin manipulating once we move far enough + # Or we move far enough self.moveDir = None watchMouseTask = Task.Task(self.watchMouseTask) watchMouseTask.initX = direct.dr.mouseX @@ -223,52 +222,68 @@ class DirectManipulationControl(PandaObject): def spawnManipulateObjectTask(self): # reset hit-pt flag self.fHitInit = 1 + self.fScaleInit = 1 # record initial offset between widget and camera t = Task.Task(self.manipulateObjectTask) + t.fMouseX = abs(direct.dr.mouseX) > 0.9 + t.fMouseY = abs(direct.dr.mouseY) > 0.9 + if t.fMouseX: + t.constrainedDir = 'y' + else: + t.constrainedDir = 'x' + # Compute widget's xy coords in screen space + t.coaCenter = getScreenXY(direct.widget) + # These are used to rotate about view vector + if t.fMouseX & t.fMouseY: + t.lastAngle = getCrankAngle(t.coaCenter) taskMgr.spawnTaskNamed(t, 'manipulateObject') def manipulateObjectTask(self, state): - + # Widget takes precedence if self.constraint: type = self.constraint[2:] if type == 'post': - self.xlate1D() + self.xlate1D(state) elif type == 'disc': - self.xlate2D() + self.xlate2D(state) elif type == 'ring': - self.rotate1D() + self.rotate1D(state) + # No widget interaction, determine free manip mode elif self.fFreeManip: - if self.fScaling & (not direct.fAlt): - # We had been scaling and changed modes, - # reset object handles + # If we've been scaling and changed modes, reset object handles + if 0 & self.fScaling & (not direct.fAlt): self.objectHandles.transferObjectHandlesScale() self.fScaling = 0 - if direct.fControl: - self.rotate2D() - elif direct.fAlt: + # Alt key switches to a scaling mode + if direct.fAlt: self.fScaling = 1 - self.scale3D() - elif direct.fShift: - self.xlateCamXY() + self.scale3D(state) + # Otherwise, manip mode depends on where you started + elif state.fMouseX & state.fMouseY: + # In the corner, spin around camera's axis + self.rotateAboutViewVector(state) + elif state.fMouseX | state.fMouseY: + # Mouse started elsewhere in the outer frame, rotate + self.rotate2D(state) else: - self.xlateCamXZ() - else: - # MRM: Needed, more elegant fallback - return Task.cont - + # Mouse starte in central region, xlate + # Mode depends on shift key + if direct.fShift: + self.xlateCamXZ(state) + else: + self.xlateCamXY(state) if self.fSetCoa: # Update coa based on current widget position direct.selected.last.mCoa2Dnp.assign( - direct.widget.getMat(direct.selected.last) - ) + direct.widget.getMat(direct.selected.last)) else: # Move the objects with the widget direct.selected.moveWrtWidgetAll() - # Continue return Task.cont - def xlate1D(self): + ### WIDGET MANIPULATION METHODS ### + def xlate1D(self, state): # Constrained 1D Translation along widget axis # Compute nearest hit point along axis and try to keep # that point as close to the current mouse position as possible @@ -285,7 +300,7 @@ class DirectManipulationControl(PandaObject): offset = self.hitPt - self.prevHit direct.widget.setPos(direct.widget, offset) - def xlate2D(self): + def xlate2D(self, state): # Constrained 2D (planar) translation # Compute point of intersection of ray from eyepoint through cursor # to one of the three orthogonal planes on the widget. @@ -301,51 +316,35 @@ class DirectManipulationControl(PandaObject): offset = self.hitPt - self.prevHit direct.widget.setPos(direct.widget, offset) + def rotate1D(self, state): + # Constrained 1D rotation about the widget's main axis (X,Y, or Z) + # Rotation depends upon circular motion of the mouse about the + # projection of the widget's origin on the image plane + # A complete circle about the widget results in a change in + # orientation of 360 degrees. - def xlateCamXZ(self): - """Constrained 2D motion parallel to the camera's image plane - This moves the object in the camera's XZ plane""" - # reset fHitInit - # (in case we later switch to another manipulation mode) - #self.fHitInit = 1 - # Where is the widget relative to current camera view - vWidget2Camera = direct.widget.getPos(direct.camera) - x = vWidget2Camera[0] - y = vWidget2Camera[1] - z = vWidget2Camera[2] - # Move widget (and objects) based upon mouse motion - # Scaled up accordingly based upon widget distance - dr = direct.dr - direct.widget.setX( - direct.camera, - x + 0.5 * dr.mouseDeltaX * dr.nearWidth * (y/dr.near)) - direct.widget.setZ( - direct.camera, - z + 0.5 * dr.mouseDeltaY * dr.nearHeight * (y/dr.near)) - - def xlateCamXY(self): - """Constrained 2D motion perpendicular to camera's image plane - This moves the object in the camera's XY plane""" - # Now, where is the widget relative to current camera view - vWidget2Camera = direct.widget.getPos(direct.camera) - # If this is first time around, record initial y distance + # First initialize hit point/rotation angle if self.fHitInit: self.fHitInit = 0 - # Record widget offset along y - self.initY = vWidget2Camera[1] - # Extract current values - x = vWidget2Camera[0] - y = vWidget2Camera[1] - z = vWidget2Camera[2] - # Move widget (and objects) based upon mouse motion - # Scaled up accordingly based upon widget distance - dr = direct.dr - direct.widget.setPos( - direct.camera, - x + 0.5 * dr.mouseDeltaX * dr.nearWidth * (y/dr.near), - y + self.initY * dr.mouseDeltaY, - z) - + self.rotateAxis = self.constraint[:1] + self.fWidgetTop = self.widgetCheck('top?') + self.rotationCenter = getScreenXY(direct.widget) + self.lastCrankAngle = getCrankAngle(self.rotationCenter) + + # Rotate widget based on how far cursor has swung around origin + newAngle = getCrankAngle(self.rotationCenter) + deltaAngle = self.lastCrankAngle - newAngle + if self.fWidgetTop: + deltaAngle = -1 * deltaAngle + if self.rotateAxis == 'x': + direct.widget.setP(direct.widget, deltaAngle) + elif self.rotateAxis == 'y': + direct.widget.setR(direct.widget, -deltaAngle) + elif self.rotateAxis == 'z': + direct.widget.setH(direct.widget, deltaAngle) + # Record crank angle for next time around + self.lastCrankAngle = newAngle + def widgetCheck(self,type): # Utility to see if we are looking at the top or bottom of # a 2D planar widget or if we are looking at a 2D planar widget @@ -377,34 +376,84 @@ class DirectManipulationControl(PandaObject): # Check angle between two vectors return(abs(widgetDir.dot(widgetAxis)) < .2) - def rotate1D(self): - # Constrained 1D rotation about the widget's main axis (X,Y, or Z) - # Rotation depends upon circular motion of the mouse about the - # projection of the widget's origin on the image plane - # A complete circle about the widget results in a change in - # orientation of 360 degrees. + ### FREE MANIPULATION METHODS ### + def xlateCamXZ(self, state): + """Constrained 2D motion parallel to the camera's image plane + This moves the object in the camera's XZ plane""" + # reset fHitInit in case we later switch to manip mode + self.fHitInit = 1 + # Reset scaling init flag + self.fScaleInit = 1 + # Where is the widget relative to current camera view + vWidget2Camera = direct.widget.getPos(direct.camera) + x = vWidget2Camera[0] + y = vWidget2Camera[1] + z = vWidget2Camera[2] + # Move widget (and objects) based upon mouse motion + # Scaled up accordingly based upon widget distance + dr = direct.dr + direct.widget.setX( + direct.camera, + x + 0.5 * dr.mouseDeltaX * dr.nearWidth * (y/dr.near)) + direct.widget.setZ( + direct.camera, + z + 0.5 * dr.mouseDeltaY * dr.nearHeight * (y/dr.near)) - # First initialize hit point/rotation angle + def xlateCamXY(self, state): + """Constrained 2D motion perpendicular to camera's image plane + This moves the object in the camera's XY plane""" + # Now, where is the widget relative to current camera view + vWidget2Camera = direct.widget.getPos(direct.camera) + # If this is first time around, record initial y distance if self.fHitInit: self.fHitInit = 0 - self.rotateAxis = self.constraint[:1] - self.fWidgetTop = self.widgetCheck('top?') - self.rotationCenter = getScreenXY(direct.widget) - self.lastCrankAngle = getCrankAngle(self.rotationCenter) - - # Rotate widget based on how far cursor has swung around origin - newAngle = getCrankAngle(self.rotationCenter) - deltaAngle = self.lastCrankAngle - newAngle - if self.fWidgetTop: - deltaAngle = -1 * deltaAngle - if self.rotateAxis == 'x': - direct.widget.setP(direct.widget, deltaAngle) - elif self.rotateAxis == 'y': - direct.widget.setR(direct.widget, -deltaAngle) - elif self.rotateAxis == 'z': - direct.widget.setH(direct.widget, deltaAngle) - # Record crank angle for next time around - self.lastCrankAngle = newAngle + # Use distance to widget to scale motion along Y + self.xlateSF = Vec3(vWidget2Camera).length() + # Get widget's current xy coords in screen space + coaCenter = getNearProjectionPoint(direct.widget) + self.deltaNearX = coaCenter[0] - direct.dr.nearVec[0] + # Reset scaling init flag + self.fScaleInit = 1 + # Move selected objects + dr = direct.dr + # Move object in y axis based on mouse motion + newY = vWidget2Camera[1] + self.xlateSF * dr.mouseDeltaY + # Put object at same relative point to mouse in X + newX = (direct.dr.nearVec[0] + self.deltaNearX) * (newY/dr.near) + direct.widget.setPos(direct.camera, newX, newY, vWidget2Camera[2]) + + def rotate2D(self, state): + """ Virtual trackball rotation of widget """ + # Reset init flag in case we switch to another mode + self.fHitInit = 1 + # Reset scaling init flag + self.fScaleInit = 1 + tumbleRate = 360 + # If moving outside of center, ignore motion perpendicular to edge + if ((state.constrainedDir == 'y') & (abs(direct.dr.mouseX) > 0.9)): + deltaX = 0 + deltaY = direct.dr.mouseDeltaY + elif ((state.constrainedDir == 'x') & (abs(direct.dr.mouseY) > 0.9)): + deltaX = direct.dr.mouseDeltaX + deltaY = 0 + else: + deltaX = direct.dr.mouseDeltaX + deltaY = direct.dr.mouseDeltaY + # Mouse motion edge to edge of display region results in one full turn + self.relHpr(direct.camera, deltaX * tumbleRate, + -deltaY * tumbleRate, 0) + + def rotateAboutViewVector(self, state): + # Reset init flag in case we switch to another mode + self.fHitInit = 1 + # Reset scaling init flag + self.fScaleInit = 1 + # Compute current angle + angle = getCrankAngle(state.coaCenter) + deltaAngle = angle - state.lastAngle + state.lastAngle = angle + # Mouse motion edge to edge of display region results in one full turn + self.relHpr(direct.camera, 0, 0, deltaAngle) def relHpr(self, base, h, p, r): # Compute widget2newWidget relative to base coordinate system @@ -424,24 +473,12 @@ class DirectManipulationControl(PandaObject): CSDefault) direct.widget.setHpr(hpr) - def rotate2D(self): - # Virtual trackball or arcball rotation of widget - # Rotation method depends upon variable dd-want-arcball - # Default is virtual trackball (handles 1D rotations better) - self.fHitInit = 1 - tumbleRate = 360 - # Mouse motion edge to edge of display region results in one full turn - self.relHpr(direct.camera, - direct.dr.mouseDeltaX * tumbleRate, - -direct.dr.mouseDeltaY * tumbleRate, - 0) - - def scale3D(self): + def scale3D(self, state): # Scale the selected node based upon up down mouse motion # Mouse motion from edge to edge results in a factor of 4 scaling # From midpoint to edge doubles or halves objects scale - if self.fHitInit: - self.fHitInit = 0 + if self.fScaleInit: + self.fScaleInit = 0 self.manipRef.setPos(direct.widget, 0, 0, 0) self.manipRef.setHpr(direct.camera, 0, 0, 0) self.initScaleMag = Vec3( @@ -449,6 +486,8 @@ class DirectManipulationControl(PandaObject): self.manipRef, 'y')).length() # record initial scale self.initScale = direct.widget.getScale() + # Reset fHitInitFlag + self.fHitInit = 1 # Begin # Scale factor is ratio current mag with init mag currScale = ( @@ -465,6 +504,8 @@ class ObjectHandles(NodePath,PandaObject): # Initialize the superclass NodePath.__init__(self) + # Starts off deactivated + self.fActive = 0 # Load up object handles model and assign it to self self.assign(loader.loadModel('models/misc/objectHandles')) self.node().setName('objectHandles') @@ -526,6 +567,21 @@ class ObjectHandles(NodePath,PandaObject): def manipModeColor(self): self.clearColor() + def toggleWidget(self): + if self.fActive: + self.reparentTo(hidden) + self.fActive = 0 + else: + self.reparentTo(direct.group) + self.fActive = 1 + + def showWidgetIfActive(self): + if self.fActive: + self.reparentTo(direct.group) + + def hideWidget(self): + self.reparentTo(hidden) + def enableHandles(self, handles): if type(handles) == types.ListType: for handle in handles: diff --git a/direct/src/directutil/DirectSession.py b/direct/src/directutil/DirectSession.py index c4e735e917..a709552a2f 100644 --- a/direct/src/directutil/DirectSession.py +++ b/direct/src/directutil/DirectSession.py @@ -226,7 +226,7 @@ class DirectSession(PandaObject): self.readout.reparentTo(render2d) self.readout.setText(dnp.name) # Show the manipulation widget - self.reparentWidgetTo('direct') + self.widget.showWidgetIfActive() # Update camera controls coa to this point # Coa2Camera = Coa2Dnp * Dnp2Camera mCoa2Camera = dnp.mCoa2Dnp * dnp.getMat(self.camera) @@ -257,7 +257,7 @@ class DirectSession(PandaObject): dnp = self.selected.deselect(nodePath) if dnp: # Hide the manipulation widget - self.reparentWidgetTo('hidden') + self.widget.hideWidget() self.readout.reparentTo(hidden) self.readout.setText(' ') taskMgr.removeTasksNamed('followSelectedNodePath') @@ -268,7 +268,7 @@ class DirectSession(PandaObject): def deselectAll(self): self.selected.deselectAll() # Hide the manipulation widget - self.reparentWidgetTo('hidden') + self.widget.hideWidget() self.readout.reparentTo(hidden) self.readout.setText(' ') taskMgr.removeTasksNamed('followSelectedNodePath') @@ -469,19 +469,8 @@ class DirectSession(PandaObject): def hideReadout(self): self.readout.reparentTo(hidden) - def reparentWidgetTo(self, parent): - if parent == 'direct': - self.widget.reparentTo(direct.group) - self.widgetParent = 'direct' - else: - self.widget.reparentTo(hidden) - self.widgetParent = 'hidden' - def toggleWidgetVis(self): - if self.widgetParent == 'direct': - self.reparentWidgetTo('hidden') - else: - self.reparentWidgetTo('direct') + self.widget.toggleWidget() class DisplayRegionList: def __init__(self):