diff --git a/direct/src/directdevices/DirectJoybox.py b/direct/src/directdevices/DirectJoybox.py index 41d4523078..48ebd91296 100644 --- a/direct/src/directdevices/DirectJoybox.py +++ b/direct/src/directdevices/DirectJoybox.py @@ -175,6 +175,9 @@ class DirectJoybox(PandaObject): self.enable() def joyboxFly(self): + # Do nothing if no nodePath selected + if self.nodePath == None: + return hprScale = (self.analogs.normalizeChannel(L_SLIDE, 0.1, 100) * DirectJoybox.hprMultiplier) posScale = (self.analogs.normalizeChannel(R_SLIDE, 0.1, 100) * @@ -215,6 +218,12 @@ class DirectJoybox(PandaObject): self.modifier = [1,1,1,-1,1,0] self.setMode(self.joyboxFly, 'Look At Mode') + def lookAroundMode(self): + self.mapping = [NULL_AXIS, NULL_AXIS, NULL_AXIS, + R_LEFT_RIGHT, R_FWD_BACK, NULL_AXIS] + self.modifier = [0,0,0,-1,-1,0] + self.setMode(self.joyboxFly, 'Lookaround Mode') + def demoMode(self): self.mapping = [R_LEFT_RIGHT, R_FWD_BACK, L_FWD_BACK, R_TWIST, NULL_AXIS, NULL_AXIS] @@ -227,12 +236,6 @@ class DirectJoybox(PandaObject): self.modifier = [1,1,-1,-1,-1,1] self.setMode(self.joyboxFly, 'HprXyz Mode') - def lookaroundMode(self): - self.mapping = [NULL_AXIS, NULL_AXIS, NULL_AXIS, - R_LEFT_RIGHT, R_FWD_BACK, NULL_AXIS] - self.modifier = [0,0,0,-1,-1,0] - self.setMode(self.joyboxFly, 'Lookaround Mode') - def walkthruMode(self): self.mapping = [R_LEFT_RIGHT, R_FWD_BACK, L_TWIST, R_TWIST, L_FWD_BACK, L_LEFT_RIGHT] @@ -243,6 +246,9 @@ class DirectJoybox(PandaObject): self.setMode(self.orbitFly, 'Orbit Mode') def orbitFly(self): + # Do nothing if no nodePath selected + if self.nodePath == None: + return hprScale = (self.analogs.normalizeChannel(L_SLIDE, 0.1, 100) * DirectJoybox.hprMultiplier) posScale = (self.analogs.normalizeChannel(R_SLIDE, 0.1, 100) * diff --git a/direct/src/directtools/DirectSelection.py b/direct/src/directtools/DirectSelection.py index e87ddff07d..469d5b9664 100644 --- a/direct/src/directtools/DirectSelection.py +++ b/direct/src/directtools/DirectSelection.py @@ -380,9 +380,9 @@ class DirectBoundingBox: class SelectionRay: - def __init__(self, camera): - # Create a collision node path attached to the given camera - self.rayCollisionNodePath = camera.attachNewNode( CollisionNode() ) + def __init__(self, parent): + # Create a collision node path attached to the given parent + self.rayCollisionNodePath = parent.attachNewNode( CollisionNode() ) # Don't pay the penalty of drawing this collision ray self.rayCollisionNodePath.hide() self.rayCollisionNode = self.rayCollisionNodePath.node() @@ -419,7 +419,7 @@ class SelectionRay: index = -1 # Pick out the closest object that isn't a widget for i in range(0,numEntries): - entry = direct.iRay.cq.getEntry(i) + entry = self.cq.getEntry(i) node = entry.getIntoNode() # Don't pick hidden nodes if node.isHidden(): @@ -443,8 +443,8 @@ class SelectionRay: # Did we hit an object? if(index >= 0): # Yes! - # Find hit point in camera's space - hitPt = direct.iRay.camToHitPt(index) + # Find hit point in parent's space + hitPt = self.parentToHitPt(index) hitPtDist = Vec3(hitPt - ZERO_POINT).length() return (node, hitPt, hitPtDist) else: @@ -460,11 +460,11 @@ class SelectionRay: # Yes! # Entry 0 is the closest hit point if multiple hits minPt = 0 - # Find hit point in camera's space - hitPt = direct.iRay.camToHitPt(minPt) + # Find hit point in parent's space + hitPt = self.parentToHitPt(minPt) hitPtDist = Vec3(hitPt).length() # Get the associated collision queue object - entry = direct.iRay.cq.getEntry(minPt) + entry = self.cq.getEntry(minPt) # Extract the node node = entry.getIntoNode() # Return info @@ -481,6 +481,55 @@ class SelectionRay: self.cq.sortEntries() return self.numEntries + def pickGeom3D(self, targetNodePath = render, origin = Point3(0), + dir = Vec3(0,0,-1), fIntersectUnpickable = 0): + self.collideWithGeom() + numEntries = self.pick3D(targetNodePath, origin, dir) + # Init index + index = -1 + # Pick out the closest object that isn't a widget + for i in range(0,numEntries): + entry = self.cq.getEntry(i) + node = entry.getIntoNode() + # Don't pick hidden nodes + if node.isHidden(): + pass + # Can pick unpickable, use the first visible node + elif fIntersectUnpickable: + index = i + break + # Is it a named node?, If so, see if it has a name + elif issubclass(node.__class__, NamedNode): + name = node.getName() + if name in self.unpickable: + pass + else: + index = i + break + # Not hidden and not one of the widgets, use it + else: + index = i + break + # Did we hit an object? + if(index >= 0): + # Yes! + # Find hit point in parent's space + hitPt = self.parentToHitPt(index) + hitPtDist = Vec3(hitPt - ZERO_POINT).length() + return (node, hitPt, hitPtDist) + else: + return (None, ZERO_POINT, 0) + + def pick3D(self, targetNodePath, origin, dir): + # Determine ray direction based upon the mouse coordinates + # Note! This has to be a cam object (of type ProjectionNode) + self.ray.setOrigin( origin ) + self.ray.setDirection( dir ) + self.ct.traverse( targetNodePath.node() ) + self.numEntries = self.cq.getNumEntries() + self.cq.sortEntries() + return self.numEntries + def collideWithGeom(self): self.rayCollisionNode.setIntoCollideMask(BitMask32().allOff()) self.rayCollisionNode.setFromCollideMask(BitMask32().allOff()) @@ -496,10 +545,10 @@ class SelectionRay: def objectToHitPt(self, index): return self.cq.getEntry(index).getIntoIntersectionPoint() - def camToHitPt(self, index): + def parentToHitPt(self, index): # Get the specified entry entry = self.cq.getEntry(index) hitPt = entry.getIntoIntersectionPoint() - # Convert point from object local space to camera space + # Convert point from object local space to parent's space return entry.getInvWrtSpace().xformPoint(hitPt) diff --git a/direct/src/directtools/DirectSession.py b/direct/src/directtools/DirectSession.py index 624fe00458..d3edff6e41 100644 --- a/direct/src/directtools/DirectSession.py +++ b/direct/src/directtools/DirectSession.py @@ -59,6 +59,8 @@ class DirectSession(PandaObject): if joybox: from DirectJoybox import * self.joybox = DirectJoybox(joybox) + else: + self.joybox = None else: self.deviceManager = None diff --git a/direct/src/tkpanels/DirectSessionPanel.py b/direct/src/tkpanels/DirectSessionPanel.py index a3c3a32d50..f19cdcad7e 100644 --- a/direct/src/tkpanels/DirectSessionPanel.py +++ b/direct/src/tkpanels/DirectSessionPanel.py @@ -59,6 +59,13 @@ class DirectSessionPanel(AppShell): self.nodePathDict['widget'] = direct.widget self.nodePathNames = ['widget'] + # Dictionary keeping track of all jb node paths selected so far + self.jbNodePathDict = {} + self.jbNodePathDict['none'] = 'No Node Path' + self.jbNodePathDict['widget'] = direct.widget + self.jbNodePathDict['camera'] = direct.camera + self.jbNodePathNames = ['camera', 'selected', 'none'] + # Set up event hooks self.actionEvents = [('undo', self.undoHook), ('pushUndo', self.pushUndoHook), @@ -108,7 +115,7 @@ class DirectSessionPanel(AppShell): self.nodePathMenu.component('entryfield_entry')) self.nodePathMenuBG = ( self.nodePathMenuEntry.configure('background')[3]) - self.nodePathMenu.pack(side = 'left', fill = 'x', expand = 1) + self.nodePathMenu.pack(side = LEFT, fill = X, expand = 1) self.bind(self.nodePathMenu, 'Select node path to manipulate') self.undoButton = Button(menuFrame, text = 'Undo', @@ -117,7 +124,7 @@ class DirectSessionPanel(AppShell): self.undoButton['state'] = 'normal' else: self.undoButton['state'] = 'disabled' - self.undoButton.pack(side = 'left', expand = 0) + self.undoButton.pack(side = LEFT, expand = 0) self.bind(self.undoButton, 'Undo last operation') self.redoButton = Button(menuFrame, text = 'Redo', @@ -126,7 +133,7 @@ class DirectSessionPanel(AppShell): self.redoButton['state'] = 'normal' else: self.redoButton['state'] = 'disabled' - self.redoButton.pack(side = 'left', expand = 0) + self.redoButton.pack(side = LEFT, expand = 0) self.bind(self.redoButton, 'Redo last operation') # The master frame for the dials @@ -135,7 +142,7 @@ class DirectSessionPanel(AppShell): # Scene Graph Explorer sgeFrame = Frame(mainFrame) self.sgeUpdate = Button(sgeFrame, text = 'Update Explorer') - self.sgeUpdate.pack(fill = 'x', expand = 0) + self.sgeUpdate.pack(fill = X, expand = 0) self.SGE = SceneGraphExplorer.SceneGraphExplorer( sgeFrame, nodePath = render, scrolledCanvas_hull_width = 200, @@ -149,8 +156,8 @@ class DirectSessionPanel(AppShell): notebook.pack(fill = BOTH, expand = 1) envPage = notebook.add('Environment') lightsPage = notebook.add('Lights') - renderPage = notebook.add('Render Style') gridPage = notebook.add('Grid') + devicePage = notebook.add('Devices') scenePage = notebook.add('Scene') # Put this here so it isn't called right away notebook['raisecommand'] = self.updateInfo @@ -165,7 +172,7 @@ class DirectSessionPanel(AppShell): self.backgroundColor = VectorWidgets.ColorEntry( bkgrdFrame, text = 'Background Color') self.backgroundColor['command'] = self.setBackgroundColor - self.backgroundColor.pack(fill = 'x', expand = 0) + self.backgroundColor.pack(fill = X, expand = 0) self.bind(self.backgroundColor, 'Set background color') bkgrdFrame.pack(fill = BOTH, expand = 0) @@ -180,7 +187,7 @@ class DirectSessionPanel(AppShell): entry_width = 20, selectioncommand = self.selectDisplayRegionNamed, scrolledlist_items = nameList) - self.drMenu.pack(fill = 'x', expand = 0) + self.drMenu.pack(fill = X, expand = 0) self.bind(self.drMenu, 'Select display region to configure') self.nearPlane = Floater.Floater( @@ -188,7 +195,7 @@ class DirectSessionPanel(AppShell): text = 'Near Plane', min = 0.01) self.nearPlane['command'] = self.setNear - self.nearPlane.pack(fill = 'x', expand = 0) + self.nearPlane.pack(fill = X, expand = 0) self.bind(self.nearPlane, 'Set near plane distance') self.farPlane = Floater.Floater( @@ -196,36 +203,78 @@ class DirectSessionPanel(AppShell): text = 'Far Plane', min = 0.01) self.farPlane['command'] = self.setFar - self.farPlane.pack(fill = 'x', expand = 0) + self.farPlane.pack(fill = X, expand = 0) self.bind(self.farPlane, 'Set far plane distance') - - self.hFov = Floater.Floater( - drFrame, + + fovFrame = Frame(drFrame) + fovFloaterFrame = Frame(fovFrame) + self.hFov = EntryScale.EntryScale( + fovFloaterFrame, text = 'Horizontal FOV', - min = 0.01, max = 179.9) + min = 0.01, max = 170.0) self.hFov['command'] = self.setHFov - self.hFov.pack(fill = 'x', expand = 0) + self.hFov.pack(fill = X, expand = 0) self.bind(self.hFov, 'Set horizontal field of view') - self.vFov = Floater.Floater( - drFrame, + self.vFov = EntryScale.EntryScale( + fovFloaterFrame, text = 'Vertical FOV', - min = 0.01, max = 179.9) + min = 0.01, max = 170.0) self.vFov['command'] = self.setVFov - self.vFov.pack(fill = 'x', expand = 0) + self.vFov.pack(fill = X, expand = 0) self.bind(self.vFov, 'Set vertical field of view') - + fovFloaterFrame.pack(side = LEFT, fill = X, expand = 1) + + frame = Frame(fovFrame) self.lockedFov = BooleanVar() self.lockedFov.set(1) self.lockedFovButton = Checkbutton( - drFrame, - text = 'FOV Locked', - anchor = 'w', justify = 'left', + frame, + text = 'Locked', + anchor = 'w', justify = LEFT, variable = self.lockedFov) - self.lockedFovButton.pack(fill = 'x', expand = 0) + self.lockedFovButton.pack(fill = X, expand = 0) + self.resetFovButton = Button( + frame, + text = 'Reset', + command = self.resetFov) + self.resetFovButton.pack(fill = X, expand = 0) + frame.pack(side = LEFT, fill = X, expand = 0) + fovFrame.pack(fill = X, expand = 1) + + drFrame.pack(fill = BOTH, expand = 0) + ## Render Style ## + toggleFrame = Frame(envPage, borderwidth = 2, relief = 'sunken') + Label(toggleFrame, text = 'Toggle Render Style', + font=('MSSansSerif', 14, 'bold')).pack(expand = 0) + self.toggleBackfaceButton = Button( + toggleFrame, + text = 'Backface', + command = base.toggleBackface) + self.toggleBackfaceButton.pack(side = LEFT, fill = X, expand = 1) + + self.toggleLightsButton = Button( + toggleFrame, + text = 'Lights', + command = direct.lights.toggle) + self.toggleLightsButton.pack(side = LEFT, fill = X, expand = 1) + + self.toggleTextureButton = Button( + toggleFrame, + text = 'Texture', + command = base.toggleTexture) + self.toggleTextureButton.pack(side = LEFT, fill = X, expand = 1) + + self.toggleWireframeButton = Button( + toggleFrame, + text = 'Wireframe', + command = base.toggleWireframe) + self.toggleWireframeButton.pack(fill = X, expand = 1) + toggleFrame.pack(side = LEFT, fill = X, expand = 1) + ## Lights page ## # Lights # lightFrame = Frame(lightsPage, borderwidth = 2, relief = 'sunken') @@ -258,15 +307,15 @@ class DirectSessionPanel(AppShell): # Main light switch mainSwitchFrame = Frame(lightFrame) Label(mainSwitchFrame, - text = 'Lighting:').pack(side = 'left', expand = 0) + text = 'Lighting:').pack(side = LEFT, expand = 0) self.enableLights = BooleanVar() self.enableLightsButton = Checkbutton( mainSwitchFrame, text = 'Enabled/Disabled', variable = self.enableLights, command = self.toggleLights) - self.enableLightsButton.pack(side = 'left', fill = 'x', expand = 0) - mainSwitchFrame.pack(fill = 'x', expand = 0) + self.enableLightsButton.pack(side = LEFT, fill = X, expand = 0) + mainSwitchFrame.pack(fill = X, expand = 0) # Widget to select a light to configure nameList = direct.lights.nameList @@ -277,7 +326,7 @@ class DirectSessionPanel(AppShell): entry_width = 20, selectioncommand = self.selectLightNamed, scrolledlist_items = nameList) - self.lightMenu.pack(side = 'left', fill = 'x', expand = 0) + self.lightMenu.pack(side = LEFT, fill = X, expand = 0) self.bind(self.lightMenu, 'Select light to configure') self.lightActive = BooleanVar() @@ -286,22 +335,22 @@ class DirectSessionPanel(AppShell): text = 'On/Off', variable = self.lightActive, command = self.toggleActiveLight) - self.lightActiveButton.pack(side = 'left', fill = 'x', expand = 0) + self.lightActiveButton.pack(side = LEFT, fill = X, expand = 0) # Pack light menu - lightMenuFrame.pack(fill = 'x', expand = 0, padx = 2) + lightMenuFrame.pack(fill = X, expand = 0, padx = 2) self.lightColor = VectorWidgets.ColorEntry( lightFrame, text = 'Light Color') self.lightColor['command'] = self.setLightColor - self.lightColor.pack(fill = 'x', expand = 0, padx = 4) + self.lightColor.pack(fill = X, expand = 0, padx = 4) self.bind(self.lightColor, 'Set active light color') # Directional light controls self.dSpecularColor = VectorWidgets.ColorEntry( directionalPage, text = 'Specular Color') self.dSpecularColor['command'] = self.setSpecularColor - self.dSpecularColor.pack(fill = 'x', expand = 0) + self.dSpecularColor.pack(fill = X, expand = 0) self.bind(self.dSpecularColor, 'Set directional light specular color') @@ -309,7 +358,7 @@ class DirectSessionPanel(AppShell): self.pSpecularColor = VectorWidgets.ColorEntry( pointPage, text = 'Specular Color') self.pSpecularColor['command'] = self.setSpecularColor - self.pSpecularColor.pack(fill = 'x', expand = 0) + self.pSpecularColor.pack(fill = X, expand = 0) self.bind(self.pSpecularColor, 'Set point light specular color') @@ -318,7 +367,7 @@ class DirectSessionPanel(AppShell): text = 'Constant Attenuation', min = 0.0, max = 1.0, initialValue = 1.0) self.pConstantAttenuation['command'] = self.setConstantAttenuation - self.pConstantAttenuation.pack(fill = 'x', expand = 0) + self.pConstantAttenuation.pack(fill = X, expand = 0) self.bind(self.pConstantAttenuation, 'Set point light constant attenuation') @@ -327,7 +376,7 @@ class DirectSessionPanel(AppShell): text = 'Linear Attenuation', min = 0.0, max = 1.0, initialValue = 0.0) self.pLinearAttenuation['command'] = self.setLinearAttenuation - self.pLinearAttenuation.pack(fill = 'x', expand = 0) + self.pLinearAttenuation.pack(fill = X, expand = 0) self.bind(self.pLinearAttenuation, 'Set point light linear attenuation') @@ -336,7 +385,7 @@ class DirectSessionPanel(AppShell): text = 'Quadratic Attenuation', min = 0.0, max = 1.0, initialValue = 0.0) self.pQuadraticAttenuation['command'] = self.setQuadraticAttenuation - self.pQuadraticAttenuation.pack(fill = 'x', expand = 0) + self.pQuadraticAttenuation.pack(fill = X, expand = 0) self.bind(self.pQuadraticAttenuation, 'Set point light quadratic attenuation') @@ -344,7 +393,7 @@ class DirectSessionPanel(AppShell): self.sSpecularColor = VectorWidgets.ColorEntry( spotPage, text = 'Specular Color') self.sSpecularColor['command'] = self.setSpecularColor - self.sSpecularColor.pack(fill = 'x', expand = 0) + self.sSpecularColor.pack(fill = X, expand = 0) self.bind(self.sSpecularColor, 'Set spot light specular color') @@ -353,7 +402,7 @@ class DirectSessionPanel(AppShell): text = 'Constant Attenuation', min = 0.0, max = 1.0, initialValue = 1.0) self.sConstantAttenuation['command'] = self.setConstantAttenuation - self.sConstantAttenuation.pack(fill = 'x', expand = 0) + self.sConstantAttenuation.pack(fill = X, expand = 0) self.bind(self.sConstantAttenuation, 'Set spot light constant attenuation') @@ -362,7 +411,7 @@ class DirectSessionPanel(AppShell): text = 'Linear Attenuation', min = 0.0, max = 1.0, initialValue = 0.0) self.sLinearAttenuation['command'] = self.setLinearAttenuation - self.sLinearAttenuation.pack(fill = 'x', expand = 0) + self.sLinearAttenuation.pack(fill = X, expand = 0) self.bind(self.sLinearAttenuation, 'Set spot light linear attenuation') @@ -371,7 +420,7 @@ class DirectSessionPanel(AppShell): text = 'Quadratic Attenuation', min = 0.0, max = 1.0, initialValue = 0.0) self.sQuadraticAttenuation['command'] = self.setQuadraticAttenuation - self.sQuadraticAttenuation.pack(fill = 'x', expand = 0) + self.sQuadraticAttenuation.pack(fill = X, expand = 0) self.bind(self.sQuadraticAttenuation, 'Set spot light quadratic attenuation') @@ -380,7 +429,7 @@ class DirectSessionPanel(AppShell): text = 'Exponent', min = 0.0, max = 1.0, initialValue = 0.0) self.sExponent['command'] = self.setExponent - self.sExponent.pack(fill = 'x', expand = 0) + self.sExponent.pack(fill = X, expand = 0) self.bind(self.sExponent, 'Set spot light exponent') @@ -391,33 +440,6 @@ class DirectSessionPanel(AppShell): lightFrame.pack(expand = 1, fill = BOTH) - ## Render Style ## - toggleFrame = Frame(renderPage) - self.toggleBackfaceButton = Button( - toggleFrame, - text = 'Toggle backface', - command = base.toggleBackface) - self.toggleBackfaceButton.pack(fill = 'x', expand = 0) - - self.toggleLightsButton = Button( - toggleFrame, - text = 'Toggle lights', - command = direct.lights.toggle) - self.toggleLightsButton.pack(fill = 'x', expand = 0) - - self.toggleTextureButton = Button( - toggleFrame, - text = 'Toggle texture', - command = base.toggleTexture) - self.toggleTextureButton.pack(fill = 'x', expand = 0) - - self.toggleWireframeButton = Button( - toggleFrame, - text = 'Toggle wireframe', - command = base.toggleWireframe) - self.toggleWireframeButton.pack(fill = 'x', expand = 0) - toggleFrame.pack(fill = BOTH, expand = 0) - ## GRID PAGE ## Label(gridPage, text = 'Grid', font=('MSSansSerif', 14, 'bold')).pack(expand = 0) @@ -425,28 +447,28 @@ class DirectSessionPanel(AppShell): self.enableGridButton = Checkbutton( gridPage, text = 'Enabled/Disabled', - anchor = 'w', justify = 'left', + anchor = 'w', justify = LEFT, variable = self.enableGrid, command = self.toggleGrid) - self.enableGridButton.pack(fill = 'x', expand = 0) + self.enableGridButton.pack(fill = X, expand = 0) self.xyzSnap = BooleanVar() self.xyzSnapButton = Checkbutton( gridPage, text = 'XYZ Snap', - anchor = 'w', justify = 'left', + anchor = 'w', justify = LEFT, variable = self.xyzSnap, command = self.toggleXyzSnap) - self.xyzSnapButton.pack(fill = 'x', expand = 0) + self.xyzSnapButton.pack(fill = X, expand = 0) self.hprSnap = BooleanVar() self.hprSnapButton = Checkbutton( gridPage, text = 'HPR Snap', - anchor = 'w', justify = 'left', + anchor = 'w', justify = LEFT, variable = self.hprSnap, command = self.toggleHprSnap) - self.hprSnapButton.pack(fill = 'x', expand = 0) + self.hprSnapButton.pack(fill = X, expand = 0) self.gridSpacing = Floater.Floater( gridPage, @@ -454,7 +476,7 @@ class DirectSessionPanel(AppShell): min = 0.1, initialValue = direct.grid.getGridSpacing()) self.gridSpacing['command'] = direct.grid.setGridSpacing - self.gridSpacing.pack(fill = 'x', expand = 0) + self.gridSpacing.pack(fill = X, expand = 0) self.gridSize = Floater.Floater( gridPage, @@ -462,7 +484,7 @@ class DirectSessionPanel(AppShell): min = 1.0, initialValue = direct.grid.getGridSize()) self.gridSize['command'] = direct.grid.setGridSize - self.gridSize.pack(fill = 'x', expand = 0) + self.gridSize.pack(fill = X, expand = 0) self.gridSnapAngle = Dial.Dial( gridPage, @@ -472,8 +494,75 @@ class DirectSessionPanel(AppShell): fRollover = 0, initialValue = direct.grid.getSnapAngle()) self.gridSnapAngle['command'] = direct.grid.setSnapAngle - self.gridSnapAngle.pack(fill = 'x', expand = 0) - + self.gridSnapAngle.pack(fill = X, expand = 0) + + ## DEVICE PAGE ## + Label(devicePage, text = 'DEVICES', + font=('MSSansSerif', 14, 'bold')).pack(expand = 0) + + if direct.joybox != None: + joyboxFrame = Frame(devicePage, borderwidth = 2, relief = 'sunken') + Label(joyboxFrame, text = 'Joybox', + font=('MSSansSerif', 14, 'bold')).pack(expand = 0) + self.enableJoybox = BooleanVar() + self.enableJoybox.set(1) + self.enableJoyboxButton = Checkbutton( + joyboxFrame, + text = 'Enabled/Disabled', + anchor = 'w', justify = LEFT, + variable = self.enableJoybox, + command = self.toggleJoybox) + self.enableJoyboxButton.pack(fill = X, expand = 0) + joyboxFrame.pack(fill = X, expand = 0) + + self.jbModeMenu = Pmw.ComboBox( + joyboxFrame, labelpos = W, label_text = 'Joybox Mode:', + label_width = 16, entry_width = 20, + selectioncommand = self.selectJBModeNamed, + scrolledlist_items = ['Joe Mode', 'Drive Mode', 'Orbit Mode', + 'Look At Mode', 'Look Around Mode', + 'Walkthru Mode', 'Demo Mode', + 'HPRXYZ Mode']) + self.jbModeMenu.selectitem('Joe Mode') + self.jbModeMenu.pack(fill = X, expand = 1) + + self.jbNodePathMenu = Pmw.ComboBox( + joyboxFrame, labelpos = W, label_text = 'Joybox Node Path:', + label_width = 16, entry_width = 20, + selectioncommand = self.selectJBNodePathNamed, + scrolledlist_items = self.jbNodePathNames) + self.jbNodePathMenu.selectitem('camera') + self.jbNodePathMenuEntry = ( + self.jbNodePathMenu.component('entryfield_entry')) + self.jbNodePathMenuBG = ( + self.jbNodePathMenuEntry.configure('background')[3]) + self.jbNodePathMenu.pack(fill = X, expand = 1) + self.bind(self.jbNodePathMenu, + 'Select node path to manipulate using the joybox') + + self.jbXyzSF = EntryScale.EntryScale( + joyboxFrame, + text = 'XYZ Scale Factor', + initialValue = 1.0, + hull_relief = RIDGE, hull_borderwidth = 2, + min = 1.0, max = 100.0) + self.jbXyzSF['command'] = ( + lambda v: direct.joybox.setXyzMultiplier(v)) + self.jbXyzSF.pack(fill = X, expand = 0) + self.bind(self.jbXyzSF, 'Set joybox XYZ speed multiplier') + + self.jbHprSF = EntryScale.EntryScale( + joyboxFrame, + text = 'HPR Scale Factor', + initialValue = 1.0, + hull_relief = RIDGE, hull_borderwidth = 2, + min = 1.0, max = 100.0) + self.jbHprSF['command'] = ( + lambda v: direct.joybox.setHprMultiplier(v)) + self.jbHprSF.pack(fill = X, expand = 0) + self.bind(self.jbHprSF, 'Set joybox HPR speed multiplier') + + notebook.setnaturalsize() mainFrame.pack(fill = 'both', expand = 1) @@ -546,6 +635,59 @@ class DirectSessionPanel(AppShell): self.addNodePathToDict(nodePath, self.nodePathNames, self.nodePathMenu, self.nodePathDict) + def selectJBModeNamed(self, name): + if name == 'Joe Mode': + direct.joybox.joeMode() + elif name == 'Drive Mode': + direct.joybox.driveMode() + elif name == 'Orbit Mode': + direct.joybox.orbitMode() + elif name == 'Look At Mode': + direct.joybox.lookAtMode() + elif name == 'Look Around Mode': + direct.joybox.lookAroundMode() + elif name == 'Walkthru Mode': + direct.joybox.walkthruMode() + elif name == 'Demo Mode': + direct.joybox.demoMode() + elif name == 'HPRXYZ Mode': + direct.joybox.hprXyzMode() + + def selectJBNodePathNamed(self, name): + if name == 'selected': + nodePath = direct.selected.last + # Add Combo box entry for this selected object + self.addJBNodePath(nodePath) + else: + # See if node path has already been selected + nodePath = self.jbNodePathDict.get(name, None) + if (nodePath == None): + # If not, see if listbox evals into a node path + try: + nodePath = eval(name) + if isinstance(nodePath, NodePath): + self.addJBNodePath(nodePath) + else: + # Good eval but not a node path, give up + nodePath = None + except: + # Bogus eval + nodePath = None + # Clear bogus entry from listbox + listbox = self.jbNodePathMenu.component('scrolledlist') + listbox.setlist(self.jbNodePathNames) + # Did we finally get something? + if (nodePath != None): + # Yes, select it! + if (nodePath == 'No Node Path'): + direct.joybox.setNodePath(None) + else: + direct.joybox.setNodePath(nodePath) + + def addJBNodePath(self, nodePath): + self.addNodePathToDict(nodePath, self.jbNodePathNames, + self.jbNodePathMenu, self.jbNodePathDict) + def addNodePathToDict(self, nodePath, names, menu, dict): if not nodePath: return @@ -598,7 +740,7 @@ class DirectSessionPanel(AppShell): if dr: if self.lockedFov.get(): sf = hFov/dr.camNode.getHfov() - vFov = dr.camNode.getVfov() * sf + vFov = min(dr.camNode.getVfov() * sf, 170.0) dr.camNode.setFov(hFov, vFov) # Update scale self.vFov.set(vFov, 0) @@ -611,7 +753,7 @@ class DirectSessionPanel(AppShell): if dr: if self.lockedFov.get(): sf = vFov/dr.camNode.getVfov() - hFov = dr.camNode.getHfov() * sf + hFov = min(dr.camNode.getHfov() * sf, 170.0) dr.camNode.setFov(hFov, vFov) # Update scale self.hFov.set(hFov, 0) @@ -619,6 +761,13 @@ class DirectSessionPanel(AppShell): # Just set horizontal dr.camNode.setVfov(vFov) + def resetFov(self): + dr = self.activeDisplayRegion + if dr: + dr.camNode.setFov(45.0, 33.75) + self.hFov.set(45.0, 0) + self.vFov.set(33.75, 0) + # Lights # def selectLightNamed(self, name): self.activeLight = None @@ -723,6 +872,13 @@ class DirectSessionPanel(AppShell): def toggleHprSnap(self): direct.grid.setHprSnap(self.hprSnap.get()) + ## DEVICE CONTROLS + def toggleJoybox(self): + if self.enableJoybox.get(): + direct.joybox.enable() + else: + direct.joybox.disable() + ## UPDATE INFO ## def updateInfo(self, page = 'Environment'): if page == 'Environment': diff --git a/direct/src/tkpanels/MopathRecorder.py b/direct/src/tkpanels/MopathRecorder.py index aa8c42a83e..c5b334d904 100644 --- a/direct/src/tkpanels/MopathRecorder.py +++ b/direct/src/tkpanels/MopathRecorder.py @@ -5,36 +5,28 @@ from PandaObject import * from Tkinter import * from AppShell import * from DirectGeometry import * +from DirectSelection import * from tkFileDialog import * import os import Pmw import Dial import Floater import EntryScale +import VectorWidgets import __builtin__ -""" -To Add -Num Segs lines between knots -Num Ticks -setCvColor -setColor -setHullColor -setKnotColor -setShowCvs -setShowHull -setShowKnots -setNumSegs -setNumTicks -setTickColor -setTickScale -""" +PRF_UTILITIES = [ + 'lambda: direct.camera.lookAt(render)', + 'lambda: direct.camera.setZ(render, 0.0)', + 'lambda s = self: s.playbackMarker.lookAt(render)', + 'lambda s = self: s.playbackMarker.setZ(render, 0.0)', + 'lambda s = self: s.followTerrain(10.0)'] class MopathRecorder(AppShell, PandaObject): # Override class variables here appname = 'Mopath Recorder Panel' frameWidth = 450 - frameHeight = 510 + frameHeight = 535 usecommandarea = 0 usestatusarea = 0 count = 0 @@ -67,8 +59,6 @@ class MopathRecorder(AppShell, PandaObject): self.recorderNodePath = direct.group.attachNewNode(self.name) self.tempCS = self.recorderNodePath.attachNewNode( 'mopathRecorderTempCS') - self.editCS = self.recorderNodePath.attachNewNode( - 'mopathRecorderEditCS') self.playbackMarker = loader.loadModel('models/directmodels/smiley') self.playbackMarker.reparentTo(self.recorderNodePath) self.playbackMarker.hide() @@ -95,14 +85,12 @@ class MopathRecorder(AppShell, PandaObject): self.recNodePathDict['camera'] = direct.camera self.recNodePathDict['widget'] = direct.widget self.recNodePathDict['mopathRecorderTempCS'] = self.tempCS - self.recNodePathDict['edit CS'] = self.editCS self.recNodePathNames = ['marker', 'camera', 'selected'] self.pbNodePathDict = {} self.pbNodePathDict['marker'] = self.playbackMarker self.pbNodePathDict['camera'] = direct.camera self.pbNodePathDict['widget'] = direct.widget self.pbNodePathDict['mopathRecorderTempCS'] = self.tempCS - self.pbNodePathDict['edit CS'] = self.editCS self.pbNodePathNames = ['marker', 'camera', 'selected'] # Count of point sets recorded self.pointSet = [] @@ -112,6 +100,7 @@ class MopathRecorder(AppShell, PandaObject): self.pointSetCount = 0 self.pointSetName = self.name + '-ps-' + `self.pointSetCount` # User callback to call before recording point + self.samplingMode = 'Continuous' self.preRecordFunc = None # Hook to start/stop recording self.startStopHook = 'f6' @@ -125,7 +114,7 @@ class MopathRecorder(AppShell, PandaObject): self.numTicks = 1 # Number of segments to represent each parametric unit # This just affects the visual appearance of the curve - self.numSegs = 20 + self.numSegs = 40 # The nurbs curves self.xyzNurbsCurve = None self.hprNurbsCurve = None @@ -152,6 +141,10 @@ class MopathRecorder(AppShell, PandaObject): self.cropFrom = 0.0 self.cropTo = 0.0 self.fAdjustingValues = 0 + # For terrain following + self.iRayCS = self.recorderNodePath.attachNewNode( + 'mopathRecorderIRayCS') + self.iRay = SelectionRay(self.iRayCS) # Set up event hooks self.actionEvents = [ ('undo', self.undoHook), @@ -194,52 +187,6 @@ class MopathRecorder(AppShell, PandaObject): 'Save current curve as a new point set', label = 'Save Point Set', command = self.savePointSet) - self.menuBar.addcascademenu( - 'Recorder', 'Show/Hide', - statusHelp = 'Show/Hide Mopath Recorder Objects', - tearoff = 1) - self.pathVis = BooleanVar() - self.pathVis.set(1) - self.menuBar.addmenuitem('Show/Hide', 'checkbutton', - 'Toggle Path Visability', - label = 'Toggle Path Vis', - variable = self.pathVis, - command = self.setPathVis) - self.hullVis = BooleanVar() - self.hullVis.set(0) - self.menuBar.addmenuitem('Show/Hide', 'checkbutton', - 'Toggle Hull Visability', - label = 'Toggle Hull Vis', - variable = self.hullVis, - command = self.setHullVis) - self.cvVis = BooleanVar() - self.cvVis.set(0) - self.menuBar.addmenuitem('Show/Hide', 'checkbutton', - 'Toggle CV Visability', - label = 'Toggle CV Vis', - variable = self.cvVis, - command = self.setCvVis) - self.knotVis = BooleanVar() - self.knotVis.set(1) - self.menuBar.addmenuitem('Show/Hide', 'checkbutton', - 'Toggle Knot Visability', - label = 'Toggle Knot Vis', - variable = self.knotVis, - command = self.setKnotVis) - self.tickVis = BooleanVar() - self.tickVis.set(0) - self.menuBar.addmenuitem('Show/Hide', 'checkbutton', - 'Toggle Tick Visability', - label = 'Toggle Tick Vis', - variable = self.tickVis, - command = self.setTickVis) - self.markerVis = BooleanVar() - self.markerVis.set(1) - self.menuBar.addmenuitem('Show/Hide', 'checkbutton', - 'Toggle Marker Visability', - label = 'Toggle Marker Vis', - variable = self.markerVis, - command = self.setMarkerVis) self.menuBar.addmenuitem( 'Recorder', 'command', 'Toggle widget visability', @@ -281,6 +228,10 @@ class MopathRecorder(AppShell, PandaObject): side = LEFT, fill = BOTH, expand = 1) widget.configure(foreground = 'Red', relief = RAISED, borderwidth = 2, anchor = CENTER, width = 16) + widget = self.createButton(frame, 'Recording', 'Add Keyframe', + 'Add Keyframe To Current Path', + self.addKeyframe, + side = LEFT, expand = 1) self.recordingType = StringVar() self.recordingType.set('New Curve') widget = self.createRadiobutton( @@ -381,6 +332,7 @@ class MopathRecorder(AppShell, PandaObject): self.refinePage = self.mainNotebook.add('Refine') self.extendPage = self.mainNotebook.add('Extend') self.cropPage = self.mainNotebook.add('Crop') + self.stylePage = self.mainNotebook.add('Style') ## RECORD PAGE ## recordFrame = Frame(self.recordPage, relief = SUNKEN, @@ -427,44 +379,18 @@ class MopathRecorder(AppShell, PandaObject): frame.pack(expand = 1, fill = X) # PreRecordFunc frame = Frame(recordFrame) - widget = self.createLabeledEntry( + widget = self.createComboBox( frame, 'Recording', 'Pre-Record Func', - 'Function called before recording each point', - command = self.setPreRecordFunc)[0] - label = self.getWidget('Recording', 'Pre-Record Func-Label') - label.configure(width = 16, anchor = W) + 'Function called before sampling each point', + PRF_UTILITIES, self.setPreRecordFunc, + history = 1, expand = 1) + widget.configure(label_width = 16, label_anchor = W) + widget.configure(entryfield_entry_state = 'normal') self.createCheckbutton(frame, 'Recording', 'PRF Active', 'On: Pre Record Func enabled', None, 0, side = LEFT, fill = BOTH, expand = 0) frame.pack(expand = 1, fill = X) - - # Record type - frame = Frame(recordFrame) - self.samplingMode = StringVar() - self.samplingMode.set('Continuous') - widget = self.createRadiobutton( - frame, 'left', - 'Recording', 'Continuous Sampling', - ('New point added to curve fitter every frame'), - self.samplingMode, 'Continuous', self.setSamplingMode, - expand = 1) - widget['anchor'] = 'center' - widget = self.createRadiobutton( - frame, 'left', - 'Recording', 'Keyframe Sampling', - ('Add new point to curve fitter by pressing keyframe button'), - self.samplingMode, 'Keyframe', self.setSamplingMode, - expand = 1) - widget['anchor'] = 'center' - frame.pack(expand = 1, fill = X) - - widget = self.createButton(recordFrame, 'Recording', 'Add Key Frame', - 'Add Keyframe To Current Path', - self.addKeyframe, - side = LEFT, expand = 1) - widget.configure(state = 'disabled') - # Pack record frame recordFrame.pack(fill = X, pady = 2) @@ -497,7 +423,7 @@ class MopathRecorder(AppShell, PandaObject): font=('MSSansSerif', 12, 'bold')).pack() widget = self.createEntryScale( desampleFrame, 'Resample', 'Points Between Samples', - 'Recompute curve using every nth point', + 'Specify number of points to skip between samples', min = 1, max = 100, resolution = 1, command = self.setDesampleFrequency) widget.component('hull')['relief'] = RIDGE @@ -516,6 +442,7 @@ class MopathRecorder(AppShell, PandaObject): 'Begin time of refine pass', resolution = 0.01, command = self.setRecordStart) + widget.onPress = self.setRefineMode widget.onRelease = widget.onReturnRelease = ( lambda s = self: s.getPrePoints('Refine')) widget = self.createEntryScale( @@ -524,18 +451,19 @@ class MopathRecorder(AppShell, PandaObject): 'Time when full control of node path is given during refine pass', resolution = 0.01, command = self.setControlStart) - widget.onRelease = widget.onReturnRelease = self.setRefineMode + widget.onPress = widget.onReturn = self.setRefineMode widget = self.createEntryScale( refineFrame, 'Refine Page', 'Control Stop', 'Time when node path begins transition back to original curve', resolution = 0.01, command = self.setControlStop) - widget.onRelease = widget.onReturnRelease = self.setRefineMode + widget.onPress = widget.onReturn = self.setRefineMode widget = self.createEntryScale(refineFrame, 'Refine Page', 'Refine To', 'Stop time of refine pass', resolution = 0.01, command = self.setRefineStop) + widget.onPress = self.setRefineMode widget.onRelease = widget.onReturnRelease = self.getPostPoints refineFrame.pack(fill = X) @@ -551,6 +479,7 @@ class MopathRecorder(AppShell, PandaObject): 'Begin time of extend pass', resolution = 0.01, command = self.setRecordStart) + widget.onPress = self.setExtendMode widget.onRelease = widget.onReturnRelease = ( lambda s = self: s.getPrePoints('Extend')) widget = self.createEntryScale( @@ -559,7 +488,7 @@ class MopathRecorder(AppShell, PandaObject): 'Time when full control of node path is given during extend pass', resolution = 0.01, command = self.setControlStart) - widget.onRelease = widget.onReturnRelease = self.setExtendMode + widget.onPress = widget.onReturn = self.setExtendMode extendFrame.pack(fill = X) ## CROP PAGE ## @@ -588,6 +517,88 @@ class MopathRecorder(AppShell, PandaObject): self.cropCurve, fill = NONE) cropFrame.pack(fill = X) + ## STYLE PAGE ## + styleFrame = Frame(self.stylePage, relief = SUNKEN, + borderwidth = 2) + label = Label(styleFrame, text = 'CURVE RENDERINNG STYLE', + font=('MSSansSerif', 12, 'bold')) + label.pack(fill = X) + + self.sf = Pmw.ScrolledFrame(styleFrame, horizflex = 'elastic') + self.sf.pack(fill = 'both', expand = 1) + sfFrame = self.sf.interior() + frame = Frame(sfFrame) + widget = self.createCheckbutton( + frame, 'Style', 'Show Path', + 'On: path is visible', self.setPathVis, 1, + side = LEFT, fill = X, expand = 1) + widget = self.createCheckbutton( + frame, 'Style', 'Show Knots', + 'On: path knots are visible', self.setKnotVis, 1, + side = LEFT, fill = X, expand = 1) + widget = self.createCheckbutton( + frame, 'Style', 'Show CVs', + 'On: path CVs are visible', self.setCvVis, 0, + side = LEFT, fill = X, expand = 1) + widget = self.createCheckbutton( + frame, 'Style', 'Show Hull', + 'On: path hull is visible', self.setHullVis, 0, + side = LEFT, fill = X, expand = 1) + widget = self.createCheckbutton( + frame, 'Style', 'Show Marker', + 'On: playback marker is visible', self.setMarkerVis, 0, + side = LEFT, fill = X, expand = 1) + frame.pack(fill = X, expand = 1) + # Sliders + widget = self.createEntryScale( + sfFrame, 'Style', 'Num Segs', + 'Set number of segments used to approximate each parametric unit', + min = 1.0, max = 400, resolution = 1.0, + initialValue = 40, + command = self.setNumSegs, side = TOP) + widget.component('hull')['relief'] = RIDGE + widget = self.createEntryScale( + sfFrame, 'Style', 'Num Ticks', + 'Set number of tick marks drawn for each unit of time', + min = 0.0, max = 10.0, resolution = 1.0, + initialValue = 0.0, + command = self.setNumTicks, side = TOP) + widget.component('hull')['relief'] = RIDGE + widget = self.createEntryScale( + sfFrame, 'Style', 'Tick Scale', + 'Set visible size of time tick marks', + min = 0.01, max = 100.0, resolution = 0.01, + initialValue = 1.0, + command = self.setTickScale, side = TOP) + widget.component('hull')['relief'] = RIDGE + self.createColorEntry( + sfFrame, 'Style', 'Path Color', + 'Color of curve', + command = self.setPathColor, + initialValue = [255.0,255.0,255.0,255.0]) + self.createColorEntry( + sfFrame, 'Style', 'Knot Color', + 'Color of knots', + command = self.setKnotColor, + initialValue = [0,0,255.0,255.0]) + self.createColorEntry( + sfFrame, 'Style', 'CV Color', + 'Color of CVs', + command = self.setCvColor, + initialValue = [255.0,0,0,255.0]) + self.createColorEntry( + sfFrame, 'Style', 'Tick Color', + 'Color of Ticks', + command = self.setTickColor, + initialValue = [255.0,0,0,255.0]) + self.createColorEntry( + sfFrame, 'Style', 'Hull Color', + 'Color of Hull', + command = self.setHullColor, + initialValue = [255.0,128.0,128.0,255.0]) + + styleFrame.pack(fill = X) + self.mainNotebook.setnaturalsize() def pushUndo(self, fResetRedo = 1): @@ -761,33 +772,61 @@ class MopathRecorder(AppShell, PandaObject): # Compute curve self.computeCurves() - def setHullVis(self): - self.xyzNurbsCurveDrawer.setShowHull(self.hullVis.get()) - def setPathVis(self): - if self.pathVis.get(): + if self.getVariable('Style', 'Show Path').get(): self.curveNodePath.show() else: self.curveNodePath.hide() - def setCvVis(self): - self.xyzNurbsCurveDrawer.setShowCvs(self.cvVis.get()) - def setKnotVis(self): - self.xyzNurbsCurveDrawer.setShowKnots(self.knotVis.get()) - - def setTickVis(self): - if self.tickVis.get(): - self.xyzNurbsCurveDrawer.setNumTicks(self.numTicks) - else: - self.xyzNurbsCurveDrawer.setNumTicks(0) + self.xyzNurbsCurveDrawer.setShowKnots( + self.getVariable('Style', 'Show Knots').get()) + def setCvVis(self): + self.xyzNurbsCurveDrawer.setShowCvs( + self.getVariable('Style', 'Show CVs').get()) + + def setHullVis(self): + self.xyzNurbsCurveDrawer.setShowHull( + self.getVariable('Style', 'Show Hull').get()) + def setMarkerVis(self): - if self.markerVis.get(): + if self.getVariable('Style', 'Show Marker').get(): self.playbackMarker.reparentTo(self.recorderNodePath) else: self.playbackMarker.reparentTo(hidden) + + def setNumSegs(self, value): + self.numSegs = int(value) + self.xyzNurbsCurveDrawer.setNumSegs(self.numSegs) + def setNumTicks(self, value): + self.xyzNurbsCurveDrawer.setNumTicks(float(value)) + + def setTickScale(self, value): + self.xyzNurbsCurveDrawer.setTickScale(float(value)) + + def setPathColor(self, color): + self.xyzNurbsCurveDrawer.setColor( + color[0]/255.0,color[1]/255.0,color[2]/255.0) + self.xyzNurbsCurveDrawer.draw() + + def setKnotColor(self, color): + self.xyzNurbsCurveDrawer.setKnotColor( + color[0]/255.0,color[1]/255.0,color[2]/255.0) + + def setCvColor(self, color): + self.xyzNurbsCurveDrawer.setCvColor( + color[0]/255.0,color[1]/255.0,color[2]/255.0) + + def setTickColor(self, color): + self.xyzNurbsCurveDrawer.setTickColor( + color[0]/255.0,color[1]/255.0,color[2]/255.0) + + def setHullColor(self, color): + self.xyzNurbsCurveDrawer.setHullColor( + color[0]/255.0,color[1]/255.0,color[2]/255.0) + def setStartStopHook(self, event = None): # Clear out old hook self.ignore(self.startStopHook) @@ -821,11 +860,13 @@ class MopathRecorder(AppShell, PandaObject): self.hprCurveFitter.reset() self.xyzNurbsCurveDrawer.hide() - def setSamplingMode(self): - if self.samplingMode.get() == 'Keyframe': - self.getWidget('Recording', 'Add Key Frame')['state'] = 'normal' - else: - self.getWidget('Recording', 'Add Key Frame')['state'] = 'disabled' + def setSamplingMode(self, mode): + self.samplingMode = mode + + def disableKeyframeButton(self): + self.getWidget('Recording', 'Add Keyframe')['state'] = 'disabled' + def enableKeyframeButton(self): + self.getWidget('Recording', 'Add Keyframe')['state'] = 'normal' def setRecordingType(self, type): self.recordingType.set(type) @@ -856,12 +897,15 @@ class MopathRecorder(AppShell, PandaObject): # Reset curve fitters self.xyzCurveFitter.reset() self.hprCurveFitter.reset() + # Update sampling mode button if necessary + if self.samplingMode == 'Continuous': + self.disableKeyframeButton() # Create a new point set to hold raw data self.createNewPointSet() # Record nopath's parent self.nodePathParent = self['nodePath'].getParent() # Keyframe mode? - if (self.samplingMode.get() == 'Keyframe'): + if (self.samplingMode == 'Keyframe'): # Add hook self.acceptKeyframeHook() # Record first point @@ -891,14 +935,8 @@ class MopathRecorder(AppShell, PandaObject): t = taskMgr.spawnMethodNamed( self.recordTask, self.name + '-recordTask') t.startTime = globalClock.getTime() - - # Don't want to change record modes - self.getWidget('Recording', 'Continuous Sampling')['state'] = ( - 'disabled') - self.getWidget('Recording', 'Keyframe Sampling')['state'] = ( - 'disabled') else: - if self.samplingMode.get() == 'Continuous': + if self.samplingMode == 'Continuous': # Kill old task taskMgr.removeTasksNamed(self.name + '-recordTask') if ((self.recordingType.get() == 'Refine') | @@ -915,6 +953,9 @@ class MopathRecorder(AppShell, PandaObject): self.addKeyframe(0) # Ignore hook self.ignoreKeyframeHook() + # Reset sampling mode + self.setSamplingMode('Continuous') + self.enableKeyframeButton() # Clean up after refine or extend if ((self.recordingType.get() == 'Refine') | (self.recordingType.get() == 'Extend')): @@ -927,11 +968,6 @@ class MopathRecorder(AppShell, PandaObject): self.setNewCurveMode() # Compute curve self.computeCurves() - # Now you can change record modes - self.getWidget('Recording', 'Continuous Sampling')['state'] = ( - 'normal') - self.getWidget('Recording', 'Keyframe Sampling')['state'] = ( - 'normal') def recordTask(self, state): # Record raw data point @@ -943,6 +979,8 @@ class MopathRecorder(AppShell, PandaObject): # Make sure we're in a recording mode! if (fToggleRecord & (not self.getVariable('Recording', 'Record').get())): + # Set sampling mode + self.setSamplingMode('Keyframe') # This will automatically add the first point self.toggleRecordVar() else: @@ -955,11 +993,10 @@ class MopathRecorder(AppShell, PandaObject): x = t * t return (3 * x) - (2 * t * x) - def setPreRecordFunc(self, event): + def setPreRecordFunc(self, func): # Note: If func is one defined at command prompt, need to set # __builtins__.func = func at command line - self.preRecordFunc = eval( - self.getVariable('Recording', 'Pre-Record Func').get()) + self.preRecordFunc = eval(func) # Update widget to reflect new value self.getVariable('Recording', 'PRF Active').set(1) @@ -1336,8 +1373,24 @@ class MopathRecorder(AppShell, PandaObject): for i in range(self.hprNurbsCurve.getNumKnots()): self.hprNurbsCurve.setKnot(i, sf * self.hprNurbsCurve.getKnot(i)) self.hprNurbsCurve.recompute() - # Update info - self.updateWidgets() + # Scale point set + # Save handle to old point set + oldPointSet = self.pointSet + # Create new point set + self.createNewPointSet() + # Reset curve fitters + self.xyzCurveFitter.reset() + self.hprCurveFitter.reset() + # Now scale values + for time, pos, hpr in oldPointSet: + newTime = time * sf + # Update point set + self.pointSet.append([newTime, Point3(pos), Point3(hpr)]) + # Add it to the curve fitters + self.xyzCurveFitter.addPoint(newTime, pos ) + self.hprCurveFitter.addPoint(newTime, hpr) + # Compute curve + self.computeCurves() def setRecordStart(self,value): self.recordStart = value @@ -1620,6 +1673,14 @@ class MopathRecorder(AppShell, PandaObject): self.xyzNurbsCurve.writeEgg(mopathFilename) self.hprNurbsCurve.writeEgg(mopathFilename) + def followTerrain(self, height = 1.0): + self.iRay.rayCollisionNodePath.reparentTo(self['nodePath']) + node, hitPt, hitPtDist = self.iRay.pickGeom3D() + if node: + self['nodePath'].setZ(self['nodePath'], height - hitPtDist) + self.iRay.rayCollisionNodePath.reparentTo(self.recorderNodePath) + + ## WIDGET UTILITY FUNCTIONS ## def addWidget(self, widget, category, text): self.widgetDict[category + '-' + text] = widget @@ -1639,10 +1700,12 @@ class MopathRecorder(AppShell, PandaObject): variable.set(initialValue) label = Label(frame, text = text) label.pack(side = LEFT, fill = X) + self.bind(label, balloonHelp) self.widgetDict[category + '-' + text + '-Label'] = label entry = Entry(frame, width = width, relief = relief, textvariable = variable) entry.pack(side = LEFT, fill = X, expand = expand) + self.bind(entry, balloonHelp) self.widgetDict[category + '-' + text] = entry self.variableDict[category + '-' + text] = variable if command: @@ -1800,7 +1863,7 @@ class MopathRecorder(AppShell, PandaObject): widget.selectitem(items[0]) # Bind selection command widget['selectioncommand'] = command - widget.pack(side = side, expand = expand) + widget.pack(side = side, fill = fill, expand = expand) # Bind help self.bind(widget, balloonHelp) # Record widget