panda3d/doc/SceneEditor/seBlendAnimPanel.py
2005-01-19 23:27:15 +00:00

667 lines
28 KiB
Python

#################################################################
# collisionWindow.py
# Written by Yi-Hong Lin, yihhongl@andrew.cmu.edu, 2004
#################################################################
# Import Tkinter, Pmw, and the floater code from this directory tree.
from direct.tkwidgets.AppShell import *
from direct.showbase.TkGlobal import *
from tkSimpleDialog import askfloat
import string
import math
import types
from direct.task import Task
FRAMES = 0
SECONDS = 1
#####################################################################################
# BlendAnimPanel(AppShell)
# This Panel will allow user to blend tow animations
# that have already been loaded for this actor.
# user can play and manipulate this blended animation
# just like in the animation panel. And, they can save this blended animation.
#####################################################################################
class BlendAnimPanel(AppShell):
# Override class variables
appname = 'Blend Anim Panel'
frameWidth = 575
frameHeight = 450
usecommandarea = 0
usestatusarea = 0
index = 0
dragMode = False
blendRatio = 0
rateList= ['1/24.0', '0.1', '0.5', '1.0', '2.0', '5.0' , '10.0']
enableBlend = False
currentBlendName = None
def __init__(self, aNode = None, blendDict={}, parent = None, **kw):
INITOPT = Pmw.INITOPT
self.id = 'BlendAnimPanel '+ aNode.getName()
self.appname = self.id
self.actorNode = aNode
self.blendDict = blendDict.copy()
if len(blendDict)>0:
self.blendList = blendDict.keys()
else:
self.blendList = []
optiondefs = (
('title', self.appname, None),
('actor', aNode, None),
('animList', [], None),
('blendAnimList', self.blendList, None),
)
self.defineoptions(kw, optiondefs)
self.id = 'Blend AnimPanel '+ aNode.getName()
self.nodeName = aNode.getName()
# Initialize the superclass
AppShell.__init__(self)
# Execute option callbacks
self.initialiseoptions(BlendAnimPanel)
self.currTime = 0.0
self.animNameA = None
self.animNameB = None
self.parent.resizable(False,False) ## Disable the ability to resize for this Window.
def createInterface(self):
# Handle to the toplevels interior
interior = self.interior()
self.menuBar.destroy()
# show the actor's name
actorFrame = Frame(interior)
name_label = Label(actorFrame, text= self.nodeName,font=('MSSansSerif', 14),
relief = SUNKEN, borderwidth=3)
name_label.pack(side = TOP, expand = False)
actorFrame.pack(side = TOP, expand = False, fill = X)
# Create a frame to show is there any ore-blended animation and save, edit, rename button.
group = Pmw.Group(interior, tag_pyclass=None)
actorFrame = group.interior()
group.pack(side = TOP, expand = False, fill = X)
Label(actorFrame, text= "Blended:", font=('MSSansSerif', 10)).pack(side=LEFT)
self.blendAnimEntry = self.createcomponent(
'Blended Animation', (), None,
Pmw.ComboBox, (actorFrame,),
labelpos = W, entry_width = 20, selectioncommand = self.setBlendAnim,
scrolledlist_items = self['blendAnimList'])
self.blendAnimEntry.pack(side=LEFT)
Label(actorFrame, text= " ", font=('MSSansSerif', 10)).pack(side=LEFT)
button = Button(actorFrame, text="Save", font=('MSSansSerif', 10),width = 12,
command = self.saveButtonPushed).pack(side=LEFT)
button = Button(actorFrame, text="Remove", font=('MSSansSerif', 10),width = 12,
command = self.removeButtonPushed).pack(side=LEFT)
button = Button(actorFrame, text="Rename", font=('MSSansSerif', 10),width = 12,
command = self.renameButtonPushed).pack(side=LEFT)
actorFrame.pack(side = TOP, expand = False, fill = X)
# Create a frame to hold all the animation setting
group = Pmw.Group(interior, tag_pyclass=None)
actorFrame = group.interior()
group.pack(side = TOP, expand = False, fill = X)
Label(actorFrame, text= "Animation A:", font=('MSSansSerif', 10)).pack(side=LEFT)
self['animList'] = self['actor'].getAnimNames()
self.AnimEntryA = self.createcomponent(
'AnimationMenuA', (), None,
Pmw.ComboBox, (actorFrame,),
labelpos = W, entry_width = 20, entry_state = DISABLED,
selectioncommand = lambda name, a = 'a' : self.setAnimation(name, AB=a),
scrolledlist_items = self['animList'])
self.AnimEntryA.pack(side=LEFT)
Label(actorFrame, text= " ", font=('MSSansSerif', 10)).pack(side=LEFT,)
Label(actorFrame, text= "Animation B:", font=('MSSansSerif', 10)).pack(side=LEFT)
self['animList'] = self['actor'].getAnimNames()
self.AnimEntryB = self.createcomponent(
'AnimationMenuB', (), None,
Pmw.ComboBox, (actorFrame,),
labelpos = W, entry_width = 20, entry_state = DISABLED,
selectioncommand = lambda name, a = 'b' : self.setAnimation(name, AB=a),
scrolledlist_items = self['animList'])
self.AnimEntryB.pack(side=LEFT)
actorFrame.pack(side = TOP, expand = False, fill = X)
### Blend Enable checkbox
actorFrame = Frame(interior, relief = SUNKEN, bd = 1)
Label(actorFrame, text= "Enable Blending:", font=('MSSansSerif', 10)).pack(side=LEFT,)
self.blendVar = IntVar()
self.blendVar.set(0)
self.blendButton = self.createcomponent(
'blendButton', (), None,
Checkbutton, (actorFrame,),
variable = self.blendVar,
command = self.toggleBlend)
self.blendButton.pack(side=LEFT)
actorFrame.pack(side = TOP, expand = False, fill = X)
## Ratio control
actorFrame = Frame(interior)
frameFrame = Frame(actorFrame, relief = SUNKEN, bd = 1)
minRatioLabel = self.createcomponent(
'minRatioLabel', (), 'sLabel',
Label, (frameFrame,),
text = 0.00)
minRatioLabel.pack(side = LEFT)
self.ratioControl = self.createcomponent(
'ratio', (), None,
Scale, (frameFrame,),
from_ = 0.0, to = 1.0, resolution = 0.01,
command = self.setRatio, length = 500,
orient = HORIZONTAL, showvalue = 1)
self.ratioControl.pack(side = LEFT, expand = 1)
self.ratioControl.set(1.0)
self.maxRatioLabel = self.createcomponent(
'maxRatioLabel', (), 'sLabel',
Label, (frameFrame,),
text = 1.00)
self.maxRatioLabel.pack(side = LEFT)
frameFrame.pack(side = LEFT, expand = 1, fill = X)
actorFrame.pack(side = TOP, expand = True, fill = X)
###################################################################################
###################################################################################
actorFrame = Frame(interior)
Label(actorFrame, text= "Play Rate:", font=('MSSansSerif', 10)).pack(side=LEFT)
self.playRateEntry = self.createcomponent(
'playRateMenu', (), None,
Pmw.ComboBox, (actorFrame,),
labelpos = W, entry_width = 20, selectioncommand = self.setPlayRate,
scrolledlist_items = self.rateList)
self.playRateEntry.pack(side=LEFT)
self.playRateEntry.selectitem('1.0')
### Loop checkbox
Label(actorFrame, text= " ", font=('MSSansSerif', 10)).pack(side=LEFT,)
Label(actorFrame, text= "Loop:", font=('MSSansSerif', 10)).pack(side=LEFT,)
self.loopVar = IntVar()
self.loopVar.set(0)
self.loopButton = self.createcomponent(
'loopButton', (), None,
Checkbutton, (actorFrame,),
variable = self.loopVar)
self.loopButton.pack(side=LEFT)
actorFrame.pack(side = TOP, expand = True, fill = X)
### Display Frames/Seconds
actorFrame = Frame(interior)
Label(actorFrame, text= "Frame/Second:", font=('MSSansSerif', 10)).pack(side=LEFT)
self.unitsVar = IntVar()
self.unitsVar.set(FRAMES)
self.displayButton = self.createcomponent(
'displayButton', (), None,
Checkbutton, (actorFrame,),
command = self.updateDisplay,
variable = self.unitsVar)
self.displayButton.pack(side=LEFT)
actorFrame.pack(side = TOP, expand = True, fill = X)
## scale control
actorFrame = Frame(interior)
frameFrame = Frame(actorFrame, relief = SUNKEN, bd = 1)
self.minLabel = self.createcomponent(
'minLabel', (), 'sLabel',
Label, (frameFrame,),
text = 0)
self.minLabel.pack(side = LEFT)
self.frameControl = self.createcomponent(
'scale', (), None,
Scale, (frameFrame,),
from_ = 0, to = 24, resolution = 1.0,
command = self.goTo, length = 500,
orient = HORIZONTAL, showvalue = 1)
self.frameControl.pack(side = LEFT, expand = 1)
self.frameControl.bind('<Button-1>', self.onPress)
self.frameControl.bind('<ButtonRelease-1>', self.onRelease)
self.maxLabel = self.createcomponent(
'maxLabel', (), 'sLabel',
Label, (frameFrame,),
text = 24)
self.maxLabel.pack(side = LEFT)
frameFrame.pack(side = LEFT, expand = 1, fill = X)
actorFrame.pack(side = TOP, expand = True, fill = X)
## button contorl
actorFrame = Frame(interior)
ButtomFrame = Frame(actorFrame, relief = SUNKEN, bd = 1,borderwidth=5)
self.toStartButton = self.createcomponent(
'toStart', (), None,
Button, (ButtomFrame,),
text = '<<',
width = 8,
command = self.resetAllToZero)
self.toStartButton.pack(side = LEFT, expand = 1, fill = X)
self.playButton = self.createcomponent(
'playButton', (), None,
Button, (ButtomFrame,),
text = 'Play', width = 8,
command = self.play)
self.playButton.pack(side = LEFT, expand = 1, fill = X)
self.stopButton = self.createcomponent(
'stopButton', (), None,
Button, (ButtomFrame,),
text = 'Stop', width = 8, state=DISABLED,
command = self.stop)
self.stopButton.pack(side = LEFT, expand = 1, fill = X)
self.toEndButton = self.createcomponent(
'toEnd', (), None,
Button, (ButtomFrame,),
text = '>>',
width = 8,
command = self.resetAllToEnd)
self.toEndButton.pack(side = LEFT, expand = 1, fill = X)
ButtomFrame.pack(side = TOP, expand = True, fill = X)
actorFrame.pack(expand = 1, fill = BOTH)
def updateList(self):
#################################################################
# updateList(self)
# This will reset the list of all animations that this actor has
# to the animation entry A and B.
#################################################################
self['animList'] = self['actor'].getAnimNames()
animL = self['actor'].getAnimNames()
self.AnimEntryA.setlist(animL)
self.AnimEntryB.setlist(animL)
def play(self):
#################################################################
# play(self)
# It works pretty much like what we have in the Animation Panel.
# The only different now is that we set two "pose" here.
# When you do the blending animation by setPose, you don't have
# to set them simultaneously.
#################################################################
self.animNameA = self.AnimEntryA.get()
self.animNameB = self.AnimEntryB.get()
if (self.animNameA in self['animList'])and(self.animNameB in self['animList']):
self.playButton.config(state=DISABLED)
self.lastT = globalClock.getFrameTime()
taskMgr.add(self.playTask, self.id + '_UpdateTask')
self.stopButton.config(state=NORMAL)
else:
print '----Illegal Animaion name!!', self.animNameA + ', '+ self.animNameB
return
def playTask(self, task):
#################################################################
# playTask(self, task)
# see play(self)
#################################################################
fLoop = self.loopVar.get()
currT = globalClock.getFrameTime()
deltaT = currT - self.lastT
self.lastT = currT
if self.dragMode:
return Task.cont
self.currTime = self.currTime + deltaT
if (self.currTime > self.maxSeconds):
if fLoop:
self.currTime = self.currTime%self.duration
self.gotoT(self.currTime)
else:
self.currTime = 0.0
self.gotoT(0.0)
self.playButton.config(state=NORMAL)
self.stopButton.config(state=DISABLED)
return Task.done
else:
self.gotoT(self.currTime)
return Task.cont
def stop(self):
#################################################################
# stop(self)
# see play(self)
#################################################################
taskMgr.remove(self.id + '_UpdateTask')
self.playButton.config(state=NORMAL)
self.stopButton.config(state=DISABLED)
return
def setAnimation(self, animation, AB = 'a'):
#################################################################
# setAnimation(self, animation, AB = 'a')
# see play(self)
#################################################################
print 'OK!!!'
if AB == 'a':
if self.animNameA != None:
self['actor'].setControlEffect(self.animNameA, 1.0, 'modelRoot','lodRoot')
self.animNameA = self.AnimEntryA.get()
else:
if self.animNameB != None:
self['actor'].setControlEffect(self.animNameB, 1.0, 'modelRoot','lodRoot')
self.animNameB = self.AnimEntryB.get()
self.currTime = 0.0
self.frameControl.set(0)
self.updateDisplay()
self.setRatio(self.blendRatio)
return
def setPlayRate(self,rate):
#################################################################
# setPlayRate(self,rate)
# see play(self)
#################################################################
self.animNameA = self.AnimEntryA.get()
if self.animNameA in self['animList']:
self['actor'].setPlayRate(eval(rate), self.animNameA)
self.updateDisplay()
if self.animNameB in self['animList']:
self['actor'].setPlayRate(eval(rate), self.animNameB)
self.updateDisplay()
return
def updateDisplay(self):
#################################################################
# updateDisplay(self)
# see play(self)
#################################################################
if not (self.animNameA in self['animList']):
return
self.fps = self['actor'].getFrameRate(self.animNameA)
self.duration = self['actor'].getDuration(self.animNameA)
self.maxFrame = self['actor'].getNumFrames(self.animNameA) - 1
if not (self.animNameB in self['animList']):
return
if self.duration > self['actor'].getDuration(self.animNameB):
self.duration = self['actor'].getDuration(self.animNameB)
if self.maxFrame > self['actor'].getNumFrames(self.animNameB) - 1:
self.maxFrame = self['actor'].getNumFrames(self.animNameB) - 1
self.maxSeconds = self.duration
if self.unitsVar.get() == FRAMES:
fromFrame = 0
toFrame = self.maxFrame
self.minLabel['text'] = fromFrame
self.maxLabel['text'] = toFrame
self.frameControl.configure(from_ = fromFrame,
to = toFrame,
resolution = 1.0)
else:
self.minLabel['text'] = '0.0'
self.maxLabel['text'] = "%.2f" % self.duration
self.frameControl.configure(from_ = 0.0,
to = self.duration,
resolution = 0.01)
def gotoT(self,time):
#################################################################
# gotoT(self,time)
# see play(self)
#################################################################
if self.unitsVar.get() == FRAMES:
self.frameControl.set(time * self.fps)
else:
self.frameControl.set(time)
return
def goTo(self,frame):
#################################################################
# goTo(self,frame)
# see play(self)
#################################################################
if (self.animNameA in self['animList'])and(self.animNameB in self['animList']):
# Convert scale value to float
frame = string.atof(frame)
# Now convert t to seconds for offset calculations
if self.unitsVar.get() == FRAMES:
frame = frame / self.fps
if self.dragMode:
self.currTime = frame
self['actor'].pose(self.animNameA,
min(self.maxFrame, int(frame * self.fps)))
self['actor'].pose(self.animNameB,
min(self.maxFrame, int(frame * self.fps)))
return
def onRelease(self,frame):
#################################################################
# onRelease(self,frame)
# see play(self)
#################################################################
self.dragMode = False
return
def onPress(self,frame):
#################################################################
# onPress(self,frame)
# see play(self)
#################################################################
self.dragMode = True
return
def resetAllToZero(self):
#################################################################
# resetAllToZero(self)
# see play(self)
#################################################################
self.currTime = 0.0
self.gotoT(0)
return
def resetAllToEnd(self):
#################################################################
# resetAllToEnd(self)
# see play(self)
#################################################################
self.currTime = self.maxSeconds
self.gotoT(self.duration)
return
def toggleBlend(self):
#################################################################
# toggleBlend(self)
# This function will enable the blending option for the actor.
# and call set ratio function to set the blending animation mixing in
# current ratio.
#
# This blending enable will not be keep when you close the window!
#
#################################################################
if self.blendVar.get():
self.enableBlend = True
self['actor'].enableBlend()
self.setRatio(self.blendRatio)
else:
self.enableBlend = False
self['actor'].disableBlend()
return
def setRatio(self, ratio):
#################################################################
# setRatio(self, ratio)
# callback funtion
# This one will be called each time when user drag the blend ratio
# slider on the panel. This will set the blening ratio to both animation.
# (Which is "setControlEffect")
#################################################################
self.blendRatio = float(ratio)
if self.enableBlend:
if self.animNameA in self['animList']:
self['actor'].setControlEffect(self.animNameA, self.blendRatio, 'modelRoot','lodRoot')
if self.animNameB in self['animList']:
self['actor'].setControlEffect(self.animNameB, 1-self.blendRatio, 'modelRoot','lodRoot')
return
def setBlendAnim(self, name):
#################################################################
# setBlendAnim(self, name)
# This function will be called each time when user try to select
# a existing blending animation from the comboBox on the panel
# This function will re-set every varaibles on the panel to what
# it should be. For example, when user choose blending anim "R,"
# which was blended by anim "a" and "b" with ratio "c,"
# then this function will set Animation A to "a" and animation B
# to "b" and set the ratio slider to "c" position.
#################################################################
if self.blendDict.has_key(name):
self.currentBlendName = name
animA = self.blendDict[name][0]
animB = self.blendDict[name][1]
ratio = self.blendDict[name][2]
self.AnimEntryA.selectitem(animA)
self.AnimEntryB.selectitem(animB)
self.setAnimation(animA, AB = 'a')
self.setAnimation(animB, AB = 'b')
self.ratioControl.set(ratio)
return
def setBlendAnimList(self, dict, select=False):
#################################################################
# setBlendAnimList(self, dict, select=False)
# This function will be called when we need to reset the dropdown list
# of "Blend Anim."
# About "selec" option, this now is mainly used when we remove
# a blended animation from the actor. When it has been specified to True,
# the function will not only reset the list, but will also automatically
# select one from the top of list, if it is not empty.
#################################################################
self.blendDict.clear()
del self.blendDict
self.blendDict = dict.copy()
print self.blendDict
if len(self.blendDict)>0:
self.blendList = self.blendDict.keys()
else:
self.blendList = []
self.blendAnimEntry.setlist(self.blendList)
if select:
if len(self.blendList)>0:
self.blendAnimEntry.selectitem(self.blendList[0])
self.setBlendAnim(self.blendList[0])
self.currentBlendName = self.blendList[0]
else:
self.blendAnimEntry.clear()
self.currentBlendName = None
return
def saveButtonPushed(self):
#################################################################
# saveButtonPushed(self)
# This function will be called when user clicked on the "Save" button
# This functiont will collect all data on the panel and send them with
# a message to sceneEditor to save the current blending animation
# into the dataHolder.
#################################################################
name = self.blendAnimEntry.get()
if name=='':
Pmw.MessageDialog(None, title='Caution!',
message_text = 'You have to give the blending animation a name first!',
iconpos='s',
defaultbutton = 'Close'
)
return
elif (not(self.animNameA in self['animList']))or(not(self.animNameB in self['animList'])):
Pmw.MessageDialog(None, title='Caution!',
message_text = 'The Animations you have selected are not exist!',
iconpos='s',
defaultbutton = 'Close'
)
return
else:
messenger.send('BAW_saveBlendAnim', [self['actor'].getName(),
name,
self.animNameA,
self.animNameB,
self.blendRatio])
self.currentBlendName = name
return
def removeButtonPushed(self):
#################################################################
# removeButtonPushed(self)
# remove the current seleted blended animation from the actor.
# This will send out a message to sceneEditor to delete the data inside
# the dataHolder and then reset the list of here.
#################################################################
name = self.blendAnimEntry.get()
messenger.send('BAW_removeBlendAnim', [self['actor'].getName(),name])
return
def renameButtonPushed(self):
#################################################################
# renameButtonPushed(self)
# this function will be called when user click on the "Rename" button.
# This function will collect all data on the panel and send them out
# with a message to sceneEditor to rename and re-save all setting about
# current animation.
#################################################################
oName = self.currentBlendName
name = self.blendAnimEntry.get()
if self.currentBlendName == None:
Pmw.MessageDialog(None, title='Caution!',
message_text = "You haven't select any blended animation!!",
iconpos='s',
defaultbutton = 'Close'
)
return
elif name=='':
Pmw.MessageDialog(None, title='Caution!',
message_text = 'You have to give the blending animation a name first!',
iconpos='s',
defaultbutton = 'Close'
)
return
elif (not(self.animNameA in self['animList']))or(not(self.animNameB in self['animList'])):
Pmw.MessageDialog(None, title='Caution!',
message_text = 'The Animations you have selected are not exist!',
iconpos='s',
defaultbutton = 'Close'
)
return
else:
messenger.send('BAW_renameBlendAnim', [self['actor'].getName(),
name,
oName,
self.animNameA,
self.animNameB,
self.blendRatio]
)
self.currentBlendName = name
return
def onDestroy(self, event):
#################################################################
# onDestroy(self, event)
# This function will be call when user try to close the window.
# In here we will stop all tasks we have opend and disable the
# blend setting of actor.
# If we didn't disable the blend option, the next time you play
# the animation via animation panel will cause some error.
#################################################################
if taskMgr.hasTaskNamed(self.id + '_UpdateTask'):
taskMgr.remove(self.id + '_UpdateTask')
messenger.send('BAW_close',[self.nodeName])
self.actorNode.setControlEffect(self.animNameA, 1.0, 'modelRoot','lodRoot')
self.actorNode.setControlEffect(self.animNameB, 1.0, 'modelRoot','lodRoot')
self.actorNode.disableBlend()
'''
If you have open any thing, please rewrite here!
'''
pass