| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739 |
- """DIRECT Animation Control Panel"""
- __all__ = ['AnimPanel', 'ActorControl']
- ### SEE END OF FILE FOR EXAMPLE USEAGE ###
- # Import Tkinter, Pmw, and the floater code from this directory tree.
- from direct.tkwidgets.AppShell import *
- from direct.showbase.TkGlobal import *
- import Pmw, os
- from direct.task import Task
- from panda3d.core import Filename, getModelPath
- from tkinter.simpledialog import askfloat
- from tkinter.filedialog import askopenfilename
- FRAMES = 0
- SECONDS = 1
- class AnimPanel(AppShell):
- # Override class variables
- appname = 'Anim Panel'
- frameWidth = 675
- frameHeight = 250
- usecommandarea = 0
- usestatusarea = 0
- index = 0
- def __init__(self, aList = [], parent = None, session = None, **kw):
- INITOPT = Pmw.INITOPT
- if isinstance(aList, (list, tuple)):
- kw['actorList'] = aList
- else:
- kw['actorList'] = [aList]
- optiondefs = (
- ('title', self.appname, None),
- ('actorList', [], None),
- ('Actor_label_width', 12, None),
- )
- self.defineoptions(kw, optiondefs)
- # direct session that spawned me, if any, used
- # for certain interactions with the session such
- # as being able to see selected objects/actors
- self.session = session
- self.frameHeight = 60 + (50 * len(self['actorList']))
- self.playList = []
- self.id = 'AnimPanel_%d' % AnimPanel.index
- AnimPanel.index += 1
- # current index used for creating new actor controls
- self.actorControlIndex = 0
- # Initialize the superclass
- AppShell.__init__(self)
- # Execute option callbacks
- self.initialiseoptions(AnimPanel)
- # We need to know when AnimPanel is closed
- self.destroyCallBack = None
- def createInterface(self):
- # Handle to the toplevels interior
- interior = self.interior()
- menuBar = self.menuBar
- menuBar.addmenu('AnimPanel', 'Anim Panel Operations')
- # Actor control status
- menuBar.addcascademenu('AnimPanel', 'Control Status',
- 'Enable/disable actor control panels')
- menuBar.addmenuitem('Control Status', 'command',
- 'Enable all actor controls',
- label = 'Enable all',
- command = self.enableActorControls)
- menuBar.addmenuitem('Control Status', 'command',
- 'Disable all actor controls',
- label = 'Disable all',
- command = self.disableActorControls)
- # Frame Slider units
- menuBar.addcascademenu('AnimPanel', 'Display Units',
- 'Select display units')
- menuBar.addmenuitem('Display Units', 'command',
- 'Display frame counts', label = 'Frame count',
- command = self.displayFrameCounts)
- menuBar.addmenuitem('Display Units', 'command',
- 'Display seconds', label = 'Seconds',
- command = self.displaySeconds)
- # Reset all actor controls
- menuBar.addmenuitem('AnimPanel', 'command',
- 'Set actor controls to t = 0.0',
- label = 'Jump all to zero',
- command = self.resetAllToZero)
- menuBar.addmenuitem('AnimPanel', 'command',
- 'Set Actor controls to end time',
- label = 'Jump all to end time',
- command = self.resetAllToEnd)
- # Add some buttons to update all Actor Controls
- self.fToggleAll = 1
- b = self.createcomponent(
- 'toggleEnableButton', (), None,
- Button, (self.menuFrame,),
- text = 'Toggle Enable',
- command = self.toggleAllControls)
- b.pack(side = RIGHT, expand = 0)
- b = self.createcomponent(
- 'showSecondsButton', (), None,
- Button, (self.menuFrame,),
- text = 'Show Seconds',
- command = self.displaySeconds)
- b.pack(side = RIGHT, expand = 0)
- b = self.createcomponent(
- 'showFramesButton', (), None,
- Button, (self.menuFrame,),
- text = 'Show Frames',
- command = self.displayFrameCounts)
- b.pack(side = RIGHT, expand = 0)
- self.actorFrame = None
- self.createActorControls()
- # Create a frame to hold the playback controls
- controlFrame = Frame(interior)
- self.toStartButton = self.createcomponent(
- 'toStart', (), None,
- Button, (controlFrame,),
- text = '<<',
- width = 4,
- command = self.resetAllToZero)
- self.toStartButton.pack(side = LEFT, expand = 1, fill = X)
- self.toPreviousFrameButton = self.createcomponent(
- 'toPreviousFrame', (), None,
- Button, (controlFrame,),
- text = '<',
- width = 4,
- command = self.previousFrame)
- self.toPreviousFrameButton.pack(side = LEFT, expand = 1, fill = X)
- self.playButton = self.createcomponent(
- 'playButton', (), None,
- Button, (controlFrame,),
- text = 'Play', width = 8,
- command = self.playActorControls)
- self.playButton.pack(side = LEFT, expand = 1, fill = X)
- self.stopButton = self.createcomponent(
- 'stopButton', (), None,
- Button, (controlFrame,),
- text = 'Stop', width = 8,
- command = self.stopActorControls)
- self.stopButton.pack(side = LEFT, expand = 1, fill = X)
- self.toNextFrameButton = self.createcomponent(
- 'toNextFrame', (), None,
- Button, (controlFrame,),
- text = '>',
- width = 4,
- command = self.nextFrame)
- self.toNextFrameButton.pack(side = LEFT, expand = 1, fill = X)
- self.toEndButton = self.createcomponent(
- 'toEnd', (), None,
- Button, (controlFrame,),
- text = '>>',
- width = 4,
- command = self.resetAllToEnd)
- self.toEndButton.pack(side = LEFT, expand = 1, fill = X)
- self.loopVar = IntVar()
- self.loopVar.set(0)
- self.loopButton = self.createcomponent(
- 'loopButton', (), None,
- Checkbutton, (controlFrame,),
- text = 'Loop', width = 8,
- variable = self.loopVar)
- self.loopButton.pack(side = LEFT, expand = 1, fill = X)
- # add actors and animations, only allowed if a direct
- # session has been specified since these currently require
- # interaction with selected objects
- if (self.session):
- menuBar.addmenuitem('File', 'command',
- 'Set currently selected group of objects as actors to animate.',
- label = 'Set Actors',
- command = self.setActors)
- menuBar.addmenuitem('File', 'command',
- 'Load animation file',
- label = 'Load Anim',
- command = self.loadAnim)
- controlFrame.pack(fill = X)
- def createActorControls(self):
- # Create a frame to hold all the actor controls
- self.actorFrame = Frame(self.interior())
- # Create a control for each actor
- self.actorControlList = []
- for actor in self['actorList']:
- anims = actor.getAnimNames()
- print("actor animnames: %s"%anims)
- topAnims = []
- if 'neutral' in anims:
- i = anims.index('neutral')
- del(anims[i])
- topAnims.append('neutral')
- if 'walk' in anims:
- i = anims.index('walk')
- del(anims[i])
- topAnims.append('walk')
- if 'run' in anims:
- i = anims.index('run')
- del(anims[i])
- topAnims.append('run')
- anims.sort()
- anims = topAnims + anims
- if (len(anims)== 0):
- # no animations set for this actor, don't
- # display the control panel
- continue
- # currComponents = self.components()
- # if ('actorControl%d' % index in currComponents):
- # self.destroycomponent('actorControl%d' % index)
- # ac = self.component('actorControl%d' % index)
- # if (ac == None):
- ac = self.createcomponent(
- 'actorControl%d' % self.actorControlIndex, (), 'Actor',
- ActorControl, (self.actorFrame,),
- animPanel = self,
- text = actor.getName(),
- animList = anims,
- actor = actor)
- ac.pack(expand = 1, fill = X)
- self.actorControlList.append(ac)
- self.actorControlIndex = self.actorControlIndex + 1
- # Now pack the actor frame
- self.actorFrame.pack(expand = 1, fill = BOTH)
- def clearActorControls(self):
- if (self.actorFrame):
- self.actorFrame.forget()
- self.actorFrame.destroy()
- self.actorFrame = None
- def setActors(self):
- self.stopActorControls()
- actors = self.session.getSelectedActors()
- # make sure selected objects are actors, if not don't
- # use?
- aList = []
- for currActor in actors:
- aList.append(currActor)
- self['actorList'] = aList
- self.clearActorControls()
- self.createActorControls()
- def loadAnim(self):
- # bring up file open box to allow selection of an
- # animation file
- animFilename = askopenfilename(
- defaultextension = '.mb',
- filetypes = (('Maya Models', '*.mb'),
- ('All files', '*')),
- initialdir = '/i/beta',
- title = 'Load Animation',
- parent = self.component('hull')
- )
- if not animFilename or animFilename == 'None':
- # no file selected, canceled
- return
- # add directory where animation was loaded from to the
- # current model path so any further searches for the file
- # can find it
- fileDirName = os.path.dirname(animFilename)
- fileBaseName = os.path.basename(animFilename)
- fileBaseNameBase = os.path.splitext(fileBaseName)[0]
- fileDirNameFN = Filename(fileDirName)
- fileDirNameFN.makeCanonical()
- getModelPath().prependDirectory(fileDirNameFN)
- for currActor in self['actorList']:
- # replace all currently loaded anims with specified one
- # currActor.unloadAnims(None, None, None)
- currActor.loadAnims({fileBaseNameBase:fileBaseNameBase})
- self.clearActorControls()
- self.createActorControls()
- def playActorControls(self):
- self.stopActorControls()
- self.lastT = globalClock.getFrameTime()
- self.playList = self.actorControlList[:]
- taskMgr.add(self.play, self.id + '_UpdateTask')
- def play(self, task):
- if not self.playList:
- return Task.done
- fLoop = self.loopVar.get()
- currT = globalClock.getFrameTime()
- deltaT = currT - self.lastT
- self.lastT = currT
- for actorControl in self.playList:
- # scale time by play rate value
- actorControl.play(deltaT * actorControl.playRate, fLoop)
- return Task.cont
- def stopActorControls(self):
- taskMgr.remove(self.id + '_UpdateTask')
- def getActorControlAt(self, index):
- return self.actorControlList[index]
- def enableActorControlAt(self, index):
- self.getActorControlAt(index).enableControl()
- def toggleAllControls(self):
- if self.fToggleAll:
- self.disableActorControls()
- else:
- self.enableActorControls()
- self.fToggleAll = 1 - self.fToggleAll
- def enableActorControls(self):
- for actorControl in self.actorControlList:
- actorControl.enableControl()
- def disableActorControls(self):
- for actorControl in self.actorControlList:
- actorControl.disableControl()
- def disableActorControlAt(self, index):
- self.getActorControlAt(index).disableControl()
- def displayFrameCounts(self):
- for actorControl in self.actorControlList:
- actorControl.displayFrameCounts()
- def displaySeconds(self):
- for actorControl in self.actorControlList:
- actorControl.displaySeconds()
- def resetAllToZero(self):
- for actorControl in self.actorControlList:
- actorControl.resetToZero()
- def resetAllToEnd(self):
- for actorControl in self.actorControlList:
- actorControl.resetToEnd()
- def nextFrame(self):
- for actorControl in self.actorControlList:
- actorControl.nextFrame()
- def previousFrame(self):
- for actorControl in self.actorControlList:
- actorControl.previousFrame()
- def setDestroyCallBack(self, callBack):
- self.destroyCallBack = callBack
- def destroy(self):
- # First clean up
- taskMgr.remove(self.id + '_UpdateTask')
- if self.destroyCallBack is not None:
- self.destroyCallBack()
- self.destroyCallBack = None
- AppShell.destroy(self)
- class ActorControl(Pmw.MegaWidget):
- def __init__(self, parent = None, **kw):
- INITOPT = Pmw.INITOPT
- DEFAULT_FONT = (('MS', 'Sans', 'Serif'), 12, 'bold')
- DEFAULT_ANIMS = ('neutral', 'run', 'walk')
- animList = kw.get('animList', DEFAULT_ANIMS)
- if len(animList) > 0:
- initActive = animList[0]
- else:
- initActive = DEFAULT_ANIMS[0]
- optiondefs = (
- ('text', 'Actor', self._updateLabelText),
- ('animPanel', None, None),
- ('actor', None, None),
- ('animList', DEFAULT_ANIMS, None),
- ('active', initActive, None),
- ('sLabel_width', 5, None),
- ('sLabel_font', DEFAULT_FONT, None),
- )
- self.defineoptions(kw, optiondefs)
- # Initialize the superclass
- Pmw.MegaWidget.__init__(self, parent)
- # Handle to the toplevels hull
- interior = self.interior()
- interior.configure(relief = RAISED, bd = 2)
- # Instance variables
- self.fps = 24
- self.offset = 0.0
- self.maxSeconds = 1.0
- self.currT = 0.0
- self.fScaleCommand = 0
- self.fOneShot = 0
- # Create component widgets
- self._label = self.createcomponent(
- 'label', (), None,
- Menubutton, (interior,),
- font=('MSSansSerif', 14, 'bold'),
- relief = RAISED, bd = 1,
- activebackground = '#909090',
- text = self['text'])
- # Top level menu
- labelMenu = Menu(self._label, tearoff = 0)
- # Menu to select display mode
- self.unitsVar = IntVar()
- self.unitsVar.set(FRAMES)
- displayMenu = Menu(labelMenu, tearoff = 0)
- displayMenu.add_radiobutton(label = 'Frame count',
- value = FRAMES,
- variable = self.unitsVar,
- command = self.updateDisplay)
- displayMenu.add_radiobutton(label = 'Seconds',
- value = SECONDS,
- variable = self.unitsVar,
- command = self.updateDisplay)
- # Items for top level menu
- labelMenu.add_cascade(label = 'Display Units', menu = displayMenu)
- # labelMenu.add_command(label = 'Set Offset', command = self.setOffset)
- labelMenu.add_command(label = 'Jump To Zero',
- command = self.resetToZero)
- labelMenu.add_command(label = 'Jump To End Time',
- command = self.resetToEnd)
- # Now associate menu with menubutton
- self._label['menu'] = labelMenu
- self._label.pack(side = LEFT, fill = X)
- # Combo box to select current animation
- self.animMenu = self.createcomponent(
- 'animMenu', (), None,
- Pmw.ComboBox, (interior,),
- labelpos = W, label_text = 'Anim:',
- entry_width = 12, selectioncommand = self.selectAnimNamed,
- scrolledlist_items = self['animList'])
- self.animMenu.selectitem(self['active'])
- self.animMenu.pack(side = 'left', padx = 5, expand = 0)
- # Combo box to select frame rate
- playRateList = ['1/24.0', '0.1', '0.5', '1.0', '2.0', '5.0', '10.0']
- playRate = '%0.1f' % self['actor'].getPlayRate(self['active'])
- if playRate not in playRateList:
- def strCmp(a, b):
- return cmp(eval(a), eval(b))
- playRateList.append(playRate)
- playRateList.sort(strCmp)
- playRateMenu = self.createcomponent(
- 'playRateMenu', (), None,
- Pmw.ComboBox, (interior,),
- labelpos = W, label_text = 'Play Rate:',
- entry_width = 4, selectioncommand = self.setPlayRate,
- scrolledlist_items = playRateList)
- playRateMenu.selectitem(playRate)
- playRateMenu.pack(side = LEFT, padx = 5, expand = 0)
- # Scale to control animation
- frameFrame = Frame(interior, 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,
- 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)
- # Checkbutton to enable/disable control
- self.frameActiveVar = IntVar()
- self.frameActiveVar.set(1)
- frameActive = self.createcomponent(
- 'checkbutton', (), None,
- Checkbutton, (interior,),
- variable = self.frameActiveVar)
- frameActive.pack(side = LEFT, expand = 1)
- # Execute option callbacks
- self.initialiseoptions(ActorControl)
- self.playRate = 1.0
- self.updateDisplay()
- def _updateLabelText(self):
- self._label['text'] = self['text']
- def updateDisplay(self):
- actor = self['actor']
- active = self['active']
- self.fps = actor.getFrameRate(active)
- if (self.fps == None):
- # there was probably a problem loading the
- # active animation, set default anim properties
- print("unable to get animation fps, zeroing out animation info")
- self.fps = 24
- self.duration = 0
- self.maxFrame = 0
- self.maxSeconds = 0
- else:
- self.duration = actor.getDuration(active)
- self.maxFrame = actor.getNumFrames(active) - 1
- self.maxSeconds = self.offset + self.duration
- # switch between showing frame counts and seconds
- if self.unitsVar.get() == FRAMES:
- # these are approximate due to discrete frame size
- 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 __onPress(self, event):
- # Enable slider command
- self.fScaleCommand = 1
- def __onRelease(self, event):
- # Disable slider command
- self.fScaleCommand = 0
- def selectAnimNamed(self, name):
- # Update active anim
- self['active'] = name
- # Reset play rate
- self.component('playRateMenu').selectitem('1.0')
- self.setPlayRate('1.0')
- # Move slider to zero
- self.resetToZero()
- def setPlayRate(self, rate):
- # set play rate on the actor, although for the AnimPanel
- # purpose we don't use the actor's play rate, but rather
- # the self.playRate value since we drive the animation
- # playback ourselves
- self['actor'].setPlayRate(eval(rate), self['active'])
- self.playRate = eval(rate)
- self.updateDisplay()
- def setOffset(self):
- newOffset = askfloat(parent = self.interior(),
- title = self['text'],
- prompt = 'Start offset (seconds):')
- if newOffset != None:
- self.offset = newOffset
- self.updateDisplay()
- def enableControl(self):
- self.frameActiveVar.set(1)
- def disableControl(self):
- self.frameActiveVar.set(0)
- def displayFrameCounts(self):
- self.unitsVar.set(FRAMES)
- self.updateDisplay()
- def displaySeconds(self):
- self.unitsVar.set(SECONDS)
- self.updateDisplay()
- def play(self, deltaT, fLoop):
- if self.frameActiveVar.get():
- # Compute new time
- self.currT = self.currT + deltaT
- if fLoop and self.duration:
- # If its looping compute modulo
- loopT = self.currT % self.duration
- self.goToT(loopT)
- else:
- if (self.currT > self.maxSeconds):
- # Clear this actor control from play list
- self['animPanel'].playList.remove(self)
- else:
- self.goToT(self.currT)
- else:
- # Clear this actor control from play list
- self['animPanel'].playList.remove(self)
- def goToF(self, f):
- if self.unitsVar.get() == FRAMES:
- self.frameControl.set(f)
- else:
- self.frameControl.set(f/self.fps)
- def goToT(self, t):
- if self.unitsVar.get() == FRAMES:
- self.frameControl.set(t * self.fps)
- else:
- self.frameControl.set(t)
- def goTo(self, t):
- # Convert scale value to float
- t = float(t)
- # Now convert t to seconds for offset calculations
- if self.unitsVar.get() == FRAMES:
- t = t / self.fps
- # Update currT
- if self.fScaleCommand or self.fOneShot:
- self.currT = t
- self.fOneShot = 0
- # Now update actor (pose specifed as frame count)
- self['actor'].pose(self['active'],
- min(self.maxFrame, int(t * self.fps)))
- def resetToZero(self):
- # This flag forces self.currT to be updated to new value
- self.fOneShot = 1
- self.goToT(0)
- def resetToEnd(self):
- # This flag forces self.currT to be updated to new value
- self.fOneShot = 1
- self.goToT(self.duration)
- def nextFrame(self):
- """
- There needed to be a better way to select an exact frame number
- as the control slider doesn't have the desired resolution
- """
- self.fOneShot = 1
- self.goToT((self.currT+(1/self.fps))%self.duration)
- def previousFrame(self):
- """
- There needed to be a better way to select an exact frame number
- as the control slider doesn't have the desired resolution
- """
- self.fOneShot = 1
- self.goToT((self.currT-(1/self.fps))%self.duration)
- """
- # EXAMPLE CODE
- from direct.actor import Actor
- import AnimPanel
- a = Actor.Actor({250:{"head":"phase_3/models/char/dogMM_Shorts-head-250",
- "torso":"phase_3/models/char/dogMM_Shorts-torso-250",
- "legs":"phase_3/models/char/dogMM_Shorts-legs-250"},
- 500:{"head":"phase_3/models/char/dogMM_Shorts-head-500",
- "torso":"phase_3/models/char/dogMM_Shorts-torso-500",
- "legs":"phase_3/models/char/dogMM_Shorts-legs-500"},
- 1000:{"head":"phase_3/models/char/dogMM_Shorts-head-1000",
- "torso":"phase_3/models/char/dogMM_Shorts-torso-1000",
- "legs":"phase_3/models/char/dogMM_Shorts-legs-1000"}},
- {"head":{"walk":"phase_3/models/char/dogMM_Shorts-head-walk", \
- "run":"phase_3/models/char/dogMM_Shorts-head-run"}, \
- "torso":{"walk":"phase_3/models/char/dogMM_Shorts-torso-walk", \
- "run":"phase_3/models/char/dogMM_Shorts-torso-run"}, \
- "legs":{"walk":"phase_3/models/char/dogMM_Shorts-legs-walk", \
- "run":"phase_3/models/char/dogMM_Shorts-legs-run"}})
- a.attach("head", "torso", "joint-head", 250)
- a.attach("torso", "legs", "joint-hips", 250)
- a.attach("head", "torso", "joint-head", 500)
- a.attach("torso", "legs", "joint-hips", 500)
- a.attach("head", "torso", "joint-head", 1000)
- a.attach("torso", "legs", "joint-hips", 1000)
- a.drawInFront("joint-pupil?", "eyes*", -1, lodName=250)
- a.drawInFront("joint-pupil?", "eyes*", -1, lodName=500)
- a.drawInFront("joint-pupil?", "eyes*", -1, lodName=1000)
- a.setLOD(250, 250, 75)
- a.setLOD(500, 75, 15)
- a.setLOD(1000, 15, 1)
- a.fixBounds()
- a.reparentTo(render)
- a2 = Actor.Actor({250:{"head":"phase_3/models/char/dogMM_Shorts-head-250",
- "torso":"phase_3/models/char/dogMM_Shorts-torso-250",
- "legs":"phase_3/models/char/dogMM_Shorts-legs-250"},
- 500:{"head":"phase_3/models/char/dogMM_Shorts-head-500",
- "torso":"phase_3/models/char/dogMM_Shorts-torso-500",
- "legs":"phase_3/models/char/dogMM_Shorts-legs-500"},
- 1000:{"head":"phase_3/models/char/dogMM_Shorts-head-1000",
- "torso":"phase_3/models/char/dogMM_Shorts-torso-1000",
- "legs":"phase_3/models/char/dogMM_Shorts-legs-1000"}},
- {"head":{"walk":"phase_3/models/char/dogMM_Shorts-head-walk", \
- "run":"phase_3/models/char/dogMM_Shorts-head-run"}, \
- "torso":{"walk":"phase_3/models/char/dogMM_Shorts-torso-walk", \
- "run":"phase_3/models/char/dogMM_Shorts-torso-run"}, \
- "legs":{"walk":"phase_3/models/char/dogMM_Shorts-legs-walk", \
- "run":"phase_3/models/char/dogMM_Shorts-legs-run"}})
- a2.attach("head", "torso", "joint-head", 250)
- a2.attach("torso", "legs", "joint-hips", 250)
- a2.attach("head", "torso", "joint-head", 500)
- a2.attach("torso", "legs", "joint-hips", 500)
- a2.attach("head", "torso", "joint-head", 1000)
- a2.attach("torso", "legs", "joint-hips", 1000)
- a2.drawInFront("joint-pupil?", "eyes*", -1, lodName=250)
- a2.drawInFront("joint-pupil?", "eyes*", -1, lodName=500)
- a2.drawInFront("joint-pupil?", "eyes*", -1, lodName=1000)
- a2.setLOD(250, 250, 75)
- a2.setLOD(500, 75, 15)
- a2.setLOD(1000, 15, 1)
- a2.fixBounds()
- a2.reparentTo(render)
- ap = AnimPanel.AnimPanel([a, a2])
- # Alternately
- ap = a.animPanel()
- ap2 = a2.animPanel()
- """
|