*** empty log message ***

This commit is contained in:
Mark Mine 2001-02-04 06:34:52 +00:00
parent d4d24efb9c
commit 2524515269
5 changed files with 635 additions and 178 deletions

View File

@ -96,19 +96,26 @@ class DirectAnalogs(AnalogNode, PandaObject):
def disable(self):
self.nodePath.reparentTo(base.dataUnused)
def normalize(self, val, min = -1, max = -1):
def normalize(self, val, minVal = -1, maxVal = -1):
# First record sign
if val < 0:
sign = -1
else:
sign = 1
# Zero out values in deadband
val = sign * max(abs(val) - ANALOG_DEADBAND, 0.0)
# Now clamp value between minVal and maxVal
val = CLAMP(val, ANALOG_MIN, ANALOG_MAX)
if abs(val) < ANALOG_DEADBAND:
val = 0.0
return ((max - min) * ((val - ANALOG_MIN) / ANALOG_RANGE)) + min
return (((maxVal - minVal) * ((val - ANALOG_MIN) / ANALOG_RANGE))
+ minVal)
def normalizeChannel(self, chan, min = -1, max = 1):
def normalizeChannel(self, chan, minVal = -1, maxVal = 1):
try:
if (chan == 2) | (chan == 6):
# These channels have reduced range
return self.normalize(self[chan] * 3.0, min, max)
return self.normalize(self[chan] * 3.0, minVal, maxVal)
else:
return self.normalize(self[chan], min, max)
return self.normalize(self[chan], minVal, maxVal)
except IndexError:
return 0.0

View File

@ -30,22 +30,23 @@ class DirectJoybox(PandaObject):
joyboxCount = 0
xyzMultiplier = 1.0
hprMultiplier = 1.0
def __init__(self, nodePath = direct.camera):
def __init__(self, device = 'CerealBox', nodePath = direct.camera):
# See if device manager has been initialized
if direct.deviceManager == None:
direct.deviceManager = DirectDeviceManager()
# Set name
self.name = 'Joybox-' + `DirectJoybox.joyboxCount`
# Get buttons and analogs
self.device = base.config.GetString('joybox-device', 'CerealBox')
self.device = device
self.analogs = direct.deviceManager.createAnalogs(self.device)
self.buttons = direct.deviceManager.createButtons(self.device)
self.aList = [0,0,0,0,0,0,0,0]
self.bList = [0,0,0,0,0,0,0,0]
# For joybox fly mode
self.mapping = [L_LEFT_RIGHT, L_FWD_BACK, L_TWIST,
R_TWIST, R_FWD_BACK, R_LEFT_RIGHT]
self.modifier = [1,1,1,1,1,1]
# Default is joe mode
self.mapping = [R_LEFT_RIGHT, R_FWD_BACK, L_FWD_BACK,
R_TWIST, L_TWIST, NULL_AXIS]
self.modifier = [1,1,1,-1,-1,0]
# Initialize time
self.lastTime = globalClock.getTime()
# Record node path
@ -58,7 +59,7 @@ class DirectJoybox(PandaObject):
# List of functions to cycle through
self.modeList = [self.joeMode, self.driveMode]
# Pick initial mode
self.updateFunc = self.joeFly
self.updateFunc = self.joyboxFly
self.modeName = 'Joe Mode'
# Button registry
self.addButtonEvents()
@ -173,35 +174,8 @@ class DirectJoybox(PandaObject):
self.showMode(self.modeName)
self.enable()
def joeMode(self):
self.setMode(self.joeFly, 'Joe Mode')
def joeFly(self):
hprScale = (self.analogs.normalizeChannel(L_SLIDE, 0.1, 200) *
DirectJoybox.hprMultiplier)
posScale = (self.analogs.normalizeChannel(R_SLIDE, 0.1, 100) *
DirectJoybox.xyzMultiplier)
# XYZ
x = self.aList[R_LEFT_RIGHT]
y = self.aList[R_FWD_BACK]
if self.bList[L_STICK]:
z = 0.0
else:
z = self.aList[L_FWD_BACK]
pos = Vec3(x,y,z) * (posScale * self.deltaTime)
# HPR
h = -1 * self.aList[R_TWIST]
if self.bList[L_STICK]:
p = -1 * self.aList[L_FWD_BACK]
else:
p = 0.0
r = 0.0
hpr = Vec3(h,p,r) * (hprScale * self.deltaTime)
# Move node path
self.nodePath.setPosHpr(self.nodePath, pos, hpr)
def joyboxFly(self):
hprScale = (self.analogs.normalizeChannel(L_SLIDE, 0.1, 200) *
hprScale = (self.analogs.normalizeChannel(L_SLIDE, 0.1, 100) *
DirectJoybox.hprMultiplier)
posScale = (self.analogs.normalizeChannel(R_SLIDE, 0.1, 100) *
DirectJoybox.xyzMultiplier)
@ -223,18 +197,30 @@ class DirectJoybox(PandaObject):
# Move node path
self.nodePath.setPosHpr(self.nodePath, pos, hpr)
def joeMode(self):
self.mapping = [R_LEFT_RIGHT, R_FWD_BACK, L_FWD_BACK,
R_TWIST, L_TWIST, NULL_AXIS]
self.modifier = [1,1,1,-1,-1,0]
self.setMode(self.joyboxFly, 'Joe Mode')
def driveMode(self):
self.mapping = [L_LEFT_RIGHT, R_FWD_BACK, R_TWIST,
R_LEFT_RIGHT, L_FWD_BACK, NULL_AXIS]
self.modifier = [1,1,-1,-1,-1,0]
self.setMode(self.joyboxFly, 'Drive Mode')
def lookAtMode(self):
self.mapping = [R_LEFT_RIGHT, R_TWIST, R_FWD_BACK,
L_LEFT_RIGHT, L_FWD_BACK, NULL_AXIS]
self.modifier = [1,1,1,-1,1,0]
self.setMode(self.joyboxFly, 'Look At Mode')
def demoMode(self):
self.mapping = [R_LEFT_RIGHT, R_FWD_BACK, L_FWD_BACK,
R_TWIST, NULL_AXIS, NULL_AXIS]
self.modifier = [1,1,1,-1,0,0]
self.setMode(self.joyboxFly, 'Demo Mode')
def driveMode(self):
self.mapping = [L_LEFT_RIGHT, R_FWD_BACK, L_FWD_BACK,
R_LEFT_RIGHT, NULL_AXIS, NULL_AXIS]
self.modifier = [1,1,1,-1,0,0]
self.setMode(self.joyboxFly, 'Drive Mode')
def hprXyzMode(self):
self.mapping = [R_LEFT_RIGHT, R_FWD_BACK, R_TWIST,
L_TWIST, L_FWD_BACK, L_LEFT_RIGHT]
@ -257,7 +243,7 @@ class DirectJoybox(PandaObject):
self.setMode(self.orbitFly, 'Orbit Mode')
def orbitFly(self):
hprScale = (self.analogs.normalizeChannel(L_SLIDE, 0.1, 200) *
hprScale = (self.analogs.normalizeChannel(L_SLIDE, 0.1, 100) *
DirectJoybox.hprMultiplier)
posScale = (self.analogs.normalizeChannel(R_SLIDE, 0.1, 100) *
DirectJoybox.xyzMultiplier)

View File

@ -54,6 +54,11 @@ class DirectSession(PandaObject):
# Create a vrpn client vrpn-server or default
if base.config.GetBool('want-vrpn', 0):
self.deviceManager = DirectDeviceManager()
# Automatically create any devices specified in config file
joybox = base.config.GetString('vrpn-joybox-device', '')
if joybox:
from DirectJoybox import *
self.joybox = DirectJoybox(joybox)
else:
self.deviceManager = None

View File

@ -41,12 +41,30 @@ class MopathRecorder(AppShell, PandaObject):
self.initialiseoptions(MopathRecorder)
self.selectRecordNodePathNamed('init')
self.selectPlaybackNodePathNamed('init')
def appInit(self):
# Dictionary of widgets
self.widgetDict = {}
self.variableDict = {}
# Initialize state
self.tempCS = direct.group.attachNewNode('mopathRecorderTempCS')
self.recorderNodePath = direct.group.attachNewNode(self['name'])
self.tempCS = self.recorderNodePath.attachNewNode(
'mopathRecorderTempCS')
self.playbackMarker = loader.loadModel('models/directmodels/happy')
self.playbackMarker.reparentTo(self.recorderNodePath)
# For node path selectors
self.recNodePathDict = {}
self.recNodePathDict['marker'] = self.playbackMarker
self.recNodePathDict['camera'] = direct.camera
self.recNodePathDict['widget'] = direct.widget
self.recNodePathNames = ['marker', 'camera', 'widget', 'selected']
self.pbNodePathDict = {}
self.pbNodePathDict['marker'] = self.playbackMarker
self.pbNodePathDict['camera'] = direct.camera
self.pbNodePathDict['widget'] = direct.widget
self.pbNodePathNames = ['marker', 'camera', 'widget', 'selected']
# Count of point sets recorded
self.pointSet = []
self.pointSetDict = {}
@ -54,18 +72,33 @@ class MopathRecorder(AppShell, PandaObject):
self.pointSetName = self['name'] + '-ps-' + `self.pointSetCount`
# Hook to start/stop recording
self.startStopHook = 'f6'
self.keyframeHook = 'f12'
# Curve fitter object
self.fHasPoints = 0
self.startPos = Point3(0)
self.xyzCurveFitter = CurveFitter()
self.hprCurveFitter = CurveFitter()
# Curve objects
self.nurbsCurve = NurbsCurve()
self.nurbsCurveDrawer = NurbsCurveDrawer(self.nurbsCurve)
self.curveNodePath = None
# Curve variables
# Number of ticks per parametric unit
self.numTicks = 1
# Number of segments to represent each parametric unit
# This just affects the visual appearance of the curve
self.numSegs = 2
# The nurbs curves
self.xyzNurbsCurve = None
self.hprNurbsCurve = None
# Curve drawers
self.nurbsCurveDrawer = NurbsCurveDrawer(NurbsCurve())
self.nurbsCurveDrawer.setNumSegs(self.numSegs)
self.curveNodePath = self.recorderNodePath.attachNewNode(
self.nurbsCurveDrawer.getGeomNode())
# Playback variables
self.playbackTime = 0.0
self.loopPlayback = 1
# Sample variables
self.fEven = 0
self.desampleFrequency = 1
self.numSamples = 100
# Set up event hooks
self.undoEvents = [('undo', self.undoHook),
('pushUndo', self.pushUndoHook),
@ -158,7 +191,7 @@ class MopathRecorder(AppShell, PandaObject):
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(self.menuFrame, text = 'Redo',
@ -167,14 +200,13 @@ class MopathRecorder(AppShell, PandaObject):
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')
# Create notebook pages
self.mainNotebook = Pmw.NoteBook(interior)
self.mainNotebook.pack(fill = BOTH, expand = 1)
self.recordPage = self.mainNotebook.add('Record')
self.cvPage = self.mainNotebook.add('CV Controls')
self.refinePage = self.mainNotebook.add('Refine')
# Put this here so it isn't called right away
self.mainNotebook['raisecommand'] = self.updateInfo
@ -184,85 +216,179 @@ class MopathRecorder(AppShell, PandaObject):
borderwidth = 2)
label = Label(recordFrame, text = 'RECORD PATH',
font=('MSSansSerif', 12, 'bold'))
label.pack(fill = 'x')
label.pack(fill = X)
# Recording Buttons
frame = Frame(recordFrame)
self.createCheckbutton(frame, 'Recording', 'Recording Path',
'On: path is being recorded',
self.toggleRecord, 0,
side = 'left')
self.getWidget('Recording', 'Recording Path').configure(
foreground = 'Red', relief = RAISED, borderwidth = 2,
anchor = CENTER)
self.getWidget('Recording', 'Recording Path').pack(
fill = 'x', expand = 1)
self.createLabeledEntry(frame, 'Recording', 'Start/Stop Hook',
'Hook used to start/stop recording',
initialValue = self.startStopHook,
command = self.setStartStopHook)
self.setStartStopHook(None)
frame.pack(fill = 'x', expand = 1)
recordFrame.pack(fill = 'x')
# Record node path
self.gridFrame = Frame(recordFrame)
self.recNodePathMenu = Pmw.ComboBox(
self.gridFrame, labelpos = W, label_text = 'Record Node Path:',
entry_width = 20,
selectioncommand = self.selectRecordNodePathNamed,
scrolledlist_items = self.recNodePathNames)
self.recNodePathMenu.selectitem('camera')
self.recNodePathMenuEntry = (
self.recNodePathMenu.component('entryfield_entry'))
self.recNodePathMenuBG = (
self.recNodePathMenuEntry.configure('background')[3])
self.recNodePathMenu.grid(row = 0, col = 0, sticky = NSEW)
self.bind(self.recNodePathMenu,
'Select node path to track when recording a new curve')
# Record type
self.recordType = StringVar()
self.recordType.set('Continuous')
widget = self.createRadiobutton(
self.gridFrame, 'left',
'Recording', 'Continuous Recording',
('New point added to curve fitter every frame'),
self.recordType, 'Continuous', self.setRecordType,
expand = 1)
widget['anchor'] = 'center'
widget.pack_forget()
widget.grid(row = 1, col = 0, sticky = NSEW)
widget = self.createRadiobutton(
self.gridFrame, 'left',
'Recording', 'Keyframe Recording',
('Add new point to curve fitter by pressing keyframe button'),
self.recordType, 'Keyframe', self.setRecordType,
expand = 1)
widget['anchor'] = 'center'
widget.pack_forget()
widget.grid(row = 1, col = 1, sticky = NSEW)
# Record button
widget = self.createCheckbutton(
self.gridFrame, 'Recording', 'Recording Path',
'On: path is being recorded', self.toggleRecord, 0,
side = LEFT, fill = BOTH, expand = 1)
widget.pack_forget()
widget.grid(row=2, column=0, sticky = NSEW)
widget.configure(foreground = 'Red', relief = RAISED, borderwidth = 2,
anchor = CENTER, width = 10)
widget = self.createButton(self.gridFrame, 'Recording', 'Add Key Frame',
'Add Keyframe To Current Path',
self.addKeyframe,
side = LEFT, expand = 1)
widget.configure(state = 'disabled', width = 24)
widget.pack_forget()
widget.grid(row=2, column=1, sticky = NSEW)
# Keyframe button
# Hook
widget = self.createLabeledEntry(
self.gridFrame, 'Recording', 'Record Hook',
'Hook used to start/stop recording',
initialValue = self.startStopHook,
command = self.setStartStopHook)[0]
label['width'] = 14
self.setStartStopHook()
widget.pack_forget()
widget.grid(row=3, column=0, sticky = NSEW)
widget = self.createLabeledEntry(
self.gridFrame, 'Recording', 'Keyframe Hook',
'Hook used to add a new keyframe',
initialValue = self.keyframeHook,
command = self.setKeyframeHook)[0]
label['width'] = 14
self.setKeyframeHook()
widget.pack_forget()
widget.grid(row=3, column=1, sticky = NSEW)
self.gridFrame.pack(fill = X, expand = 1)
# This gets the widgets to spread out
self.gridFrame.grid_columnconfigure(1,weight = 1)
recordFrame.pack(fill = X, pady = 2)
# Playback controls
playbackFrame = Frame(self.recordPage, relief = SUNKEN,
borderwidth = 2)
Label(playbackFrame, text = 'PLAYBACK CONTROLS',
font=('MSSansSerif', 12, 'bold')).pack(fill = 'x')
self.createEntryScale(playbackFrame, 'Playback', 'Time',
'Set current playback time',
resolution = 0.01,
command = self.setPlaybackTime)
font=('MSSansSerif', 12, 'bold')).pack(fill = X)
# Playback modifiers
frame = Frame(playbackFrame)
self.createButton(frame, 'Playback', 'Stop', 'Stop playback',
self.stopPlayback, side = 'left', expand = 1)
self.createButton(frame, 'Playback', 'Play', 'Start playback',
self.startPlayback, side = 'left', expand = 1)
self.createButton(frame, 'Playback', 'Pause', 'Pause playback',
self.pausePlayback, side = 'left', expand = 1)
frame.pack(fill = 'x', expand = 1)
playbackFrame.pack(fill = 'x')
## CV PAGE ##
cvFrame = Frame(self.cvPage, relief = SUNKEN,
borderwidth = 2)
label = Label(cvFrame, text = 'CV CONTROLS',
font=('MSSansSerif', 12, 'bold'))
label.pack(fill = 'x')
self.createEntryScale(cvFrame, 'CV Controls', 'Delta Pos',
'Position threshold between selected points',
resolution = 0.01,
command = self.setDeltaPos)
self.createEntryScale(cvFrame, 'CV Controls', 'Delta Hpr',
'Orientation threshold between selected points',
resolution = 0.01,
command = self.setDeltaHpr)
self.createEntryScale(cvFrame, 'CV Controls', 'Delta Time',
'Time threshold between selected points',
resolution = 0.01,
command = self.setDeltaTime)
# Constant velocity frame
frame = Frame(cvFrame)
self.createCheckbutton(frame, 'CV Controls', 'Constant Velocity',
'On: Resulting path has constant velocity',
self.toggleConstantVelocity, 0,
side = 'left')
self.getWidget('CV Controls', 'Constant Velocity').configure(
relief = RAISED, borderwidth = 2, anchor = CENTER)
self.getWidget('CV Controls', 'Constant Velocity').pack(
fill = 'x', expand = 1)
self.createLabeledEntry(frame, 'CV Controls', 'Path Duration',
# Playback node path
self.pbNodePathMenu = Pmw.ComboBox(
frame, labelpos = W, label_text = 'Playback Node Path:',
entry_width = 20,
selectioncommand = self.selectPlaybackNodePathNamed,
scrolledlist_items = self.pbNodePathNames)
self.pbNodePathMenu.selectitem('camera')
self.pbNodePathMenuEntry = (
self.pbNodePathMenu.component('entryfield_entry'))
self.pbNodePathMenuBG = (
self.pbNodePathMenuEntry.configure('background')[3])
self.pbNodePathMenu.pack(side = LEFT, fill = X, expand = 1)
self.bind(self.pbNodePathMenu,
'Select node path to fly along path during playback')
# Duration entry
self.createLabeledEntry(frame, 'Resample', 'Path Duration',
'Set total curve duration',
command = self.setTotalTime)
frame.pack(fill = 'x', expand = 1)
cvFrame.pack(fill = 'x')
command = self.setPathDuration)
frame.pack(fill = X, expand = 1)
frame = Frame(playbackFrame)
widget = self.createEntryScale(
frame, 'Playback', 'Time', 'Set current playback time',
resolution = 0.01, command = self.setPlaybackTime, side = LEFT)
widget.component('hull')['relief'] = RIDGE
# Kill playback task if drag slider
widget.component('scale').bind(
'<ButtonPress-1>', lambda e = None, s = self: s.stopPlayback())
self.createCheckbutton(frame, 'Playback', 'Loop',
'On: loop playback',
self.setLoopPlayback, self.loopPlayback,
side = LEFT, fill = BOTH, expand = 0)
frame.pack(fill = X, expand = 1)
# Start stop buttons
frame = Frame(playbackFrame)
widget = self.createButton(frame, 'Playback', '<<',
'Jump to start of playback',
self.jumpToStartOfPlayback,
side = LEFT, expand = 1)
widget['font'] = (('MSSansSerif', 12, 'bold'))
widget = self.createCheckbutton(frame, 'Playback', 'Play',
'Start/Stop playback',
self.startStopPlayback, 0,
side = LEFT, fill = BOTH, expand = 1)
widget.configure(anchor = 'center', justify = 'center',
relief = RAISED, font = ('MSSansSerif', 12, 'bold'))
widget = self.createButton(frame, 'Playback', '>>',
'Jump to end of playback',
self.jumpToEndOfPlayback,
side = LEFT, expand = 1)
widget['font'] = (('MSSansSerif', 12, 'bold'))
frame.pack(fill = X, expand = 1)
playbackFrame.pack(fill = X, pady = 2)
# Desample
desampleFrame = Frame(
self.recordPage, relief = SUNKEN, borderwidth = 2)
Label(desampleFrame, text = 'DESAMPLE CURVE',
font=('MSSansSerif', 12, 'bold')).pack()
widget = self.createEntryScale(
desampleFrame, 'Resample', 'Points Between Samples',
'Recompute curve using every nth point',
resolution = 1, max = 100, command = self.setDesampleFrequency)
widget.component('hull')['relief'] = RIDGE
widget.onRelease = widget.onReturnRelease = self.desampleCurve
desampleFrame.pack(fill = X, expand = 0, pady = 2)
# Resample
resampleFrame = Frame(
self.recordPage, relief = SUNKEN, borderwidth = 2)
label = Label(resampleFrame, text = 'RESAMPLE CURVE',
font=('MSSansSerif', 12, 'bold')).pack()
widget = self.createEntryScale(
resampleFrame, 'Resample', 'Num. Samples',
'Number of samples in resampled curve',
resolution = 1, max = 1000, command = self.setNumSamples,
side = LEFT)
widget.component('hull')['relief'] = RIDGE
widget.onRelease = widget.onReturnRelease = self.sampleCurve
self.createCheckbutton(resampleFrame, 'Resample', 'Even',
'On: Resulting path has constant velocity',
self.setEven, self.fEven,
side = LEFT, fill = BOTH, expand = 0)
resampleFrame.pack(fill = X, expand = 0, pady = 2)
## REFINE PAGE ##
refineFrame = Frame(self.refinePage, relief = SUNKEN,
borderwidth = 2)
label = Label(refineFrame, text = 'REFINE CURVE',
font=('MSSansSerif', 12, 'bold'))
label.pack(fill = 'x')
label.pack(fill = X)
self.createEntryScale(refineFrame, 'Refine Page', 'From',
'Begin time of refine pass',
resolution = 0.01,
@ -271,12 +397,12 @@ class MopathRecorder(AppShell, PandaObject):
'Stop time of refine pass',
resolution = 0.01,
command = self.setRefineStop)
refineFrame.pack(fill = 'x')
refineFrame.pack(fill = X)
offsetFrame = Frame(self.refinePage)
self.createButton(offsetFrame, 'Refine Page', 'Offset',
'Zero refine curve offset',
self.resetOffset, side = 'left')
self.resetOffset, side = LEFT)
self.createLabeledEntry(offsetFrame, 'Refine Page', 'X',
'Refine pass X offset', width = 3, expand = 1)
self.createLabeledEntry(offsetFrame, 'Refine Page', 'Y',
@ -289,12 +415,12 @@ class MopathRecorder(AppShell, PandaObject):
'Refine pass P offset', width = 3, expand = 1)
self.createLabeledEntry(offsetFrame, 'Refine Page', 'R',
'Refine pass R offset', width = 3, expand = 1)
offsetFrame.pack(fill = 'x')
offsetFrame.pack(fill = X)
frame = Frame(self.refinePage)
self.createButton(frame, 'Refine Page', 'Speed',
'Reset refine speed',
self.resetRefineSpeed, side = 'left',)
self.resetRefineSpeed, side = LEFT,)
self.mainNotebook.setnaturalsize()
@ -337,7 +463,8 @@ class MopathRecorder(AppShell, PandaObject):
self.ignore(event)
# remove start stop hook
self.ignore(self.startStopHook)
self.tempCS.removeNode()
self.ignore(self.keyframeHook)
self.recorderNodePath.removeNode()
def createNewPointSet(self):
self.pointSet = []
@ -351,11 +478,24 @@ class MopathRecorder(AppShell, PandaObject):
names = list(listbox.get(0,'end'))
names.append(self.pointSetName)
scrolledList.setlist(names)
comboBox.selectitem(self.pointSetName)
# Update count
self.pointSetCount += 1
def selectPointSetNamed(self, name):
self.pointSet = self.pointSetDict.get(name, None)
# Reload points into curve fitter
# Reset curve fitters
self.xyzCurveFitter.reset()
self.hprCurveFitter.reset()
for time, pos, hpr in self.pointSet:
# Add it to the curve fitters
self.xyzCurveFitter.addPoint(time, pos )
self.hprCurveFitter.addPoint(time, hpr)
self.fHasPoints = 1
# Compute curve
self.computeCurves()
def setTraceVis(self):
print self.traceVis.get()
@ -382,17 +522,41 @@ class MopathRecorder(AppShell, PandaObject):
self.nurbsCurveDrawer.setNumTicks(0)
def setMarkerVis(self):
print self.markerVis.get()
if self.markerVis.get():
self.playbackMarker.reparentTo(self.recorderNodePath)
else:
self.playbackMarker.reparentTo(hidden)
def setStartStopHook(self, event):
def setStartStopHook(self, event = None):
# Clear out old hook
self.ignore(self.startStopHook)
# Record new one
hook = self.getVariable('Recording', 'Start/Stop Hook').get()
hook = self.getVariable('Recording', 'Record Hook').get()
self.startStopHook = hook
# Add new one
self.accept(self.startStopHook, self.toggleRecordVar)
def setKeyframeHook(self, event = None):
# Clear out old hook
self.ignoreKeyframeHook()
# Record new one
hook = self.getVariable('Recording', 'Keyframe Hook').get()
self.keyframeHook = hook
def acceptKeyframeHook(self):
# Add new one
self.accept(self.keyframeHook, self.addKeyframe)
def ignoreKeyframeHook(self):
# Clear out old hook
self.ignore(self.keyframeHook)
def setRecordType(self):
if self.recordType.get() == 'Keyframe':
self.getWidget('Recording', 'Add Key Frame')['state'] = 'normal'
else:
self.getWidget('Recording', 'Add Key Frame')['state'] = 'disabled'
def toggleRecordVar(self):
# Get recording variable
v = self.getVariable('Recording', 'Recording Path')
@ -410,18 +574,52 @@ class MopathRecorder(AppShell, PandaObject):
self.hprCurveFitter.reset()
# Create a new point set to hold raw data
self.createNewPointSet()
# Start new task
t = taskMgr.spawnMethodNamed(
self.recordTask, self['name'] + '-recordTask')
t.startTime = globalClock.getTime()
t.uponDeath = self.endRecordTask
# Continuous or keyframe?
if self.recordType.get() == 'Continuous':
# Start new task
t = taskMgr.spawnMethodNamed(
self.recordTask, self['name'] + '-recordTask')
t.startTime = globalClock.getTime()
t.uponDeath = self.endRecordTask
else:
# Add hook
self.acceptKeyframeHook()
# Record first point
self.startPos = self['nodePath'].getPos()
self.recordPoint(0.0)
# Don't want to change record modes
self.getWidget('Recording', 'Continuous Recording')['state'] = (
'disabled')
self.getWidget('Recording', 'Keyframe Recording')['state'] = (
'disabled')
else:
# Kill old task
taskMgr.removeTasksNamed(self['name'] + '-recordTask')
if self.recordType.get() == 'Continuous':
# Kill old task
taskMgr.removeTasksNamed(self['name'] + '-recordTask')
else:
# Add last point
self.addKeyframe()
# Ignore hook
self.ignoreKeyframeHook()
# Compute curve
self.computeCurves()
# Now you can change record modes
self.getWidget('Recording', 'Continuous Recording')['state'] = (
'normal')
self.getWidget('Recording', 'Keyframe Recording')['state'] = (
'normal')
def recordTask(self, state):
# Record raw data point
time = globalClock.getTime() - state.startTime
self.recordPoint(time)
return Task.cont
def endRecordTask(self, state):
self.computeCurves()
def recordPoint(self, time):
pos = self['nodePath'].getPos()
hpr = self['nodePath'].getHpr()
# Add it to the point set
@ -429,45 +627,278 @@ class MopathRecorder(AppShell, PandaObject):
# Add it to the curve fitters
self.xyzCurveFitter.addPoint(time, pos )
self.hprCurveFitter.addPoint(time, hpr)
return Task.cont
self.fHasPoints = 1
def endRecordTask(self, state):
# Create curve
def addKeyframe(self):
time = Vec3(self['nodePath'].getPos() - self.startPos).length()
self.recordPoint(time)
def computeCurves(self):
# MRM: Would be better if curvefitter had getNumPoints
if not self.fHasPoints:
print 'MopathRecorder: Must define curve first'
return
# Create curves
# XYZ
self.xyzCurveFitter.computeTangents(1)
self.hprCurveFitter.computeTangents(1)
self.nurbsCurve = self.xyzCurveFitter.makeNurbs()
self.nurbsCurveDrawer.setCurve(self.nurbsCurve)
self.nurbsCurveDrawer.setNumSegs(self.numSegs)
self.xyzNurbsCurve = self.xyzCurveFitter.makeNurbs()
self.nurbsCurveDrawer.setCurve(self.xyzNurbsCurve)
self.nurbsCurveDrawer.draw()
self.curveNodePath = render.attachNewNode(
self.nurbsCurveDrawer.getGeomNode())
def setPlaybackTime(self, time):
print time
# HPR
self.hprCurveFitter.wrapHpr()
self.hprCurveFitter.computeTangents(1)
self.hprNurbsCurve = self.hprCurveFitter.makeNurbs()
# Update widget based on new curve
self.updateCurveInfo()
def stopPlayback(self):
print 'stop'
def updateCurveInfo(self):
if not self.xyzNurbsCurve:
return
# Widgets depending on max T
maxT = '%.2f' % self.xyzNurbsCurve.getMaxT()
self.getWidget('Playback', 'Time').configure(max = maxT)
self.getVariable('Resample', 'Path Duration').set(maxT)
self.getWidget('Refine Page', 'From').configure(max = maxT)
self.getWidget('Refine Page', 'To').configure(max = maxT)
self.maxT = float(maxT)
# Widgets depending on number of knots
numKnots = self.xyzNurbsCurve.getNumKnots()
self.getWidget('Resample', 'Points Between Samples')['max'] = numKnots
self.getWidget('Resample', 'Num. Samples')['max'] = 2 * numKnots
def selectRecordNodePathNamed(self, name):
nodePath = None
if name == 'init':
nodePath = self['nodePath']
# Add Combo box entry for the initial node path
self.addRecordNodePath(nodePath)
elif name == 'selected':
nodePath = direct.selected.last
# Add Combo box entry for this selected object
self.addRecordNodePath(nodePath)
else:
nodePath = self.recNodePathDict.get(name, None)
if (nodePath == None):
# See if this evaluates into a node path
try:
nodePath = eval(name)
if isinstance(nodePath, NodePath):
self.addRecordNodePath(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.recNodePathMenu.component('scrolledlist')
listbox.setlist(self.recNodePathNames)
else:
if name == 'widget':
# Record relationship between selected nodes and widget
direct.selected.getWrtAll()
# Update active node path
self.setRecordNodePath(nodePath)
def setRecordNodePath(self, nodePath):
self['nodePath'] = nodePath
if self['nodePath']:
self.recNodePathMenuEntry.configure(
background = self.recNodePathMenuBG)
else:
# Flash entry
self.recNodePathMenuEntry.configure(background = 'Pink')
def selectPlaybackNodePathNamed(self, name):
nodePath = None
if name == 'init':
nodePath = self['nodePath']
# Add Combo box entry for the initial node path
self.addPlaybackNodePath(nodePath)
elif name == 'selected':
nodePath = direct.selected.last
# Add Combo box entry for this selected object
self.addPlaybackNodePath(nodePath)
else:
nodePath = self.pbNodePathDict.get(name, None)
if (nodePath == None):
# See if this evaluates into a node path
try:
nodePath = eval(name)
if isinstance(nodePath, NodePath):
self.addPlaybackNodePath(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.pbNodePathMenu.component('scrolledlist')
listbox.setlist(self.pbNodePathNames)
else:
if name == 'widget':
# Record relationship between selected nodes and widget
direct.selected.getWrtAll()
# Update active node path
self.setPlaybackNodePath(nodePath)
def setPlaybackNodePath(self, nodePath):
self.playbackNodePath = nodePath
if self.playbackNodePath:
self.pbNodePathMenuEntry.configure(
background = self.pbNodePathMenuBG)
else:
# Flash entry
self.pbNodePathMenuEntry.configure(background = 'Pink')
def addRecordNodePath(self, nodePath):
self.addNodePathToDict(nodePath, self.recNodePathNames,
self.recNodePathMenu, self.recNodePathDict)
def addPlaybackNodePath(self, nodePath):
self.addNodePathToDict(nodePath, self.pbNodePathNames,
self.pbNodePathMenu, self.pbNodePathDict)
def addNodePathToDict(self, nodePath, names, menu, dict):
if not nodePath:
return
# Get node path's name
name = nodePath.getName()
if name in ['parent', 'render', 'camera', 'marker']:
dictName = name
else:
# Generate a unique name for the dict
dictName = name + '-' + `nodePath.id().this`
if not dict.has_key(dictName):
# Update combo box to include new item
names.append(dictName)
listbox = menu.component('scrolledlist')
listbox.setlist(names)
# Add new item to dictionary
dict[dictName] = nodePath
menu.selectitem(dictName)
def setLoopPlayback(self):
self.loopPlayback = self.getVariable('Playback', 'Loop').get()
def setPlaybackTime(self, time):
self.playbackGoTo(time)
def playbackGoTo(self, time):
if (self.xyzNurbsCurve == None) & (self.hprNurbsCurve == None):
return
self.playbackTime = CLAMP(time, 0.0, self.maxT)
if self.xyzNurbsCurve != None:
pos = Vec3(0)
self.xyzNurbsCurve.getPoint(self.playbackTime, pos)
self.playbackNodePath.setPos(pos)
self.playbackNodePath.setPos(pos)
if self.hprNurbsCurve != None:
hpr = Vec3(0)
self.hprNurbsCurve.getPoint(self.playbackTime, hpr)
self.playbackNodePath.setHpr(hpr)
def startPlayback(self):
print 'start'
if (self.xyzNurbsCurve == None) & (self.hprNurbsCurve == None):
return
# Kill any existing tasks
self.stopPlayback()
# Make sure checkbutton is set
self.getVariable('Playback', 'Play').set(1)
# Start new playback task
t = taskMgr.spawnMethodNamed(
self.playbackTask, self['name'] + '-playbackTask')
t.startOffset = self.playbackTime
t.startTime = globalClock.getTime()
def playbackTask(self, state):
dTime = globalClock.getTime() - state.startTime
if self.loopPlayback:
cTime = (state.startOffset + dTime) % self.maxT
else:
cTime = CLAMP(state.startOffset + dTime, 0.0, self.maxT)
self.getWidget('Playback', 'Time').set(cTime)
# Stop task if not looping and at end of curve
if ((self.loopPlayback == 0) & ((cTime + 0.01) > self.maxT)):
self.stopPlayback()
return Task.done
return Task.cont
def pausePlayback(self):
print 'pause'
def stopPlayback(self):
self.getVariable('Playback', 'Play').set(0)
taskMgr.removeTasksNamed(self['name'] + '-playbackTask')
def setDeltaPos(self, dPos):
print dPos
def jumpToStartOfPlayback(self):
self.stopPlayback()
self.getWidget('Playback', 'Time').set(0.0)
def setDeltaHpr(self, dHpr):
print dHpr
def jumpToEndOfPlayback(self):
self.stopPlayback()
if self.xyzNurbsCurve != None:
self.getWidget('Playback', 'Time').set(self.maxT)
def setDeltaTime(self, dTime):
print dTime
def startStopPlayback(self):
if self.getVariable('Playback', 'Play').get():
self.startPlayback()
else:
self.stopPlayback()
def toggleConstantVelocity(self):
print self.getWidget('CV Controls', 'Constant Velocity').get()
def setDesampleFrequency(self, frequency):
self.desampleFrequency = frequency
def desampleCurve(self):
if not self.fHasPoints:
print 'MopathRecorder: Must define curve first'
return
# Record current curve length
maxT = self.maxT
# NOTE: This is destructive, points will be deleted from curve fitter
self.xyzCurveFitter.desample(self.desampleFrequency)
self.hprCurveFitter.desample(self.desampleFrequency)
self.computeCurves()
# Resize curve to original duration
self.setPathDurationTo(maxT)
def setTotalTime(self):
print 'path duration'
def setNumSamples(self, numSamples):
self.numSamples = int(numSamples)
def sampleCurve(self):
if (self.xyzNurbsCurve == None) & (self.hprNurbsCurve == None):
print 'MopathRecorder: Must define curve first'
return
# Record current curve length
maxT = self.maxT
# Reset curve fitters
self.xyzCurveFitter.reset()
self.hprCurveFitter.reset()
# Get new data points based on given curve
self.xyzCurveFitter.sample(
self.xyzNurbsCurve, self.numSamples, self.fEven)
self.hprCurveFitter.sample(
self.hprNurbsCurve, self.numSamples, self.fEven)
self.computeCurves()
# Resize curve to original duration
self.setPathDurationTo(maxT)
def setEven(self):
self.fEven = self.getVariable('Resample', 'Even').get()
def setPathDuration(self, event):
newMaxT = float(self.getWidget('Resample', 'Path Duration').get())
self.setPathDurationTo(newMaxT)
def setPathDurationTo(self, newMaxT):
sf = newMaxT/self.maxT
# Scale knots
for i in range(self.xyzNurbsCurve.getNumKnots()):
self.xyzNurbsCurve.setKnot(i, sf * self.xyzNurbsCurve.getKnot(i))
self.xyzNurbsCurve.recompute()
for i in range(self.hprNurbsCurve.getNumKnots()):
self.hprNurbsCurve.setKnot(i, sf * self.hprNurbsCurve.getKnot(i))
self.hprNurbsCurve.recompute()
# Update info
self.updateCurveInfo()
def setRefineStart(self,value):
print 'refine start'
@ -492,21 +923,24 @@ class MopathRecorder(AppShell, PandaObject):
return self.variableDict[category + '-' + text]
def createLabeledEntry(self, parent, category, text, balloonHelp,
initialValue = '',
command = None, relief = 'sunken',
side = 'left', expand = 1, width = 12):
initialValue = '', command = None,
relief = 'sunken', side = LEFT,
expand = 1, width = 12):
frame = Frame(parent)
variable = StringVar()
variable.set(initialValue)
frame = Frame(parent)
Label(frame, text = text).pack(side = 'left', fill = 'x')
label = Label(frame, text = text)
label.pack(side = LEFT, fill = X)
self.widgetDict[category + '-' + text + '-Label'] = label
entry = Entry(frame, width = width, relief = relief,
textvariable = variable)
entry.pack(side = 'left', fill = 'x', expand = expand)
entry.pack(side = LEFT, fill = X, expand = expand)
self.widgetDict[category + '-' + text] = entry
self.variableDict[category + '-' + text] = variable
if command:
entry.bind('<Return>', command)
frame.pack(side = side, fill = 'x', expand = expand)
frame.pack(side = side, fill = X, expand = expand)
return (frame, label, entry)
def createButton(self, parent, category, text, balloonHelp, command,
side = 'top', expand = 0):
@ -520,14 +954,14 @@ class MopathRecorder(AppShell, PandaObject):
def createCheckbutton(self, parent, category, text,
balloonHelp, command, initialState,
side = 'top'):
side = 'top', fill = X, expand = 0):
bool = BooleanVar()
bool.set(initialState)
widget = Checkbutton(parent, text = text, anchor = W,
variable = bool)
# Do this after the widget so command isn't called on creation
widget['command'] = command
widget.pack(side = side, fill = X)
widget.pack(side = side, fill = fill, expand = expand)
self.bind(widget, balloonHelp)
self.widgetDict[category + '-' + text] = widget
self.variableDict[category + '-' + text] = bool
@ -535,12 +969,12 @@ class MopathRecorder(AppShell, PandaObject):
def createRadiobutton(self, parent, side, category, text,
balloonHelp, variable, value,
command):
command, fill = X, expand = 0):
widget = Radiobutton(parent, text = text, anchor = W,
variable = variable, value = value)
# Do this after the widget so command isn't called on creation
widget['command'] = command
widget.pack(side = side, fill = X)
widget.pack(side = side, fill = fill, expand = expand)
self.bind(widget, balloonHelp)
self.widgetDict[category + '-' + text] = widget
return widget
@ -573,7 +1007,8 @@ class MopathRecorder(AppShell, PandaObject):
def createEntryScale(self, parent, category, text, balloonHelp,
command = None, min = 0.0, max = 1.0,
resolution = None, **kw):
resolution = None,
side = TOP, fill = X, expand = 1, **kw):
kw['text'] = text
kw['min'] = min
kw['max'] = max
@ -581,7 +1016,7 @@ class MopathRecorder(AppShell, PandaObject):
widget = apply(EntryScale.EntryScale, (parent,), kw)
# Do this after the widget so command isn't called on creation
widget['command'] = command
widget.pack(fill = X)
widget.pack(side = side, fill = fill, expand = expand)
self.bind(widget, balloonHelp)
self.widgetDict[category + '-' + text] = widget
return widget
@ -656,7 +1091,7 @@ class MopathRecorder(AppShell, PandaObject):
widget.selectitem(items[0])
# Bind selection command
widget['selectioncommand'] = command
widget.pack(side = 'left', expand = 0)
widget.pack(side = LEFT, expand = 0)
# Bind help
self.bind(widget, balloonHelp)
# Record widget

View File

@ -18,6 +18,7 @@ class EntryScale(Pmw.MegaWidget):
('initialValue', 0.0, Pmw.INITOPT),
('resolution', 0.001, None),
('command', None, None),
('callbackData', [], None),
('min', 0.0, self._updateValidate),
('max', 100.0, self._updateValidate),
('text', 'EntryScale', self._updateLabelText),
@ -99,6 +100,12 @@ class EntryScale(Pmw.MegaWidget):
self.scale.pack(side = 'left', expand = 1, fill = 'x')
# Set scale to the middle of its range
self.scale.set(self['initialValue'])
self.scale.bind('<Button-1>',
lambda event, s = self:
apply(s.onPress, s['callbackData']))
self.scale.bind('<ButtonRelease-1>',
lambda event, s = self:
apply(s.onRelease, s['callbackData']))
self.maxLabel = self.createcomponent('maxLabel', (), None,
Button, self.minMaxFrame,
@ -176,7 +183,9 @@ class EntryScale(Pmw.MegaWidget):
def _entryCommand(self, event = None):
try:
val = string.atof( self.entryValue.get() )
apply(self.onReturn,self['callbackData'])
self.set( val )
apply(self.onReturnRelease,self['callbackData'])
except ValueError:
pass
@ -213,6 +222,21 @@ class EntryScale(Pmw.MegaWidget):
if fCommand & (self['command'] is not None):
self['command']( newVal )
def onReturn(self, *args):
""" User redefinable callback executed on <Return> in entry """
pass
def onReturnRelease(self, *args):
""" User redefinable callback executed on <Return> release in entry """
pass
def onPress(self, *args):
""" User redefinable callback executed on button press """
pass
def onRelease(self, *args):
""" User redefinable callback executed on button release """
pass
class EntryScaleGroup(Pmw.MegaToplevel):
def __init__(self, parent = None, **kw):