浏览代码

*** empty log message ***

Mark Mine 25 年之前
父节点
当前提交
79ab675f32

+ 3 - 6
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

+ 165 - 109
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:

+ 4 - 15
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):