ActorInterval.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. """ActorInterval module: contains the ActorInterval class"""
  2. __all__ = ['ActorInterval', 'LerpAnimInterval']
  3. from pandac.PandaModules import *
  4. from direct.directnotify.DirectNotifyGlobal import *
  5. import Interval
  6. import math
  7. from direct.showbase import LerpBlendHelpers
  8. class ActorInterval(Interval.Interval):
  9. # create ActorInterval DirectNotify category
  10. notify = directNotify.newCategory('ActorInterval')
  11. # Name counter
  12. animNum = 1
  13. # Class methods
  14. # Plays an animation on an Actor. The subrange of the animation
  15. # to be played may be specified via frames (startFrame up to and
  16. # including endFrame) or seconds (startTime up to and including
  17. # endTime). If neither is specified, the default is the entire
  18. # range of the animation.
  19. # The duration may be implicit or explicit. If it is omitted, it
  20. # is taken to be endTime - startTime. There's not much point in
  21. # specifying otherwise unless you also specify loop=1, which will
  22. # loop the animation over its frame range during the duration of
  23. # the interval.
  24. # Note: if loop == 0 and duration > anim duration then the
  25. # animation will play once and then hold its final pose for the
  26. # remainder of the interval.
  27. # loop = 1 implies a loop within the entire range of animation,
  28. # while constrainedLoop = 1 implies a loop within startFrame and
  29. # endFrame only.
  30. def __init__(self, actor, animName, loop=0, constrainedLoop=0,
  31. duration=None, startTime=None, endTime=None,
  32. startFrame=None, endFrame=None,
  33. playRate=1.0, name=None, forceUpdate=0,
  34. partName=None, lodName=None):
  35. # Generate unique id
  36. id = 'Actor-%s-%d' % (animName, ActorInterval.animNum)
  37. ActorInterval.animNum += 1
  38. # Record class specific variables
  39. self.actor = actor
  40. self.animName = animName
  41. self.controls = self.actor.getAnimControls(
  42. self.animName, partName = partName, lodName = lodName)
  43. self.loopAnim = loop
  44. self.constrainedLoop = constrainedLoop
  45. self.forceUpdate = forceUpdate
  46. self.playRate = playRate
  47. # If no name specified, use id as name
  48. if (name == None):
  49. name = id
  50. if len(self.controls) == 0:
  51. self.notify.warning("Unknown animation for actor: %s" % (self.animName))
  52. self.frameRate = 1.0
  53. self.startFrame = 0
  54. self.endFrame = 0
  55. else:
  56. self.frameRate = self.controls[0].getAnim().getBaseFrameRate() * abs(playRate)
  57. # Compute start and end frames.
  58. if startFrame != None:
  59. self.startFrame = startFrame
  60. elif startTime != None:
  61. self.startFrame = startTime * self.frameRate
  62. else:
  63. self.startFrame = 0
  64. if endFrame != None:
  65. self.endFrame = endFrame
  66. elif endTime != None:
  67. self.endFrame = endTime * self.frameRate
  68. elif duration != None:
  69. if startTime == None:
  70. startTime = float(self.startFrame) / float(self.frameRate)
  71. endTime = startTime + duration
  72. self.endFrame = duration * self.frameRate
  73. else:
  74. # No end frame specified. Choose the maximum of all
  75. # of the controls' numbers of frames.
  76. maxFrames = self.controls[0].getNumFrames()
  77. warned = 0
  78. for i in range(1, len(self.controls)):
  79. numFrames = self.controls[i].getNumFrames()
  80. if numFrames != maxFrames and numFrames != 1 and not warned:
  81. self.notify.warning("Animations '%s' on %s have an inconsistent number of frames." % (animName, actor.getName()))
  82. warned = 1
  83. maxFrames = max(maxFrames, numFrames)
  84. self.endFrame = maxFrames - 1
  85. # Must we play the animation backwards? We play backwards if
  86. # either (or both) of the following is true: the playRate is
  87. # negative, or endFrame is before startFrame.
  88. self.reverse = (playRate < 0)
  89. if self.endFrame < self.startFrame:
  90. self.reverse = 1
  91. t = self.endFrame
  92. self.endFrame = self.startFrame
  93. self.startFrame = t
  94. self.numFrames = self.endFrame - self.startFrame + 1
  95. # Compute duration if no duration specified
  96. self.implicitDuration = 0
  97. if duration == None:
  98. self.implicitDuration = 1
  99. duration = float(self.numFrames) / self.frameRate
  100. # Initialize superclass
  101. Interval.Interval.__init__(self, name, duration)
  102. def getCurrentFrame(self):
  103. """Calculate the current frame playing in this interval.
  104. returns a float value between startFrame and endFrame, inclusive
  105. returns None if there are any problems
  106. """
  107. retval = None
  108. if not self.isStopped():
  109. framesPlayed = self.numFrames * self.currT
  110. retval = self.startFrame + framesPlayed
  111. return retval
  112. def privStep(self, t):
  113. frameCount = t * self.frameRate
  114. if self.constrainedLoop:
  115. frameCount = frameCount % self.numFrames
  116. if self.reverse:
  117. absFrame = self.endFrame - frameCount
  118. else:
  119. absFrame = self.startFrame + frameCount
  120. # Calc integer frame number
  121. intFrame = int(math.floor(absFrame + 0.0001))
  122. # Pose anim
  123. # We use our pre-computed list of animControls for
  124. # efficiency's sake, rather than going through the relatively
  125. # expensive Actor interface every frame.
  126. for control in self.controls:
  127. # Each animControl might have a different number of frames.
  128. numFrames = control.getNumFrames()
  129. if self.loopAnim:
  130. frame = (intFrame % numFrames) + (absFrame - intFrame)
  131. else:
  132. frame = max(min(absFrame, numFrames - 1), 0)
  133. control.pose(frame)
  134. if self.forceUpdate:
  135. self.actor.update()
  136. self.state = CInterval.SStarted
  137. self.currT = t
  138. def privFinalize(self):
  139. if self.implicitDuration and not self.loopAnim:
  140. # As a special case, we ensure we end up posed to the last
  141. # frame of the animation if the original duration was
  142. # implicit. This is necessary only to guard against
  143. # possible roundoff error in computing the final frame
  144. # from the duration. We don't do this in the case of a
  145. # looping animation, however, because this would introduce
  146. # a hitch in the animation when it plays back-to-back with
  147. # the next cycle.
  148. if self.reverse:
  149. for control in self.controls:
  150. control.pose(self.startFrame)
  151. else:
  152. for control in self.controls:
  153. control.pose(self.endFrame)
  154. if self.forceUpdate:
  155. self.actor.update()
  156. else:
  157. # Otherwise, the user-specified duration determines which
  158. # is our final frame.
  159. self.privStep(self.getDuration())
  160. self.state = CInterval.SFinal
  161. self.intervalDone()
  162. # If we want to change what part this interval is playing on after
  163. # the interval has been created, call resetControls and pass in a partName
  164. # and optional lod param
  165. def resetControls(self, partName, lodName=None):
  166. self.controls = self.actor.getAnimControls(
  167. self.animName, partName = partName, lodName = lodName)
  168. class LerpAnimInterval(CLerpAnimEffectInterval):
  169. # Blends between two anims. Start both anims first (or use
  170. # parallel ActorIntervals), then invoke LerpAnimInterval to
  171. # smoothly blend the control effect from the first to the second.
  172. lerpAnimNum = 1
  173. def __init__(self, actor, duration, startAnim, endAnim,
  174. startWeight = 0.0, endWeight = 1.0,
  175. blendType = 'noBlend', name = None,
  176. partName=None, lodName=None):
  177. # Generate unique name if necessary
  178. if (name == None):
  179. name = 'LerpAnimInterval-%d' % LerpAnimInterval.lerpAnimNum
  180. LerpAnimInterval.lerpAnimNum += 1
  181. blendType = self.stringBlendType(blendType)
  182. assert blendType != self.BTInvalid
  183. # Initialize superclass
  184. CLerpAnimEffectInterval.__init__(self, name, duration, blendType)
  185. if startAnim != None:
  186. controls = actor.getAnimControls(
  187. startAnim, partName = partName, lodName = lodName)
  188. #controls = actor.getAnimControls(startAnim)
  189. for control in controls:
  190. self.addControl(control, startAnim,
  191. 1.0 - startWeight, 1.0 - endWeight)
  192. if endAnim != None:
  193. controls = actor.getAnimControls(
  194. endAnim, partName = partName, lodName = lodName)
  195. #controls = actor.getAnimControls(endAnim)
  196. for control in controls:
  197. self.addControl(control, endAnim,
  198. startWeight, endWeight)