diff --git a/direct/metalibs/direct/Sources.pp b/direct/metalibs/direct/Sources.pp index e2329a8526..e2e43c873a 100644 --- a/direct/metalibs/direct/Sources.pp +++ b/direct/metalibs/direct/Sources.pp @@ -8,7 +8,7 @@ #define BUILDING_DLL BUILDING_DIRECT #define COMPONENT_LIBS \ - directbase dcparser showbase deadrec directd + directbase dcparser showbase deadrec directd interval #define OTHER_LIBS panda pandaexpress dtoolconfig dtool diff --git a/direct/src/interval/ActorInterval.py b/direct/src/interval/ActorInterval.py index d38b3fea23..42f90f52ba 100644 --- a/direct/src/interval/ActorInterval.py +++ b/direct/src/interval/ActorInterval.py @@ -122,7 +122,7 @@ class ActorInterval(Interval.Interval): self.goToT(t) -class LerpAnimInterval(Interval.Interval): +class LerpAnimInterval(CLerpAnimEffectInterval): # Blends between two anims. Start both anims first (or use # parallel ActorIntervals), then invoke LerpAnimInterval to # smoothly blend the control effect from the first to the second. @@ -131,58 +131,25 @@ class LerpAnimInterval(Interval.Interval): def __init__(self, actor, duration, startAnim, endAnim, startWeight = 0.0, endWeight = 1.0, blendType = 'noBlend', name = None): - """ __init__(actor, duration, pos, startPos, other, blendType, name) - """ # Generate unique name if necessary if (name == None): name = 'LerpAnimInterval-%d' % LerpAnimInterval.lerpAnimNum LerpAnimInterval.lerpAnimNum += 1 - # Record class specific variables - self.actor = actor - self.startAnim = startAnim - self.endAnim = endAnim - self.startWeight = startWeight - self.deltaWeight = endWeight - startWeight - self.blendType = self.getBlend(blendType) + blendType = self.stringBlendType(blendType) + assert(blendType != self.BTInvalid) # Initialize superclass - Interval.Interval.__init__(self, name, duration) + CLerpAnimEffectInterval.__init__(self, name, duration, blendType) - - def updateFunc(self, t, event=Interval.IVAL_NONE): - """ updateFunc(t, event) - Go to time t - """ - if (self.actor.isEmpty()): - self.notify.warning('updateFunc() - %s empty actor!' % self.name) - return - - # First, normalize t into the range 0 .. 1, and apply the blendType. - t = self.blendType(float(t) / self.duration) - - # Then compute the current weight based on the time elapsed so far. - w = self.startWeight + t * self.deltaWeight - - # Apply that weight to the two anims. - if self.startAnim != None: - self.actor.setControlEffect(self.startAnim, 1.0 - w) - if self.endAnim != None: - self.actor.setControlEffect(self.endAnim, w) - - - def getBlend(self, blendType): - """__getBlend(self, string) - Return the C++ blend class corresponding to blendType string - """ - if (blendType == "easeIn"): - return LerpBlendHelpers.easeIn - elif (blendType == "easeOut"): - return LerpBlendHelpers.easeOut - elif (blendType == "easeInOut"): - return LerpBlendHelpers.easeInOut - elif (blendType == "noBlend"): - return LerpBlendHelpers.noBlend - else: - raise Exception( - 'Error: LerpAnimInterval.getBlend: Unknown blend type') + if startAnim != None: + controls = actor.getAnimControls(startAnim) + for control in controls: + self.addControl(control, startAnim, + 1.0 - startWeight, 1.0 - endWeight) + + if endAnim != None: + controls = actor.getAnimControls(endAnim) + for control in controls: + self.addControl(control, endAnim, + startWeight, endWeight) diff --git a/direct/src/interval/FunctionInterval.py b/direct/src/interval/FunctionInterval.py index cfccf205aa..199a11f5fa 100644 --- a/direct/src/interval/FunctionInterval.py +++ b/direct/src/interval/FunctionInterval.py @@ -27,7 +27,7 @@ class FunctionInterval(Interval.Interval): self.function = function # Create a unique name for the interval if necessary if (name == None): - name = 'FunctionInterval-%d' % FunctionInterval.functionIntervalNum + name = 'FunctionInterval-%s-%d' % (function.__name__, FunctionInterval.functionIntervalNum) FunctionInterval.functionIntervalNum += 1 assert(isinstance(name, types.StringType)) # Record any arguments diff --git a/direct/src/interval/Interval.py b/direct/src/interval/Interval.py index a25acdeaea..09eb220ea4 100644 --- a/direct/src/interval/Interval.py +++ b/direct/src/interval/Interval.py @@ -5,9 +5,9 @@ from PandaModules import * import Task # Interval events -IVAL_NONE = 0 -IVAL_INIT = 1 -IVAL_DONE = 2 +IVAL_NONE = CInterval.ETStep +IVAL_INIT = CInterval.ETInitialize +IVAL_DONE = CInterval.ETFinalize class Interval(DirectObject): """Interval class: Base class for timeline functionality""" @@ -75,7 +75,7 @@ class Interval(DirectObject): def setFinalT(self): """ setFinalT() """ - self.setT(self.getDuration(), event=IVAL_DONE) + self.setT(self.getDuration(), IVAL_DONE) def play(self, t0=0.0, duration=0.0, scale=1.0): """ play(t0, duration) @@ -133,7 +133,7 @@ class Interval(DirectObject): if (te < self.endTime): if (self.firstTime): # If first call, init intervals - self.setT(te, event = IVAL_INIT) + self.setT(te, IVAL_INIT) self.firstTime = 0 else: self.setT(te) @@ -142,7 +142,7 @@ class Interval(DirectObject): te = self.endTime if (self.firstTime): # If first call, init intervals - self.setT(te, event = IVAL_INIT) + self.setT(te, IVAL_INIT) self.firstTime = 0 else: self.setT(te, IVAL_DONE) @@ -181,23 +181,23 @@ class Interval(DirectObject): # Kill playback task taskMgr.remove(s.name + '-play') # INIT interval - s.setT(es.get(), event = IVAL_INIT) + s.setT(es.get(), IVAL_INIT) es.onPress = onPress # To make sure you stop free running intervals es.onRelease = lambda s=self: s.stop() # To update scale and execute intervals with IVAL_INIT def onReturn(s = self, es = es): - s.setT(es.get(), event = IVAL_INIT) + s.setT(es.get(), IVAL_INIT) s.stop() es.onReturnRelease = onReturn es.pack(expand = 1, fill = X) bf = Frame(outerFrame) # Jump to start and end def toStart(s=self, es=es): - s.setT(0.0, event = IVAL_INIT) + s.setT(0.0, IVAL_INIT) s.stop() def toEnd(s=self): - s.setT(s.getDuration(), event = IVAL_INIT) + s.setT(s.getDuration(), IVAL_INIT) s.stop() jumpToStart = Button(bf, text = '<<', command = toStart) # Stop/play buttons diff --git a/direct/src/interval/IntervalGlobal.py b/direct/src/interval/IntervalGlobal.py index cb6fa56b70..453f317b04 100644 --- a/direct/src/interval/IntervalGlobal.py +++ b/direct/src/interval/IntervalGlobal.py @@ -9,6 +9,4 @@ from MopathInterval import * from ParticleInterval import * from SoundInterval import * from WaitInterval import * - -from Track import * -from MultiTrack import * +from MetaInterval import * diff --git a/direct/src/interval/LerpInterval.py b/direct/src/interval/LerpInterval.py index 843fb4fc87..104616362a 100644 --- a/direct/src/interval/LerpInterval.py +++ b/direct/src/interval/LerpInterval.py @@ -5,6 +5,262 @@ from DirectNotifyGlobal import * import Interval import LerpBlendHelpers +# +# Most of the intervals defined in this module--the group up here at +# the front of the file--are now derived from a CInterval instead of +# an Interval, so they can run in the low-level C++ code at high +# performance. +# + +class LerpNodePathInterval(CLerpNodePathInterval): + # This is the base class for all of the lerps, defined below, that + # affect a property on a NodePath, like pos or hpr. + lerpNodePathNum = 1 + + def __init__(self, name, duration, blendType, node, other): + if name == None: + name = '%s-%d' % (self.__class__.__name__, self.lerpNodePathNum) + LerpNodePathInterval.lerpNodePathNum += 1 + + blendType = self.stringBlendType(blendType) + assert(blendType != self.BTInvalid) + + if other == None: + other = NodePath() + + CLerpNodePathInterval.__init__(self, name, duration, blendType, + node, other) + + def anyCallable(self, *params): + # Returns true if any of the parameters listed is a callable + # functor, false if none of them are. This is used by derived + # classes to determine if a functor was passed in for a + # parameter. + + for param in params: + if callable(param): + return 1 + return 0 + + def setupParam(self, func, param): + # Stores the indicated parameter by passing it to the given + # function (probably a C++ setter function). If the param is + # a callable functor, calls it; otherwise, uses the param + # directly. + if param != None: + if callable(param): + func(param()) + else: + func(param) + +##################################################################### +## +## It is legal to pass in a functor for the any of the pos, hpr, +## or scale parameters in the intervals below. These will be +## evaluated at the time the interval starts in order to determine +## the actual final position. However, doing so forces the entire +## LerpInterval to be executed up in Python, instead of down in the +## low-level C++ code, at a significant performance cost. +## +##################################################################### + +class LerpPosInterval(LerpNodePathInterval): + def __init__(self, node, duration, pos, startPos = None, + other = None, blendType = 'noBlend', name = None): + LerpNodePathInterval.__init__(self, name, duration, blendType, + node, other) + + # Check for functors in the input parameters. + if self.anyCallable(pos, startPos): + self.endPos = pos + self.startPos = startPos + self.inPython = 1 + else: + self.setEndPos(pos) + if startPos != None: + self.setStartPos(startPos) + + def setT(self, t, event): + # This function is only used if Python functors were passed in + # for some of the input parameters. + if event == Interval.IVAL_INIT: + self.setupParam(self.setEndPos, self.endPos) + self.setupParam(self.setStartPos, self.startPos) + LerpNodePathInterval.setT(self, t, event) + + +class LerpHprInterval(LerpNodePathInterval): + def __init__(self, node, duration, hpr, startHpr = None, + other = None, blendType = 'noBlend', name = None): + LerpNodePathInterval.__init__(self, name, duration, blendType, + node, other) + + # Check for functors in the input parameters. + if self.anyCallable(hpr, startHpr): + self.endHpr = hpr + self.startHpr = startHpr + self.inPython = 1 + else: + self.setEndHpr(hpr) + if startHpr != None: + self.setStartHpr(startHpr) + + def setT(self, t, event): + # This function is only used if Python functors were passed in + # for some of the input parameters. + if event == Interval.IVAL_INIT: + self.setupParam(self.setEndHpr, self.endHpr) + self.setupParam(self.setStartHpr, self.startHpr) + LerpNodePathInterval.setT(self, t, event) + +class LerpScaleInterval(LerpNodePathInterval): + def __init__(self, node, duration, scale, startScale = None, + other = None, blendType = 'noBlend', name = None): + LerpNodePathInterval.__init__(self, name, duration, blendType, + node, other) + # Check for functors in the input parameters. + if self.anyCallable(scale, startScale): + self.endScale = scale + self.startScale = startScale + self.inPython = 1 + else: + self.setEndScale(scale) + if startScale != None: + self.setStartScale(startScale) + + def setT(self, t, event): + # This function is only used if Python functors were passed in + # for some of the input parameters. + if event == Interval.IVAL_INIT: + self.setupParam(self.setEndScale, self.endScale) + self.setupParam(self.setStartScale, self.startScale) + LerpNodePathInterval.setT(self, t, event) + +class LerpPosHprInterval(LerpNodePathInterval): + def __init__(self, node, duration, pos, hpr, + startPos = None, startHpr = None, + other = None, blendType = 'noBlend', name = None): + LerpNodePathInterval.__init__(self, name, duration, blendType, + node, other) + # Check for functors in the input parameters. + if self.anyCallable(pos, startPos, hpr, startHpr): + self.endPos = pos + self.startPos = startPos + self.endHpr = hpr + self.startHpr = startHpr + self.inPython = 1 + else: + self.setEndPos(pos) + if startPos != None: + self.setStartPos(startPos) + self.setEndHpr(hpr) + if startHpr != None: + self.setStartHpr(startHpr) + + def setT(self, t, event): + # This function is only used if Python functors were passed in + # for some of the input parameters. + if event == Interval.IVAL_INIT: + self.setupParam(self.setEndPos, self.endPos) + self.setupParam(self.setStartPos, self.startPos) + self.setupParam(self.setEndHpr, self.endHpr) + self.setupParam(self.setStartHpr, self.startHpr) + LerpNodePathInterval.setT(self, t, event) + +class LerpHprScaleInterval(LerpNodePathInterval): + def __init__(self, node, duration, hpr, scale, + startHpr = None, startScale = None, + other = None, blendType = 'noBlend', name = None): + LerpNodePathInterval.__init__(self, name, duration, blendType, + node, other) + + # Check for functors in the input parameters. + if self.anyCallable(hpr, startHpr, scale, startScale): + self.endHpr = hpr + self.startHpr = startHpr + self.endScale = scale + self.startScale = startScale + self.inPython = 1 + else: + self.setEndHpr(hpr) + if startHpr != None: + self.setStartHpr(startHpr) + self.setEndScale(scale) + if startScale != None: + self.setStartScale(startScale) + + def setT(self, t, event): + # This function is only used if Python functors were passed in + # for some of the input parameters. + if event == Interval.IVAL_INIT: + self.setupParam(self.setEndHpr, self.endHpr) + self.setupParam(self.setStartHpr, self.startHpr) + self.setupParam(self.setEndScale, self.endScale) + self.setupParam(self.setStartScale, self.startScale) + LerpNodePathInterval.setT(self, t, event) + +class LerpPosHprScaleInterval(LerpNodePathInterval): + def __init__(self, node, duration, pos, hpr, scale, + startPos = None, startHpr = None, startScale = None, + other = None, blendType = 'noBlend', name = None): + LerpNodePathInterval.__init__(self, name, duration, blendType, + node, other) + # Check for functors in the input parameters. + if self.anyCallable(pos, startPos, hpr, startHpr, scale, startScale): + self.endPos = pos + self.startPos = startPos + self.endHpr = hpr + self.startHpr = startHpr + self.endScale = scale + self.startScale = startScale + self.inPython = 1 + else: + self.setEndPos(pos) + if startPos != None: + self.setStartPos(startPos) + self.setEndHpr(hpr) + if startHpr != None: + self.setStartHpr(startHpr) + self.setEndScale(scale) + if startScale != None: + self.setStartScale(startScale) + + def setT(self, t, event): + # This function is only used if Python functors were passed in + # for some of the input parameters. + if event == Interval.IVAL_INIT: + self.setupParam(self.setEndPos, self.endPos) + self.setupParam(self.setStartPos, self.startPos) + self.setupParam(self.setEndHpr, self.endHpr) + self.setupParam(self.setStartHpr, self.startHpr) + self.setupParam(self.setEndScale, self.endScale) + self.setupParam(self.setStartScale, self.startScale) + LerpNodePathInterval.setT(self, t, event) + +class LerpColorScaleInterval(LerpNodePathInterval): + def __init__(self, node, duration, colorScale, startColorScale = None, + other = None, blendType = 'noBlend', name = None): + LerpNodePathInterval.__init__(self, name, duration, blendType, + node, other) + self.setEndColorScale(colorScale) + if startColorScale != None: + self.setStartColorScale(startColorScale) + +class LerpColorInterval(LerpNodePathInterval): + def __init__(self, node, duration, color, startColor = None, + other = None, blendType = 'noBlend', name = None): + LerpNodePathInterval.__init__(self, name, duration, blendType, + node, other) + self.setEndColor(color) + if startColor != None: + self.setStartColor(startColor) + + +# +# The remaining intervals defined in this module are the old-school +# Python-based intervals. +# + class LerpInterval(Interval.Interval): # create LerpInterval DirectNotify category notify = directNotify.newCategory('LerpInterval') @@ -51,342 +307,6 @@ class LerpInterval(Interval.Interval): raise Exception( 'Error: LerpInterval.__getBlend: Unknown blend type') -class LerpPosInterval(LerpInterval): - # Name counter - lerpPosNum = 1 - # Class methods - def __init__(self, node, duration, pos, startPos=None, - other=None, blendType='noBlend', name=None): - """ __init__(node, duration, pos, startPos, other, blendType, name) - """ - def functorFunc(node=node, pos=pos, startPos=startPos, - other=other): - assert(not node.isEmpty()) - if callable(pos): - # This may be a thunk that returns a point. - pos = pos() - # Make a our own copy of the parameters: - if (pos != None): pos=Point3(pos) - if (startPos != None): startPos=Point3(startPos) - if (other != None): - # lerp wrt other - if (startPos == None): - startPos = node.getPos(other) - functor = PosLerpFunctor(node, startPos, pos, other) - else: - if (startPos == None): - startPos = node.getPos() - functor = PosLerpFunctor(node, startPos, pos) - return functor - - # Generate unique name if necessary - if (name == None): - name = 'LerpPosInterval-%d' % LerpPosInterval.lerpPosNum - LerpPosInterval.lerpPosNum += 1 - # Initialize superclass - LerpInterval.__init__(self, name, duration, functorFunc, blendType) - -class LerpHprInterval(LerpInterval): - # Name counter - lerpHprNum = 1 - # Class methods - def __init__(self, node, duration, hpr, startHpr=None, - other=None, blendType='noBlend', name=None): - """ __init__(node, duration, hpr, startHpr, other, blendType, name) - """ - def functorFunc(node=node, hpr=hpr, startHpr=startHpr, - other=other): - assert(not node.isEmpty()) - if callable(hpr): - # This may be a thunk that returns a point. - hpr = hpr() - # Make a our own copy of the parameters: - if (hpr != None): hpr=VBase3(hpr) - if (startHpr != None): startHpr=VBase3(startHpr) - if (other != None): - # lerp wrt other - if (startHpr == None): - startHpr = VBase3(node.getHpr(other)) - functor = HprLerpFunctor(node, startHpr, hpr, other) - else: - if (startHpr == None): - startHpr = node.getHpr() - functor = HprLerpFunctor(node, startHpr, hpr) - return functor - - # Generate unique name if necessary - if (name == None): - name = 'LerpHprInterval-%d' % LerpHprInterval.lerpHprNum - LerpHprInterval.lerpHprNum += 1 - # Initialize superclass - LerpInterval.__init__(self, name, duration, functorFunc, blendType) - -class LerpScaleInterval(LerpInterval): - - # Interval counter - lerpScaleNum = 1 - # Class methods - def __init__(self, node, duration, scale, startScale=None, - other=None, blendType='noBlend', name=None): - """ __init__(node, duration, scale, startScale, other, blendType, name) - """ - def functorFunc(node=node, scale=scale, - startScale=startScale, other=other): - assert(not node.isEmpty()) - if callable(scale): - # This may be a thunk that returns a point. - scale = scale() - # Make a our own copy of the parameters: - if (scale != None): scale=VBase3(scale) - if (startScale != None): startScale=VBase3(startScale) - if (other != None): - # lerp wrt other - if (startScale == None): - startScale = node.getScale(other) - functor = ScaleLerpFunctor(node, startScale, scale, other) - else: - if (startScale == None): - startScale = node.getScale() - functor = ScaleLerpFunctor(node, startScale, scale) - return functor - - # Generate unique name if necessary - if (name == None): - name = 'LerpScaleInterval-%d' % LerpScaleInterval.lerpScaleNum - LerpScaleInterval.lerpScaleNum += 1 - # Initialize superclass - LerpInterval.__init__(self, name, duration, functorFunc, blendType) - -class LerpPosHprInterval(LerpInterval): - # Interval counter - - lerpPosHprNum = 1 - - def __init__(self, node, duration, pos, hpr, startPos=None, - startHpr=None, other=None, blendType='noBlend', name=None): - """ __init__(node, duration, pos, hpr, startPos, startHpr, - other, blendType, name) - """ - def functorFunc(node=node, pos=pos, hpr=hpr, - startPos=startPos, startHpr=startHpr, other=other): - assert(not node.isEmpty()) - if callable(pos): - # This may be a thunk that returns a point. - pos = pos() - if callable(hpr): - # This may be a thunk that returns a point. - hpr = hpr() - # Make a our own copy of the parameters: - if (pos != None): pos=Point3(pos) - if (hpr != None): hpr=VBase3(hpr) - if (startPos != None): startPos=Point3(startPos) - if (startHpr != None): startHpr=VBase3(startHpr) - if (other != None): - # lerp wrt other - if (startPos == None): - startPos = node.getPos(other) - if (startHpr == None): - startHpr = node.getHpr(other) - functor = PosHprLerpFunctor( - node, startPos, pos, - startHpr, hpr, other) - else: - if (startPos == None): - startPos = node.getPos() - if (startHpr == None): - startHpr = node.getHpr() - functor = PosHprLerpFunctor( - node, startPos, pos, - startHpr, hpr) - return functor - - # Generate unique name if necessary - if (name == None): - name = 'LerpPosHpr-%d' % LerpPosHprInterval.lerpPosHprNum - LerpPosHprInterval.lerpPosHprNum += 1 - # Initialize superclass - LerpInterval.__init__(self, name, duration, functorFunc, blendType) - -class LerpHprScaleInterval(LerpInterval): - # Interval counter - lerpHprScaleNum = 1 - # Class methods - def __init__(self, node, duration, hpr, scale, - startHpr=None, startScale=None, - other=None, blendType='noBlend', name=None): - """ __init__(node, duration, hpr, scale, - startHpr, startScale, - other, blendType, name) - """ - def functorFunc(node=node, hpr=hpr, scale=scale, - startHpr=startHpr, - startScale=startScale, other=other): - assert(not node.isEmpty()) - if callable(hpr): - # This may be a thunk that returns a point. - hpr = hpr() - if callable(scale): - # This may be a thunk that returns a point. - scale = scale() - # Make a our own copy of the parameters: - if (hpr != None): hpr=VBase3(hpr) - if (scale != None): scale=VBase3(scale) - if (startHpr != None): startHpr=VBase3(startHpr) - if (startScale != None): startScale=VBase3(startScale) - if (other != None): - # lerp wrt other - if (startHpr == None): - startHpr = node.getHpr(other) - if (startScale == None): - startScale = node.getScale(other) - functor = HprScaleLerpFunctor( - node, startHpr, hpr, - startScale, scale, other) - else: - if (startHpr == None): - startHpr = node.getHpr() - if (startScale == None): - startScale = node.getScale() - functor = HprScaleLerpFunctor( - node, startHpr, hpr, startScale, scale) - return functor - - # Generate unique name if necessary - if (name == None): - name = ('LerpHprScale-%d' % - LerpHprScaleInterval.lerpHprScaleNum) - LerpHprScaleInterval.lerpHprScaleNum += 1 - # Initialize superclass - LerpInterval.__init__(self, name, duration, functorFunc, blendType) - -class LerpPosHprScaleInterval(LerpInterval): - # Interval counter - lerpPosHprScaleNum = 1 - # Class methods - def __init__(self, node, duration, pos, hpr, scale, - startPos=None, startHpr=None, startScale=None, - other=None, blendType='noBlend', name=None): - """ __init__(node, duration, pos, hpr, scale, - startPos, startHpr, startScale, - other, blendType, name) - """ - def functorFunc(node=node, pos=pos, hpr=hpr, scale=scale, - startPos=startPos, startHpr=startHpr, - startScale=startScale, other=other): - assert(not node.isEmpty()) - if callable(pos): - # This may be a thunk that returns a point. - pos = pos() - if callable(hpr): - # This may be a thunk that returns a point. - hpr = hpr() - if callable(scale): - # This may be a thunk that returns a point. - scale = scale() - # Make a our own copy of the parameters: - if (pos != None): pos=Point3(pos) - if (hpr != None): hpr=VBase3(hpr) - if (scale != None): scale=VBase3(scale) - if (startPos != None): startPos=Point3(startPos) - if (startHpr != None): startHpr=VBase3(startHpr) - if (startScale != None): startScale=VBase3(startScale) - if (other != None): - # lerp wrt other - if (startPos == None): - startPos = node.getPos(other) - if (startHpr == None): - startHpr = node.getHpr(other) - if (startScale == None): - startScale = node.getScale(other) - functor = PosHprScaleLerpFunctor( - node, startPos, pos, startHpr, hpr, - startScale, scale, other) - else: - if (startPos == None): - startPos = node.getPos() - if (startHpr == None): - startHpr = node.getHpr() - if (startScale == None): - startScale = node.getScale() - functor = PosHprScaleLerpFunctor( - node, startPos, pos, startHpr, hpr, startScale, scale) - return functor - - # Generate unique name if necessary - if (name == None): - name = ('LerpPosHprScale-%d' % - LerpPosHprScaleInterval.lerpPosHprScaleNum) - LerpPosHprScaleInterval.lerpPosHprScaleNum += 1 - # Initialize superclass - LerpInterval.__init__(self, name, duration, functorFunc, blendType) - - -class LerpColorScaleInterval(LerpInterval): - # Name counter - lerpColorScaleNum = 1 - # Class methods - def __init__(self, node, duration, startColor, endColor, - other=None, blendType='noBlend', name=None): - - def functorFunc(node=node, startColor=startColor, endColor=endColor, other=other): - assert(not node.isEmpty()) - if callable(endColor): - # This may be a thunk that returns a point. - endColor = endColor() - if callable(startColor): - # This may be a thunk that returns a point. - startColor = startColor() - # Make a our own copy of the parameters: - if (startColor != None): startColor=VBase4(startColor) - if (endColor != None): endColor=VBase4(endColor) - if (other != None): - functor = ColorScaleLerpFunctor(node, startColor, endColor, other) - else: - functor = ColorScaleLerpFunctor(node, startColor, endColor) - return functor - - # Generate unique name if necessary - if (name == None): - name = 'LerpColorScaleInterval-%d' % LerpColorScaleInterval.lerpColorScaleNum - LerpColorScaleInterval.lerpColorScaleNum += 1 - # Initialize superclass - LerpInterval.__init__(self, name, duration, functorFunc, blendType) - - - -class LerpColorInterval(LerpInterval): - # Name counter - lerpColorNum = 1 - # Class methods - def __init__(self, node, duration, startColor, endColor, - other=None, blendType='noBlend', name=None): - - def functorFunc(node=node, startColor=startColor, - endColor=endColor, other=other): - assert(not node.isEmpty()) - if callable(endColor): - # This may be a thunk that returns a point. - endColor = endColor() - if callable(startColor): - # This may be a thunk that returns a point. - startColor = startColor() - # Make a our own copy of the parameters: - if (startColor != None): startColor=VBase4(startColor) - if (endColor != None): endColor=VBase4(endColor) - if (other != None): - functor = ColorLerpFunctor(node, startColor, endColor, other) - else: - functor = ColorLerpFunctor(node, startColor, endColor) - return functor - - # Generate unique name if necessary - if (name == None): - name = 'LerpColorInterval-%d' % LerpColorInterval.lerpColorNum - LerpColorInterval.lerpColorNum += 1 - # Initialize superclass - LerpInterval.__init__(self, name, duration, functorFunc, blendType) - - class LerpFunctionInterval(Interval.Interval): """ diff --git a/direct/src/interval/MultiTrack.py b/direct/src/interval/MultiTrack.py deleted file mode 100644 index eb5072c849..0000000000 --- a/direct/src/interval/MultiTrack.py +++ /dev/null @@ -1,74 +0,0 @@ -"""MultiTrack module: contains the MultiTrack class""" - -import Interval - - -class MultiTrack(Interval.Interval): - # Name counter - multiTrackNum = 1 - # Class methods - def __init__(self, trackList, name=None): - """__init__(trackList, name) - """ - # Record track list - self.tlist = trackList - # Generate name if necessary - if (name == None): - name = 'MultiTrack-%d' % MultiTrack.multiTrackNum - MultiTrack.multiTrackNum = MultiTrack.multiTrackNum + 1 - # Duration is max of all track durations - duration = self.__computeDuration() - # Initialize superclass - Interval.Interval.__init__(self, name, duration) - # Update stopEventList after initialization - # It is the union of the stopEventLists of all tracks in the MultiTrack - for t in self.tlist: - self.stopEventList = self.stopEventList + t.stopEventList - - # Access track at given index - def __getitem__(self, item): - return self.tlist[item] - - def __computeDuration(self): - """ __computeDuration() - Returns the duration of the longest Track - """ - duration = 0.0 - for t in self.tlist: - dur = t.getDuration() - if (dur > duration): - duration = dur - return duration - - def updateFunc(self, t, event = Interval.IVAL_NONE): - """ updateFunc(t, event) - Go to time t - """ - - for track in self.tlist: - # We used to try to be smart about calling this only in - # certain cases, but in fact we just called it in every - # case anyway, so might as well eliminate all of the - # comparisons. - track.setT(t, event) - - # Print out representation of MultiTrack - def __repr__(self, indent=0): - """ __repr__(indent) - """ - str = Interval.Interval.__repr__(self, indent) + '\n' - for t in self.tlist: - str = str + t.__repr__(indent+1) - return str - - - - - - - - - -class Parallel(MultiTrack): - def __init__(self, *tracks, **kw): - MultiTrack.__init__(self, tracks, **kw) diff --git a/direct/src/interval/Sources.pp b/direct/src/interval/Sources.pp new file mode 100644 index 0000000000..678662e9fa --- /dev/null +++ b/direct/src/interval/Sources.pp @@ -0,0 +1,20 @@ +#begin lib_target + #define TARGET interval + #define LOCAL_LIBS \ + directbase + #define OTHER_LIBS \ + pgraph:c putil:c panda:m express:c pandaexpress:m dtoolconfig dtool + + #define SOURCES \ + config_interval.cxx config_interval.h \ + cInterval.cxx cInterval.I cInterval.h \ + cLerpInterval.cxx cLerpInterval.I cLerpInterval.h \ + cLerpNodePathInterval.cxx cLerpNodePathInterval.I cLerpNodePathInterval.h \ + cLerpAnimEffectInterval.cxx cLerpAnimEffectInterval.I cLerpAnimEffectInterval.h \ + cMetaInterval.cxx cMetaInterval.I cMetaInterval.h \ + showInterval.cxx showInterval.I showInterval.h \ + hideInterval.cxx hideInterval.I hideInterval.h \ + lerp_helpers.h + + #define IGATESCAN all +#end lib_target diff --git a/direct/src/interval/Track.py b/direct/src/interval/Track.py deleted file mode 100644 index 85b214da60..0000000000 --- a/direct/src/interval/Track.py +++ /dev/null @@ -1,267 +0,0 @@ -"""Track module: contains the Track class""" - -import Interval -import types - -PREVIOUS_END = 1 -PREVIOUS_START = 2 -TRACK_START = 3 - -IDATA_IVAL = 0 -IDATA_TIME = 1 -IDATA_TYPE = 2 -IDATA_START = 3 -IDATA_END = 4 - - - -class Track(Interval.Interval): - # Name counter - trackNum = 1 - # Class methods - def __init__(self, intervalList, name=None): - """__init__(intervalList, name) - intervalList: | - '[' , - - [ , PREVIOUS_END | PREVIOUS_START | TRACK_START ] ']' - """ - # Record instance variables - self.currentInterval = None - # Build ilist (need to do this before computing duration) - self.__buildIlist(intervalList) - # Generate unique name if necessary - if (name == None): - name = 'Track-%d' % Track.trackNum - Track.trackNum = Track.trackNum + 1 - # Compute duration - duration = self.__computeDuration() - # Initialize superclass - Interval.Interval.__init__(self, name, duration) - # Update stopEventList - for i in self.ilist: - self.stopEventList = self.stopEventList + i[0].stopEventList - - # Access interval at given index - def __getitem__(self, item): - return self.ilist[item] - - # Create a list of this track's intervals, recording time - # and time type (relative to track start, previous start, or previous end) - def __buildIlist(self, intervalList): - self.ilist = [] - for i in intervalList: - if isinstance(i, Interval.Interval): - self.ilist.append([i, 0.0, PREVIOUS_END, 0.0, 0.0]) - elif (isinstance(i, types.ListType) or - isinstance(i, types.TupleType)): - itime = i[0] - ival = i[1] - try: - type = i[2] - except IndexError: - type = TRACK_START - self.ilist.append([ival, itime, type, 0.0, 0.0]) - else: - print 'Track.__buildIlist: Invalid intervallist entry' - - # Compute duration of the track and precompute start and end time of - # each interval - def __computeDuration(self): - """ __computeDuration() - """ - duration = 0.0 - prev = None - for idata in self.ilist: - ival = idata[IDATA_IVAL] - itime = idata[IDATA_TIME] - type = idata[IDATA_TYPE] - assert(itime >= 0.0) - # Compute fill time, time between end of last interval and - # start of this one. Depend on interval type - fillTime = itime - if (type == PREVIOUS_END): - pass - elif (type == PREVIOUS_START): - if (prev != None): - fillTime = itime - prev.getDuration() - elif (type == TRACK_START): - fillTime = itime - duration - else: - self.notify.error( - 'Track.__computeDuration(): unknown type: %d' % type) - # Check for overlap - if (fillTime < 0.0): - self.notify.error( - 'Track.__computeDuration(): overlap detected') - # Compute start time of interval - idata[IDATA_START] = duration + fillTime - # Compute end time of interval - idata[IDATA_END] = idata[IDATA_START] + ival.getDuration() - # Keep track of cumulative duration - duration = idata[IDATA_END] - prev = ival - return duration - - def setIntervalStartTime(self, name, itime, type=TRACK_START): - """ setIntervalStartTime(name, itime, type) - """ - found = 0 - # Check for interval in current interval list - for idata in self.ilist: - # If found, update time and type - if (idata[IDATA_IVAL].getName() == name): - idata[IDATA_TIME] = itime - idata[IDATA_TYPE] = type - found = 1 - break - if (found): - # And recompute duration - self.duration = self.__computeDuration() - else: - self.notify.warning( - 'Track.setIntervalStartTime(): no Interval named: %s' % name) - - def getIntervalStartTime(self, name): - """ getIntervalStartTime(name) - """ - # Search for interval of given name - for idata in self.ilist: - if (idata[IDATA_IVAL].getName() == name): - return idata[IDATA_START] - self.notify.warning( - 'Track.getIntervalStartTime(): no Interval named: %s' % name) - return None - - def __getIntervalStartTime(self, interval): - """ __getIntervalStartTime(interval) - """ - # Search for given interval - for idata in self.ilist: - if (idata[IDATA_IVAL] == interval): - return idata[IDATA_START] - self.notify.warning( - 'Track.getIntervalStartTime(): Interval not found') - return None - - def getIntervalEndTime(self, name): - """ getIntervalEndTime(name) - """ - # Search for interval of given name - for idata in self.ilist: - if (idata[IDATA_IVAL].getName() == name): - return idata[IDATA_END] - self.notify.warning( - 'Track.getIntervalEndTime(): no Interval named: %s' % name) - return None - - def updateFunc(self, t, event = Interval.IVAL_NONE): - """ updateFunc(t, event) - Go to time t - """ - # Deterimine which interval, if any to evaluate - if (self.currentInterval != None and - event == Interval.IVAL_NONE and - t > self.currentStart and t < self.currentEnd): - # If nothing interesting happened--we're still somewhere - # within the same interval we were within last time--just - # run that one. Trivial case. - self.currentInterval.setT(t - self.currentStart, event) - - elif (t < 0): - # Before start of track, do nothing - pass - - else: - # The more sophisticated, look-for-the-proper-interval case. - - # Make sure track actually contains some intervals - if not self.ilist: - self.notify.warning( - 'Track.updateFunc(): track has no intervals') - return - - # Initialize local variables - currentInterval = None - currentStart = 0.0 - currentEnd = 0.0 - # First entry, re-init instance variables - if (event == Interval.IVAL_INIT): - # Initialize prev_t to 0.0 - self.prev_t = 0.0 - # Clear record of currentInterval - self.currentInterval = None - # Compare t with start and end of each interval to determine - # which interval(s) to execute. - # If t falls between the start and end of an interval, that - # becomes the current interval. If we've crossed over the end - # of an interval ((prev_t < tEnd) and (t > tEnd)) then execute - # that interval at its final value. If we've crossed over the - # start of an interval ((prev_t > tStart) and (t < tStart)) - # then execute that interval at its start value - for ival, itime, itype, tStart, tEnd in self.ilist: - # Compare time with each ival's start/end times - if (t < tStart): - if (event == Interval.IVAL_DONE): - # This should only happen in cases of floating - # point instability where t is very close to - # but less than tStart - ival.setT(ival.getDuration(), event) - elif (self.prev_t > tStart): - # We just crossed the start of this interval - # going backwards (e.g. via the slider) - # Execute this interval at its start time - ival.setT(0.0, event) - # Done checking intervals - break - elif (t >= tStart) and (t <= tEnd): - # Between start/end, record current interval - # Make sure event == Interval.IVAL_INIT if entering new interval - if ((event == Interval.IVAL_NONE) and - ((self.prev_t < tStart) or - (ival != self.currentInterval))): - event = Interval.IVAL_INIT - # Evaluate interval at interval relative time - if (event == Interval.IVAL_DONE): - ival.setT(ival.getDuration(), event) - else: - ival.setT(t - tStart, event) - currentInterval = ival - currentStart = tStart - currentEnd = tEnd - elif (t > tEnd): - # Crossing over interval end - if (((event == Interval.IVAL_NONE) or (event == Interval.IVAL_DONE)) and - (self.prev_t < tEnd)): - # We've just crossed the end of this interval, - # execute the interval at its end time - # and flag event as Interval.IVAL_DONE - ival.setT(ival.getDuration(), Interval.IVAL_DONE) - elif ((event == Interval.IVAL_INIT) and ival.getOpenEnded()): - # or its an INIT event after the interval's end - # and the interval is openended, - # then execute the interval at its end time - ival.setT(ival.getDuration(), Interval.IVAL_INIT) - # May not be the last, keep checking other intervals - # Record current interval (may be None) - self.currentInterval = currentInterval - self.currentStart = currentStart - self.currentEnd = currentEnd - - # Create a printable representation of the track - def __repr__(self, indent=0): - """ __repr__(indent) - """ - str = Interval.Interval.__repr__(self, indent) + '\n' - for idata in self.ilist: - # Tack on start and end time for this interval - str = (str + idata[IDATA_IVAL].__repr__(indent+1) + - (' start: %0.2f end: %0.2f' % - (idata[IDATA_START], idata[IDATA_END])) + '\n' - ) - return str - - -class Sequence(Track): - def __init__(self, *intervals, **kw): - Track.__init__(self, intervals, **kw) diff --git a/direct/src/interval/cInterval.I b/direct/src/interval/cInterval.I new file mode 100644 index 0000000000..07cbaa5bdb --- /dev/null +++ b/direct/src/interval/cInterval.I @@ -0,0 +1,127 @@ +// Filename: cInterval.I +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: CInterval::get_name +// Access: Published +// Description: Returns the interval's name. +//////////////////////////////////////////////////////////////////// +INLINE const string &CInterval:: +get_name() const { + return _name; +} + +//////////////////////////////////////////////////////////////////// +// Function: CInterval::get_duration +// Access: Published +// Description: Returns the duration of the interval in seconds. +//////////////////////////////////////////////////////////////////// +INLINE double CInterval:: +get_duration() const { + recompute(); + return _duration; +} + +//////////////////////////////////////////////////////////////////// +// Function: CInterval::get_open_ended +// Access: Published +// Description: Returns the state of the "open_ended" flag. This is +// primarily intended for instantaneous intervals like +// FunctionIntervals; it indicates true if the interval +// has some lasting effect that should be applied even +// if the interval doesn't get started until after its +// finish time, or false if the interval is a transitive +// thing that doesn't need to be called late. +//////////////////////////////////////////////////////////////////// +INLINE bool CInterval:: +get_open_ended() const { + return _open_ended; +} + +//////////////////////////////////////////////////////////////////// +// Function: CInterval::set_t +// Access: Published +// Description: Advances the interval. This function is provided +// mainly to provide compatibility with the Python +// Interval class; you should use initialize(), step(), +// and finalize() instead. +//////////////////////////////////////////////////////////////////// +INLINE void CInterval:: +set_t(double t, CInterval::EventType event) { + switch (event) { + case ET_initialize: + initialize(t); + break; + + case ET_instant: + instant(); + break; + + case ET_step: + step(t); + break; + + case ET_finalize: + finalize(); + break; + + case ET_reverse_initialize: + reverse_initialize(t); + break; + + case ET_reverse_instant: + reverse_instant(); + break; + + case ET_reverse_finalize: + reverse_finalize(); + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CInterval::get_t +// Access: Published +// Description: Returns the current time of the interval: the last +// value of t passed to initialize(), step(), or +// finalize() (or set_t()). +//////////////////////////////////////////////////////////////////// +INLINE double CInterval:: +get_t() const { + return _curr_t; +} + +//////////////////////////////////////////////////////////////////// +// Function: CInterval::recompute +// Access: Protected +// Description: Calls do_recompute() if the dirty flag has been set. +//////////////////////////////////////////////////////////////////// +INLINE void CInterval:: +recompute() const { + if (_dirty) { + ((CInterval *)this)->do_recompute(); + } +} + +INLINE ostream & +operator << (ostream &out, const CInterval &ival) { + ival.output(out); + return out; +} + diff --git a/direct/src/interval/cInterval.cxx b/direct/src/interval/cInterval.cxx new file mode 100644 index 0000000000..725f99a596 --- /dev/null +++ b/direct/src/interval/cInterval.cxx @@ -0,0 +1,362 @@ +// Filename: cInterval.cxx +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "cInterval.h" +#include "indent.h" +#include "clockObject.h" + +TypeHandle CInterval::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CInterval::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +CInterval:: +CInterval(const string &name, double duration, bool open_ended) : + _curr_t(0.0), + _name(name), + _duration(duration), + _open_ended(open_ended), + _dirty(false) +{ + _clock_start = 0.0; + _start_t = 0.0; + _end_t = 0.0; + _start_t_at_start = false; + _end_t_at_end = false; + _play_rate = 1.0; + _loop_count = 0; + _restart = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CInterval::setup_play +// Access: Published +// Description: Called to prepare the interval for automatic timed +// playback, e.g. via a Python task. The interval will +// be played from start_t to end_t, at a time factor +// specified by play_rate. start_t must always be less +// than end_t (except for the exception for end_t == -1, +// below), but if play_rate is negative the interval +// will be played backwards. +// +// Specify end_t of -1 to play the entire interval from +// start_t. +// +// Call step_play() repeatedly to execute the interval. +//////////////////////////////////////////////////////////////////// +void CInterval:: +setup_play(double start_t, double end_t, double play_rate) { + nassertv(start_t < end_t || end_t < 0.0); + nassertv(play_rate != 0.0); + + double duration = get_duration(); + + if (start_t < 0.0) { + _start_t = 0.0; + _start_t_at_start = true; + } else if (start_t > duration) { + _start_t = duration; + _start_t_at_start = false; + } else { + _start_t = start_t; + _start_t_at_start = false; + } + if (end_t < 0.0 || end_t >= duration) { + _end_t = duration; + _end_t_at_end = true; + } else { + _end_t = end_t; + _end_t_at_end = false; + } + + _clock_start = ClockObject::get_global_clock()->get_frame_time(); + _play_rate = play_rate; + _loop_count = 0; + _restart = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: CInterval::step_play +// Access: Published +// Description: Should be called once per frame to execute the +// automatic timed playback begun with setup_play(). +// The return value is the number of times the interval +// is about to repeat; stop when this reaches one to +// play the interval through exactly once. +//////////////////////////////////////////////////////////////////// +int CInterval:: +step_play() { + double now = ClockObject::get_global_clock()->get_frame_time(); + + if (_play_rate >= 0.0) { + double t = (now - _clock_start) * _play_rate + _start_t; + + if (t < _end_t) { + // In the middle of the interval, not a problem. + if (_restart) { + initialize(t); + _restart = false; + } else { + step(t); + } + + } else { + // Past the ending point; time to finalize. + if (_end_t_at_end) { + // Only finalize if the playback cycle includes the whole + // interval. + if (_restart) { + if (get_open_ended() || _loop_count != 0) { + instant(); + } + } else { + finalize(); + _restart = true; + } + } else { + if (_restart) { + initialize(_end_t); + _restart = false; + } else { + step(_end_t); + } + } + + // Advance the clock for the next loop cycle. We might have to + // advance multiple times if we skipped several cycles in the past + // frame. + + if (_end_t == _start_t) { + // If the interval has no length, we loop exactly once each + // time. + _loop_count++; + + } else { + // Otherwise, figure out how many loops we need to skip. + double time_per_loop = (_end_t - _start_t) / _play_rate; + double num_loops = floor((now - _clock_start) / time_per_loop); + _loop_count += (int)num_loops; + _clock_start += num_loops * time_per_loop; + } + } + + } else { + // Playing backwards. + double t = (now - _clock_start) * _play_rate + _end_t; + + if (t >= _start_t) { + // In the middle of the interval, not a problem. + if (_restart) { + reverse_initialize(t); + _restart = false; + } else { + step(t); + } + + } else { + // Past the ending point; time to finalize. + if (_start_t_at_start) { + // Only finalize if the playback cycle includes the whole + // interval. + if (_restart) { + if (get_open_ended() || _loop_count != 0) { + reverse_instant(); + } + } else { + reverse_finalize(); + _restart = true; + } + } else { + if (_restart) { + reverse_initialize(_start_t); + _restart = false; + } else { + step(_start_t); + } + } + + // Advance the clock for the next loop cycle. We might have to + // advance multiple times if we skipped several cycles in the past + // frame. + + if (_end_t == _start_t) { + // If the interval has no length, we loop exactly once each + // time. + _loop_count++; + + } else { + // Otherwise, figure out how many loops we need to skip. + double time_per_loop = (_end_t - _start_t) / -_play_rate; + double num_loops = floor((now - _clock_start) / time_per_loop); + _loop_count += (int)num_loops; + _clock_start += num_loops * time_per_loop; + } + } + } + + return _loop_count; +} + +//////////////////////////////////////////////////////////////////// +// Function: CInterval::initialize +// Access: Published, Virtual +// Description: This replaces the first call to step(), and indicates +// that the interval has just begun. This may be +// overridden by derived classes that need to do some +// explicit initialization on the first call. +//////////////////////////////////////////////////////////////////// +void CInterval:: +initialize(double t) { + recompute(); + step(t); +} + +//////////////////////////////////////////////////////////////////// +// Function: CInterval::instant +// Access: Published, Virtual +// Description: This is called in lieu of initialize() .. step() +// .. finalize(), when everything is to happen within +// one frame. The interval should initialize itself, +// then leave itself in the final state. +//////////////////////////////////////////////////////////////////// +void CInterval:: +instant() { + recompute(); + step(get_duration()); +} + +//////////////////////////////////////////////////////////////////// +// Function: CInterval::step +// Access: Published, Virtual +// Description: Advances the time on the interval. The time may +// either increase (the normal case) or decrease +// (e.g. if the interval is being played by a slider). +//////////////////////////////////////////////////////////////////// +void CInterval:: +step(double t) { + _curr_t = t; +} + +//////////////////////////////////////////////////////////////////// +// Function: CInterval::finalize +// Access: Published, Virtual +// Description: This is called to stop an interval, forcing it to +// whatever state it would be after it played all the +// way through. It's generally invoked by +// set_final_t(). +//////////////////////////////////////////////////////////////////// +void CInterval:: +finalize() { + step(get_duration()); +} + +//////////////////////////////////////////////////////////////////// +// Function: CInterval::reverse_initialize +// Access: Published, Virtual +// Description: Similar to initialize(), but this is called when the +// interval is being played backwards; it indicates that +// the interval should start at the finishing state and +// undo any intervening intervals. +//////////////////////////////////////////////////////////////////// +void CInterval:: +reverse_initialize(double t) { + recompute(); + step(t); +} + +//////////////////////////////////////////////////////////////////// +// Function: CInterval::reverse_instant +// Access: Published, Virtual +// Description: This is called in lieu of reverse_initialize() +// .. step() .. reverse_finalize(), when everything is +// to happen within one frame. The interval should +// initialize itself, then leave itself in the initial +// state. +//////////////////////////////////////////////////////////////////// +void CInterval:: +reverse_instant() { + recompute(); + step(0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: CInterval::reverse_finalize +// Access: Published, Virtual +// Description: Called generally following a reverse_initialize(), +// this indicates the interval should set itself to the +// initial state. +//////////////////////////////////////////////////////////////////// +void CInterval:: +reverse_finalize() { + step(0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: CInterval::output +// Access: Published, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CInterval:: +output(ostream &out) const { + out << get_name(); + if (get_duration() != 0.0) { + out << " dur " << get_duration(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CInterval::write +// Access: Published, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CInterval:: +write(ostream &out, int indent_level) const { + indent(out, indent_level) << *this << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: CInterval::mark_dirty +// Access: Public +// Description: Called by a derived class to indicate the interval has +// been changed internally and must be recomputed before +// its duration may be returned. +//////////////////////////////////////////////////////////////////// +void CInterval:: +mark_dirty() { + if (!_dirty) { + _dirty = true; + Parents::iterator pi; + for (pi = _parents.begin(); pi != _parents.end(); ++pi) { + (*pi)->mark_dirty(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CInterval::do_recompute +// Access: Protected, Virtual +// Description: Does whatever processing is necessary to recompute +// the interval after a call to mark_dirty() has +// indicated a recomputation is necessary. +//////////////////////////////////////////////////////////////////// +void CInterval:: +do_recompute() { + _dirty = false; +} diff --git a/direct/src/interval/cInterval.h b/direct/src/interval/cInterval.h new file mode 100644 index 0000000000..8c393c40d4 --- /dev/null +++ b/direct/src/interval/cInterval.h @@ -0,0 +1,135 @@ +// Filename: cInterval.h +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef CINTERVAL_H +#define CINTERVAL_H + +#include "directbase.h" +#include "typedReferenceCount.h" +#include "pvector.h" + +//////////////////////////////////////////////////////////////////// +// Class : CInterval +// Description : The base class for timeline components. A CInterval +// represents a single action, event, or collection of +// nested intervals that will be performed at some +// specific time or over a period of time. +// +// This is essentially similar to the Python "Interval" +// class, but it is implemented in C++ (hence the name). +// Intervals that may be implemented in C++ will inherit +// from this class; Intervals that must be implemented +// in Python will inherit from the similar Python class. +//////////////////////////////////////////////////////////////////// +class EXPCL_DIRECT CInterval : public TypedReferenceCount { +public: + CInterval(const string &name, double duration, bool open_ended); + +PUBLISHED: + INLINE const string &get_name() const; + INLINE double get_duration() const; + INLINE bool get_open_ended() const; + + enum EventType { + ET_initialize, + ET_instant, + ET_step, + ET_finalize, + ET_reverse_initialize, + ET_reverse_instant, + ET_reverse_finalize + }; + + INLINE void set_t(double t, EventType event = ET_step); + INLINE double get_t() const; + + void setup_play(double start_time, double end_time, double play_rate); + int step_play(); + + // These functions control the actual playback of the interval. + virtual void initialize(double t); + virtual void instant(); + virtual void step(double t); + virtual void finalize(); + virtual void reverse_initialize(double t); + virtual void reverse_instant(); + virtual void reverse_finalize(); + + virtual void output(ostream &out) const; + virtual void write(ostream &out, int indent_level) const; + +public: + void mark_dirty(); + +protected: + INLINE void recompute() const; + virtual void do_recompute(); + + double _curr_t; + string _name; + double _duration; + + // For setup_play() and step_play(). + double _clock_start; + double _start_t; + double _end_t; + bool _end_t_at_end; + bool _start_t_at_start; + double _play_rate; + int _loop_count; + bool _restart; + +private: + bool _open_ended; + bool _dirty; + + // We keep a record of the "parent" intervals (that is, any + // CMetaInterval objects that keep a pointer to this one) strictly + // so we can mark all of our parents dirty when this interval gets + // dirty. + typedef pvector Parents; + Parents _parents; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "CInterval", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; + + friend class CMetaInterval; +}; + +INLINE ostream &operator << (ostream &out, const CInterval &ival); + +#include "cInterval.I" + +#endif + + + diff --git a/direct/src/interval/cLerpAnimEffectInterval.I b/direct/src/interval/cLerpAnimEffectInterval.I new file mode 100644 index 0000000000..0e91068030 --- /dev/null +++ b/direct/src/interval/cLerpAnimEffectInterval.I @@ -0,0 +1,62 @@ +// Filename: cLerpAnimEffectInterval.I +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: CLerpAnimEffectInterval::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CLerpAnimEffectInterval:: +CLerpAnimEffectInterval(const string &name, double duration, + CLerpInterval::BlendType blend_type) : + CLerpInterval(name, duration, blend_type) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpAnimEffectInterval::add_control +// Access: Published +// Description: Adds another AnimControl to the list of AnimControls +// affected by the lerp. This control will be lerped +// from begin_effect to end_effect over the period of +// the lerp. +// +// The AnimControl name parameter is only used when +// formatting the interval for output. +//////////////////////////////////////////////////////////////////// +INLINE void CLerpAnimEffectInterval:: +add_control(AnimControl *control, const string &name, + float begin_effect, float end_effect) { + _controls.push_back(ControlDef(control, name, begin_effect, end_effect)); +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpAnimEffectInterval::ControlDef::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CLerpAnimEffectInterval::ControlDef:: +ControlDef(AnimControl *control, const string &name, + float begin_effect, float end_effect) : + _control(control), + _name(name), + _begin_effect(begin_effect), + _end_effect(end_effect) +{ +} diff --git a/direct/src/interval/cLerpAnimEffectInterval.cxx b/direct/src/interval/cLerpAnimEffectInterval.cxx new file mode 100644 index 0000000000..4744be4518 --- /dev/null +++ b/direct/src/interval/cLerpAnimEffectInterval.cxx @@ -0,0 +1,70 @@ +// Filename: cLerpAnimEffectInterval.cxx +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "cLerpAnimEffectInterval.h" +#include "lerp_helpers.h" +#include "partBundle.h" + +TypeHandle CLerpAnimEffectInterval::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CLerpAnimEffectInterval::step +// Access: Published, Virtual +// Description: Advances the time on the interval. The time may +// either increase (the normal case) or decrease +// (e.g. if the interval is being played by a slider). +//////////////////////////////////////////////////////////////////// +void CLerpAnimEffectInterval:: +step(double t) { + double d = compute_delta(t); + + Controls::iterator ci; + for (ci = _controls.begin(); ci != _controls.end(); ++ci) { + ControlDef &def = (*ci); + float effect; + lerp_value(effect, d, def._begin_effect, def._end_effect); + def._control->get_part()->set_control_effect(def._control, effect); + } + + _curr_t = t; +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpAnimEffectInterval::output +// Access: Published, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CLerpAnimEffectInterval:: +output(ostream &out) const { + out << get_name() << ": "; + + if (_controls.empty()) { + out << "(no controls)"; + } else { + Controls::const_iterator ci; + ci = _controls.begin(); + out << (*ci)._name; + ++ci; + while (ci != _controls.end()) { + out << ", " << (*ci)._name; + ++ci; + } + } + + out << " dur " << get_duration(); +} diff --git a/direct/src/interval/cLerpAnimEffectInterval.h b/direct/src/interval/cLerpAnimEffectInterval.h new file mode 100644 index 0000000000..9b5b18642e --- /dev/null +++ b/direct/src/interval/cLerpAnimEffectInterval.h @@ -0,0 +1,87 @@ +// Filename: cLerpAnimEffectInterval.h +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef CLERPANIMEFFECTINTERVAL_H +#define CLERPANIMEFFECTINTERVAL_H + +#include "directbase.h" +#include "cLerpInterval.h" +#include "animControl.h" +#include "pointerTo.h" +#include "pvector.h" + +//////////////////////////////////////////////////////////////////// +// Class : CLerpAnimEffectInterval +// Description : This interval lerps between different amounts of +// control effects for various AnimControls that might +// be playing on an actor. It's used to change the +// blending amount between multiple animations. +// +// The idea is to start all the animations playing +// first, then use a CLerpAnimEffectInterval to adjust +// the degree to which each animation affects the actor. +//////////////////////////////////////////////////////////////////// +class EXPCL_DIRECT CLerpAnimEffectInterval : public CLerpInterval { +PUBLISHED: + INLINE CLerpAnimEffectInterval(const string &name, double duration, + BlendType blend_type); + + INLINE void add_control(AnimControl *control, const string &name, + float begin_effect, float end_effect); + + virtual void step(double t); + + virtual void output(ostream &out) const; + +private: + class ControlDef { + public: + INLINE ControlDef(AnimControl *control, const string &name, + float begin_effect, float end_effect); + PT(AnimControl) _control; + string _name; + float _begin_effect; + float _end_effect; + }; + + typedef pvector Controls; + Controls _controls; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CLerpInterval::init_type(); + register_type(_type_handle, "CLerpAnimEffectInterval", + CLerpInterval::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "cLerpAnimEffectInterval.I" + +#endif + diff --git a/direct/src/interval/cLerpInterval.I b/direct/src/interval/cLerpInterval.I new file mode 100644 index 0000000000..ca2940ea37 --- /dev/null +++ b/direct/src/interval/cLerpInterval.I @@ -0,0 +1,43 @@ +// Filename: cLerpInterval.I +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: CLerpInterval::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CLerpInterval:: +CLerpInterval(const string &name, double duration, + CLerpInterval::BlendType blend_type) : + CInterval(name, duration, true), + _blend_type(blend_type) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpInterval::get_blend_type +// Access: Published +// Description: Returns the blend type specified for the interval. +// This controls how the linear interpolation behaves +// near the beginning and end of the lerp period. +//////////////////////////////////////////////////////////////////// +INLINE CLerpInterval::BlendType CLerpInterval:: +get_blend_type() const { + return _blend_type; +} diff --git a/direct/src/interval/cLerpInterval.cxx b/direct/src/interval/cLerpInterval.cxx new file mode 100644 index 0000000000..57166c21ce --- /dev/null +++ b/direct/src/interval/cLerpInterval.cxx @@ -0,0 +1,85 @@ +// Filename: cLerpInterval.cxx +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "cLerpInterval.h" +#include "string_utils.h" + +TypeHandle CLerpInterval::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CLerpInterval::string_blend_type +// Access: Published, Static +// Description: Returns the BlendType enumerated value corresponding +// to the indicated string, or BT_invalid if the string +// doesn't match anything. +//////////////////////////////////////////////////////////////////// +CLerpInterval::BlendType CLerpInterval:: +string_blend_type(const string &blend_type) { + if (blend_type == "easeIn") { + return BT_ease_in; + } else if (blend_type == "easeOut") { + return BT_ease_out; + } else if (blend_type == "easeInOut") { + return BT_ease_in_out; + } else if (blend_type == "noBlend") { + return BT_no_blend; + } else { + return BT_invalid; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpInterval::compute_delta +// Access: Protected +// Description: Given a t value in the range [0, get_duration()], +// returns the corresponding delta value clamped to the +// range [0, 1], after scaling by duration and applying +// the blend type. +//////////////////////////////////////////////////////////////////// +double CLerpInterval:: +compute_delta(double t) const { + double duration = get_duration(); + if (duration == 0.0) { + return 0.0; + } + t /= duration; + t = min(max(t, 0.0), 1.0); + + switch (_blend_type) { + case BT_ease_in: + { + double t2 = t * t; + return ((3.0 * t2) - (t2 * t)) * 0.5; + } + + case BT_ease_out: + { + double t2 = t * t; + return ((3.0 * t2) - (t2 * t)) * 0.5; + } + + case BT_ease_in_out: + { + double t2 = t * t; + return (3.0 * t2) - (2.0 * t * t2); + } + + default: + return t; + } +} diff --git a/direct/src/interval/cLerpInterval.h b/direct/src/interval/cLerpInterval.h new file mode 100644 index 0000000000..b2eb04c2fa --- /dev/null +++ b/direct/src/interval/cLerpInterval.h @@ -0,0 +1,78 @@ +// Filename: cLerpInterval.h +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef CLERPINTERVAL_H +#define CLERPINTERVAL_H + +#include "directbase.h" +#include "cInterval.h" + +//////////////////////////////////////////////////////////////////// +// Class : CLerpInterval +// Description : The base class for a family of intervals that +// linearly interpolate one or more numeric values over +// time. +//////////////////////////////////////////////////////////////////// +class EXPCL_DIRECT CLerpInterval : public CInterval { +PUBLISHED: + enum BlendType { + BT_no_blend, + BT_ease_in, + BT_ease_out, + BT_ease_in_out, + BT_invalid + }; + +public: + INLINE CLerpInterval(const string &name, double duration, + BlendType blend_type); + +PUBLISHED: + INLINE BlendType get_blend_type() const; + + static BlendType string_blend_type(const string &blend_type); + +protected: + double compute_delta(double t) const; + +private: + BlendType _blend_type; + + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CInterval::init_type(); + register_type(_type_handle, "CLerpInterval", + CInterval::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "cLerpInterval.I" + +#endif + diff --git a/direct/src/interval/cLerpNodePathInterval.I b/direct/src/interval/cLerpNodePathInterval.I new file mode 100644 index 0000000000..ae9a59f377 --- /dev/null +++ b/direct/src/interval/cLerpNodePathInterval.I @@ -0,0 +1,219 @@ +// Filename: cLerpNodePathInterval.I +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: CLerpNodePathInterval::get_node +// Access: Published +// Description: Returns the node being lerped. +//////////////////////////////////////////////////////////////////// +INLINE const NodePath &CLerpNodePathInterval:: +get_node() const { + return _node; +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpNodePathInterval::get_other +// Access: Published +// Description: Returns the "other" node, which the lerped node is +// being moved relative to. If this is an empty node +// path, the lerped node is being moved in its own +// coordinate system. +//////////////////////////////////////////////////////////////////// +INLINE const NodePath &CLerpNodePathInterval:: +get_other() const { + return _other; +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpNodePathInterval::set_start_pos +// Access: Published +// Description: Indicates the initial position of the lerped node. +// This is meaningful only if set_end_pos() is also +// called. This parameter is optional; if unspecified, +// the value will be taken from the node's actual +// position at the time the lerp is performed. +//////////////////////////////////////////////////////////////////// +INLINE void CLerpNodePathInterval:: +set_start_pos(const LVecBase3f &pos) { + _start_pos = pos; + _flags |= F_start_pos; +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpNodePathInterval::set_end_pos +// Access: Published +// Description: Indicates that the position of the node should be +// lerped, and specifies the final position of the node. +// This should be called before initialize(). If this +// is not called, the node's position will not be +// affected by the lerp. +//////////////////////////////////////////////////////////////////// +INLINE void CLerpNodePathInterval:: +set_end_pos(const LVecBase3f &pos) { + _end_pos = pos; + _flags |= F_end_pos; +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpNodePathInterval::set_start_hpr +// Access: Published +// Description: Indicates the initial rotation of the lerped node. +// This is meaningful only if set_end_hpr() is also +// called. This parameter is optional; if unspecified, +// the value will be taken from the node's actual +// rotation at the time the lerp is performed. +//////////////////////////////////////////////////////////////////// +INLINE void CLerpNodePathInterval:: +set_start_hpr(const LVecBase3f &hpr) { + _start_hpr = hpr; + _flags |= F_start_hpr; +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpNodePathInterval::set_end_hpr +// Access: Published +// Description: Indicates that the rotation of the node should be +// lerped, and specifies the final rotation of the node. +// This should be called before initialize(). If this +// is not called, the node's rotation will not be +// affected by the lerp. +//////////////////////////////////////////////////////////////////// +INLINE void CLerpNodePathInterval:: +set_end_hpr(const LVecBase3f &hpr) { + _end_hpr = hpr; + _flags |= F_end_hpr; +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpNodePathInterval::set_start_scale +// Access: Published +// Description: Indicates the initial scale of the lerped node. +// This is meaningful only if set_end_scale() is also +// called. This parameter is optional; if unspecified, +// the value will be taken from the node's actual +// scale at the time the lerp is performed. +//////////////////////////////////////////////////////////////////// +INLINE void CLerpNodePathInterval:: +set_start_scale(const LVecBase3f &scale) { + _start_scale = scale; + _flags |= F_start_scale; +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpNodePathInterval::set_start_scale +// Access: Published +// Description: Indicates the initial scale of the lerped node. +// This is meaningful only if set_end_scale() is also +// called. This parameter is optional; if unspecified, +// the value will be taken from the node's actual +// scale at the time the lerp is performed. +//////////////////////////////////////////////////////////////////// +INLINE void CLerpNodePathInterval:: +set_start_scale(float scale) { + set_start_scale(LVecBase3f(scale, scale, scale)); +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpNodePathInterval::set_end_scale +// Access: Published +// Description: Indicates that the scale of the node should be +// lerped, and specifies the final scale of the node. +// This should be called before initialize(). If this +// is not called, the node's scale will not be +// affected by the lerp. +//////////////////////////////////////////////////////////////////// +INLINE void CLerpNodePathInterval:: +set_end_scale(const LVecBase3f &scale) { + _end_scale = scale; + _flags |= F_end_scale; +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpNodePathInterval::set_end_scale +// Access: Published +// Description: Indicates that the scale of the node should be +// lerped, and specifies the final scale of the node. +// This should be called before initialize(). If this +// is not called, the node's scale will not be +// affected by the lerp. +//////////////////////////////////////////////////////////////////// +INLINE void CLerpNodePathInterval:: +set_end_scale(float scale) { + set_end_scale(LVecBase3f(scale, scale, scale)); +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpNodePathInterval::set_start_color +// Access: Published +// Description: Indicates the initial color of the lerped node. +// This is meaningful only if set_end_color() is also +// called. This parameter is optional; if unspecified, +// the value will be taken from the node's actual +// color at the time the lerp is performed. +//////////////////////////////////////////////////////////////////// +INLINE void CLerpNodePathInterval:: +set_start_color(const LVecBase4f &color) { + _start_color = color; + _flags |= F_start_color; +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpNodePathInterval::set_end_color +// Access: Published +// Description: Indicates that the color of the node should be +// lerped, and specifies the final color of the node. +// This should be called before initialize(). If this +// is not called, the node's color will not be +// affected by the lerp. +//////////////////////////////////////////////////////////////////// +INLINE void CLerpNodePathInterval:: +set_end_color(const LVecBase4f &color) { + _end_color = color; + _flags |= F_end_color; +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpNodePathInterval::set_start_color_scale +// Access: Published +// Description: Indicates the initial color scale of the lerped node. +// This is meaningful only if set_end_color_scale() is also +// called. This parameter is optional; if unspecified, +// the value will be taken from the node's actual +// color scale at the time the lerp is performed. +//////////////////////////////////////////////////////////////////// +INLINE void CLerpNodePathInterval:: +set_start_color_scale(const LVecBase4f &color_scale) { + _start_color_scale = color_scale; + _flags |= F_start_color_scale; +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpNodePathInterval::set_end_color_scale +// Access: Published +// Description: Indicates that the color scale of the node should be +// lerped, and specifies the final color scale of the node. +// This should be called before initialize(). If this +// is not called, the node's color scale will not be +// affected by the lerp. +//////////////////////////////////////////////////////////////////// +INLINE void CLerpNodePathInterval:: +set_end_color_scale(const LVecBase4f &color_scale) { + _end_color_scale = color_scale; + _flags |= F_end_color_scale; +} diff --git a/direct/src/interval/cLerpNodePathInterval.cxx b/direct/src/interval/cLerpNodePathInterval.cxx new file mode 100644 index 0000000000..85ffa19a2a --- /dev/null +++ b/direct/src/interval/cLerpNodePathInterval.cxx @@ -0,0 +1,351 @@ +// Filename: cLerpNodePathInterval.cxx +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "cLerpNodePathInterval.h" +#include "lerp_helpers.h" +#include "transformState.h" +#include "renderState.h" +#include "colorAttrib.h" +#include "colorScaleAttrib.h" +#include "dcast.h" + +TypeHandle CLerpNodePathInterval::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CLerpNodePathInterval::Constructor +// Access: Published +// Description: Constructs a lerp interval that will lerp some +// properties on the indicated node, possibly relative +// to the indicated other node (if other is nonempty). +// +// You must call set_end_pos(), etc. for the various +// properties you wish to lerp before the first call to +// initialize(). If you want to set a starting value +// for any of the properties, you may call +// set_start_pos(), etc.; otherwise, the starting value +// is taken from the actual node's value at the time the +// lerp is performed. +//////////////////////////////////////////////////////////////////// +CLerpNodePathInterval:: +CLerpNodePathInterval(const string &name, double duration, + CLerpInterval::BlendType blend_type, + const NodePath &node, const NodePath &other) : + CLerpInterval(name, duration, blend_type), + _node(node), + _other(other), + _flags(0) +{ + _prev_d = 0.0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpNodePathInterval::initialize +// Access: Published, Virtual +// Description: This replaces the first call to step(), and indicates +// that the interval has just begun. This may be +// overridden by derived classes that need to do some +// explicit initialization on the first call. +//////////////////////////////////////////////////////////////////// +void CLerpNodePathInterval:: +initialize(double t) { + recompute(); + _prev_d = 0.0; + step(t); +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpNodePathInterval::instant +// Access: Published, Virtual +// Description: This is called in lieu of initialize() .. step() +// .. finalize(), when everything is to happen within +// one frame. The interval should initialize itself, +// then leave itself in the final state. +//////////////////////////////////////////////////////////////////// +void CLerpNodePathInterval:: +instant() { + recompute(); + _prev_d = 0.0; + step(get_duration()); +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpNodePathInterval::step +// Access: Published, Virtual +// Description: Advances the time on the interval. The time may +// either increase (the normal case) or decrease +// (e.g. if the interval is being played by a slider). +//////////////////////////////////////////////////////////////////// +void CLerpNodePathInterval:: +step(double t) { + double d = compute_delta(t); + + if ((_flags & (F_end_pos | F_end_hpr | F_end_scale)) != 0) { + // We have some transform lerp. + CPT(TransformState) transform; + + if (_other.is_empty()) { + // If there is no other node, it's a local transform lerp. + transform = _node.get_transform(); + } else { + // If there *is* another node, we get the transform relative to + // that node. + transform = _node.get_transform(_other); + } + + LPoint3f pos; + LVecBase3f hpr; + LVecBase3f scale; + + if ((_flags & F_end_pos) != 0) { + if ((_flags & F_start_pos) != 0) { + lerp_value(pos, d, _start_pos, _end_pos); + + } else { + pos = transform->get_pos(); + lerp_value_from_prev(pos, d, _prev_d, pos, _end_pos); + } + } + if ((_flags & F_end_hpr) != 0) { + if ((_flags & F_start_hpr) != 0) { + lerp_value(hpr, d, _start_hpr, _end_hpr); + + } else { + hpr = transform->get_hpr(); + lerp_value_from_prev(hpr, d, _prev_d, hpr, _end_hpr); + } + } + if ((_flags & F_end_scale) != 0) { + if ((_flags & F_start_scale) != 0) { + lerp_value(scale, d, _start_scale, _end_scale); + + } else { + scale = transform->get_scale(); + lerp_value_from_prev(scale, d, _prev_d, scale, _end_scale); + } + } + + // Now apply the modifications back to the transform. We want to + // be a little careful here, because we don't want to assume the + // transform has hpr/scale components if they're not needed. And + // in any case, we only want to apply the components that we + // computed, above. + switch (_flags & (F_end_pos | F_end_hpr | F_end_scale)) { + case F_end_pos: + transform = transform->set_pos(pos); + break; + + case F_end_hpr: + transform = transform->set_hpr(hpr); + break; + + case F_end_scale: + transform = transform->set_scale(scale); + break; + + case F_end_hpr | F_end_scale: + transform = TransformState::make_pos_hpr_scale(transform->get_pos(), hpr, scale); + break; + + case F_end_pos | F_end_hpr: + transform = TransformState::make_pos_hpr_scale(pos, hpr, transform->get_scale()); + break; + + case F_end_pos | F_end_scale: + if (transform->quat_given()) { + transform = TransformState::make_pos_quat_scale(pos, transform->get_quat(), scale); + } else { + transform = TransformState::make_pos_hpr_scale(pos, transform->get_hpr(), scale); + } + break; + + case F_end_pos | F_end_hpr | F_end_scale: + transform = TransformState::make_pos_hpr_scale(pos, hpr, scale); + break; + + default: + interval_cat.error() + << "Internal error in CLerpNodePathInterval::step().\n"; + } + + // Now apply the new transform back to the node. + if (_other.is_empty()) { + _node.set_transform(transform); + } else { + _node.set_transform(_other, transform); + } + } + + if ((_flags & (F_end_color | F_end_color_scale)) != 0) { + // We have some render state lerp. + CPT(RenderState) state; + + if (_other.is_empty()) { + // If there is no other node, it's a local state lerp. This is + // most common. + state = _node.get_state(); + } else { + // If there *is* another node, we get the state relative to that + // node. This is weird, but you could lerp color (for instance) + // relative to some other node's color. + state = _node.get_state(_other); + } + + // Unlike in the transform case above, we can go ahead and modify + // the state immediately with each attribute change, since these + // attributes don't interrelate. + + if ((_flags & F_end_color) != 0) { + Colorf color; + + if ((_flags & F_start_color) != 0) { + lerp_value(color, d, _start_color, _end_color); + + } else { + // Get the previous color. + color.set(1.0f, 1.0f, 1.0f, 1.0f); + const RenderAttrib *attrib = + state->get_attrib(ColorAttrib::get_class_type()); + if (attrib != (const RenderAttrib *)NULL) { + const ColorAttrib *ca = DCAST(ColorAttrib, attrib); + if (ca->get_color_type() == ColorAttrib::T_flat) { + color = ca->get_color(); + } + } + + lerp_value_from_prev(color, d, _prev_d, color, _end_color); + } + + state = state->add_attrib(ColorAttrib::make_flat(color)); + } + + if ((_flags & F_end_color_scale) != 0) { + LVecBase4f color_scale; + + if ((_flags & F_start_color_scale) != 0) { + lerp_value(color_scale, d, _start_color_scale, _end_color_scale); + + } else { + // Get the previous color scale. + color_scale.set(1.0f, 1.0f, 1.0f, 1.0f); + const RenderAttrib *attrib = + state->get_attrib(ColorScaleAttrib::get_class_type()); + if (attrib != (const RenderAttrib *)NULL) { + const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attrib); + color_scale = csa->get_scale(); + } + + lerp_value_from_prev(color_scale, d, _prev_d, color_scale, _end_color_scale); + } + + state = state->add_attrib(ColorScaleAttrib::make(color_scale)); + } + + // Now apply the new state back to the node. + if (_other.is_empty()) { + _node.set_state(state); + } else { + _node.set_state(_other, state); + } + } + + _prev_d = d; + _curr_t = t; +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpNodePathInterval::reverse_initialize +// Access: Published, Virtual +// Description: Similar to initialize(), but this is called when the +// interval is being played backwards; it indicates that +// the interval should start at the finishing state and +// undo any intervening intervals. +//////////////////////////////////////////////////////////////////// +void CLerpNodePathInterval:: +reverse_initialize(double t) { + recompute(); + _prev_d = 1.0; + step(t); +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpNodePathInterval::reverse_instant +// Access: Published, Virtual +// Description: This is called in lieu of reverse_initialize() +// .. step() .. reverse_finalize(), when everything is +// to happen within one frame. The interval should +// initialize itself, then leave itself in the initial +// state. +//////////////////////////////////////////////////////////////////// +void CLerpNodePathInterval:: +reverse_instant() { + recompute(); + _prev_d = 1.0; + step(0.0); +} + +//////////////////////////////////////////////////////////////////// +// Function: CLerpNodePathInterval::output +// Access: Published, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CLerpNodePathInterval:: +output(ostream &out) const { + out << get_name() << ":"; + + if ((_flags & F_end_pos) != 0) { + out << " pos"; + if ((_flags & F_start_pos) != 0) { + out << " from " << _start_pos; + } + out << " to " << _end_pos; + } + + if ((_flags & F_end_hpr) != 0) { + out << " hpr"; + if ((_flags & F_start_hpr) != 0) { + out << " from " << _start_hpr; + } + out << " to " << _end_hpr; + } + + if ((_flags & F_end_scale) != 0) { + out << " scale"; + if ((_flags & F_start_scale) != 0) { + out << " from " << _start_scale; + } + out << " to " << _end_scale; + } + + if ((_flags & F_end_color) != 0) { + out << " color"; + if ((_flags & F_start_color) != 0) { + out << " from " << _start_color; + } + out << " to " << _end_color; + } + + if ((_flags & F_end_color_scale) != 0) { + out << " color_scale"; + if ((_flags & F_start_color_scale) != 0) { + out << " from " << _start_color_scale; + } + out << " to " << _end_color_scale; + } + + out << " dur " << get_duration(); +} diff --git a/direct/src/interval/cLerpNodePathInterval.h b/direct/src/interval/cLerpNodePathInterval.h new file mode 100644 index 0000000000..adaba87ff5 --- /dev/null +++ b/direct/src/interval/cLerpNodePathInterval.h @@ -0,0 +1,109 @@ +// Filename: cLerpNodePathInterval.h +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef CLERPNODEPATHINTERVAL_H +#define CLERPNODEPATHINTERVAL_H + +#include "directbase.h" +#include "cLerpInterval.h" +#include "nodePath.h" + +//////////////////////////////////////////////////////////////////// +// Class : CLerpNodePathInterval +// Description : An interval that lerps one or more properties (like +// pos, hpr, etc.) on a NodePath over time. +//////////////////////////////////////////////////////////////////// +class EXPCL_DIRECT CLerpNodePathInterval : public CLerpInterval { +PUBLISHED: + CLerpNodePathInterval(const string &name, double duration, + BlendType blend_type, + const NodePath &node, const NodePath &other); + + INLINE const NodePath &get_node() const; + INLINE const NodePath &get_other() const; + + INLINE void set_start_pos(const LVecBase3f &pos); + INLINE void set_end_pos(const LVecBase3f &pos); + INLINE void set_start_hpr(const LVecBase3f &hpr); + INLINE void set_end_hpr(const LVecBase3f &hpr); + INLINE void set_start_scale(const LVecBase3f &scale); + INLINE void set_start_scale(float scale); + INLINE void set_end_scale(const LVecBase3f &scale); + INLINE void set_end_scale(float scale); + INLINE void set_start_color(const LVecBase4f &color); + INLINE void set_end_color(const LVecBase4f &color); + INLINE void set_start_color_scale(const LVecBase4f &color_scale); + INLINE void set_end_color_scale(const LVecBase4f &color_scale); + + virtual void initialize(double t); + virtual void instant(); + virtual void step(double t); + virtual void reverse_initialize(double t); + virtual void reverse_instant(); + + virtual void output(ostream &out) const; + +private: + NodePath _node; + NodePath _other; + + enum Flags { + F_end_pos = 0x0001, + F_end_hpr = 0x0002, + F_end_scale = 0x0004, + F_end_color = 0x0008, + F_end_color_scale = 0x0010, + + F_start_pos = 0x0100, + F_start_hpr = 0x0200, + F_start_scale = 0x0400, + F_start_color = 0x0800, + F_start_color_scale = 0x1000, + }; + + int _flags; + LPoint3f _start_pos, _end_pos; + LVecBase3f _start_hpr, _end_hpr; + LVecBase3f _start_scale, _end_scale; + Colorf _start_color, _end_color; + LVecBase4f _start_color_scale, _end_color_scale; + + double _prev_d; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CLerpInterval::init_type(); + register_type(_type_handle, "CLerpNodePathInterval", + CLerpInterval::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "cLerpNodePathInterval.I" + +#endif + diff --git a/direct/src/interval/cMetaInterval.I b/direct/src/interval/cMetaInterval.I new file mode 100644 index 0000000000..ae44157afa --- /dev/null +++ b/direct/src/interval/cMetaInterval.I @@ -0,0 +1,239 @@ +// Filename: cMetaInterval.I +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::set_precision +// Access: Published +// Description: Indicates the precision with which time measurements +// are compared. For numerical accuracy, all +// floating-point time values are converted to integer +// values internally by scaling by the precision factor. +// The larger the number given here, the smaller the +// delta of time that can be differentiated; the +// limit is the maximum integer that can be represented +// in the system. +//////////////////////////////////////////////////////////////////// +INLINE void CMetaInterval:: +set_precision(double precision) { + _precision = precision; + mark_dirty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::get_precision +// Access: Published +// Description: Returns the precision with which time measurements +// are compared. See set_precision(). +//////////////////////////////////////////////////////////////////// +INLINE double CMetaInterval:: +get_precision() const { + return _precision; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::get_num_defs +// Access: Published +// Description: Returns the number of interval and push/pop +// definitions that have been added to the meta +// interval. +//////////////////////////////////////////////////////////////////// +INLINE int CMetaInterval:: +get_num_defs() const { + return (int)_defs.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::get_def_type +// Access: Published +// Description: Returns the type of the nth interval definition that +// has been added. +//////////////////////////////////////////////////////////////////// +INLINE CMetaInterval::DefType CMetaInterval:: +get_def_type(int n) const { + nassertr(n >= 0 && n < (int)_defs.size(), DT_c_interval); + return _defs[n]._type; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::get_c_interval +// Access: Published +// Description: Return the CInterval pointer associated with the nth +// interval definition. It is only valid to call this +// if get_def_type(n) returns DT_c_interval. +//////////////////////////////////////////////////////////////////// +INLINE CInterval *CMetaInterval:: +get_c_interval(int n) const { + nassertr(n >= 0 && n < (int)_defs.size(), NULL); + nassertr(_defs[n]._type == DT_c_interval, NULL); + return _defs[n]._c_interval; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::get_ext_index +// Access: Published +// Description: Return the external interval index number associated +// with the nth interval definition. It is only valid +// to call this if get_def_type(n) returns DT_ext_index. +//////////////////////////////////////////////////////////////////// +INLINE int CMetaInterval:: +get_ext_index(int n) const { + nassertr(n >= 0 && n < (int)_defs.size(), NULL); + nassertr(_defs[n]._type == DT_ext_index, NULL); + return _defs[n]._ext_index; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::is_event_ready +// Access: Published +// Description: Returns true if a recent call to initialize(), +// step(), or finalize() has left some external +// intervals ready to play. If this returns true, call +// get_event_index(), get_event_t(), and pop_event() to +// retrieve the relevant information. +//////////////////////////////////////////////////////////////////// +INLINE bool CMetaInterval:: +is_event_ready() { + return service_event_queue(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::get_event_index +// Access: Published +// Description: If a previous call to is_event_ready() returned +// true, this returns the index number (added via +// add_event_index()) of the external interval that needs +// to be played. +//////////////////////////////////////////////////////////////////// +INLINE int CMetaInterval:: +get_event_index() const { + nassertr(!_event_queue.empty(), -1); + const EventQueueEntry &entry = _event_queue.front(); + const IntervalDef &def = _defs[entry._n]; + nassertr(def._type == DT_ext_index, -1); + return def._ext_index; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::get_event_t +// Access: Published +// Description: If a previous call to is_event_ready() returned +// true, this returns the t value that should be fed to +// the given interval. +//////////////////////////////////////////////////////////////////// +INLINE double CMetaInterval:: +get_event_t() const { + nassertr(!_event_queue.empty(), 0.0f); + return int_to_double_time(_event_queue.front()._time); +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::get_event_type +// Access: Published +// Description: If a previous call to is_event_ready() returned +// true, this returns the type of the event (initialize, +// step, finalize, etc.) for the given interval. +//////////////////////////////////////////////////////////////////// +INLINE CInterval::EventType CMetaInterval:: +get_event_type() const { + nassertr(!_event_queue.empty(), ET_step); + return _event_queue.front()._event_type; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::pop_event +// Access: Published +// Description: Acknowledges that the external interval on the top of +// the queue has been extracted, and is about to be +// serviced by the scripting language. This prepares +// the interval so the next call to is_event_ready() +// will return information about the next external +// interval on the queue, if any. +//////////////////////////////////////////////////////////////////// +INLINE void CMetaInterval:: +pop_event() { +#ifndef NDEBUG + nassertv(!_event_queue.empty()); + const EventQueueEntry &entry = _event_queue.front(); + const IntervalDef &def = _defs[entry._n]; + nassertv(def._type == DT_ext_index); +#endif + _event_queue.pop_front(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::double_to_int_time +// Access: Private +// Description: Converts from an external double time value or offset +// in seconds to an internal integer value or offset. +//////////////////////////////////////////////////////////////////// +INLINE int CMetaInterval:: +double_to_int_time(double t) const { + // Use floor() just in case there are negative values involved. + return (int)floor(t * _precision + 0.5); +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::int_to_double_time +// Access: Private +// Description: Converts from an internal integer time value or +// offset to an external double time value or offset in +// seconds. +//////////////////////////////////////////////////////////////////// +INLINE double CMetaInterval:: +int_to_double_time(int time) const { + return (double)time / _precision; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::PlaybackEvent::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CMetaInterval::PlaybackEvent:: +PlaybackEvent(int time, int n, + CMetaInterval::PlaybackEventType type) : + _time(time), + _n(n), + _type(type) +{ + _begin_event = this; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::PlaybackEvent::Ordering operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool CMetaInterval::PlaybackEvent:: +operator < (const CMetaInterval::PlaybackEvent &other) const { + return _time < other._time; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::EventQueueEntry::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE CMetaInterval::EventQueueEntry:: +EventQueueEntry(int n, CInterval::EventType event_type, int time) : + _n(n), + _event_type(event_type), + _time(time) +{ +} diff --git a/direct/src/interval/cMetaInterval.cxx b/direct/src/interval/cMetaInterval.cxx new file mode 100644 index 0000000000..a3f8d8d847 --- /dev/null +++ b/direct/src/interval/cMetaInterval.cxx @@ -0,0 +1,877 @@ +// Filename: cMetaInterval.cxx +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "cMetaInterval.h" +#include "config_interval.h" +#include "indirectLess.h" +#include +#include // for log10() +#include // for sprintf() + +TypeHandle CMetaInterval::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +CMetaInterval:: +CMetaInterval(const string &name) : + CInterval(name, 0.0, true) +{ + _precision = interval_precision; + _current_nesting_level = 0; + _next_event_index = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::Destructor +// Access: Published, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CMetaInterval:: +~CMetaInterval() { + clear_intervals(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::clear_intervals +// Access: Published +// Description: Resets the list of intervals and prepares for +// receiving a new list. +//////////////////////////////////////////////////////////////////// +void CMetaInterval:: +clear_intervals() { + // Better not do this unless you have serviced all of the + // outstanding events! + nassertv(_event_queue.empty()); + + clear_events(); + + // Go through all of our nested intervals and remove ourselves as + // their parent. + Defs::iterator di; + for (di = _defs.begin(); di != _defs.end(); ++di) { + IntervalDef &def = (*di); + if (def._c_interval != (CInterval *)NULL) { + CInterval::Parents::iterator pi = + find(def._c_interval->_parents.begin(), + def._c_interval->_parents.end(), + this); + nassertv(pi != def._c_interval->_parents.end()); + def._c_interval->_parents.erase(pi); + } + } + _defs.clear(); + + _current_nesting_level = 0; + _next_event_index = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::push_level +// Access: Published +// Description: Marks the beginning of a nested level of child +// intervals. Within the nested level, a RelativeStart +// time of RS_level_begin refers to the start of the +// level, and the first interval added within the level +// is always relative to the start of the level. +// +// The return value is the index of the def entry +// created by this push. +//////////////////////////////////////////////////////////////////// +int CMetaInterval:: +push_level(double rel_time, RelativeStart rel_to) { + _defs.push_back(IntervalDef()); + IntervalDef &def = _defs.back(); + def._type = DT_push_level; + def._rel_time = rel_time; + def._rel_to = rel_to; + _current_nesting_level++; + mark_dirty(); + + return (int)_defs.size() - 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::add_c_interval +// Access: Published +// Description: Adds a new CInterval to the list. The interval will +// be played when the indicated time (relative to the +// given point) has been reached. +// +// The return value is the index of the def entry +// representing the new interval. +//////////////////////////////////////////////////////////////////// +int CMetaInterval:: +add_c_interval(CInterval *c_interval, + double rel_time, RelativeStart rel_to) { + nassertr(c_interval != (CInterval *)NULL, -1); + + c_interval->_parents.push_back(this); + _defs.push_back(IntervalDef()); + IntervalDef &def = _defs.back(); + def._type = DT_c_interval; + def._c_interval = c_interval; + def._rel_time = rel_time; + def._rel_to = rel_to; + mark_dirty(); + + return (int)_defs.size() - 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::add_ext_index +// Access: Published +// Description: Adds a new external interval to the list. This +// represents some object in the external scripting +// language that has properties similar to a CInterval +// (for instance, a Python Interval object). +// +// The CMetaInterval object cannot play this external +// interval directly, but it records a placeholder for +// it and will ask the scripting language to play it +// when it is time, via is_event_ready() and related +// methods. +// +// The ext_index number itself is simply a handle that +// the scripting language makes up and associates with +// its interval object somehow. The CMetaInterval +// object does not attempt to interpret this value. +// +// The return value is the index of the def entry +// representing the new interval. +//////////////////////////////////////////////////////////////////// +int CMetaInterval:: +add_ext_index(int ext_index, const string &name, double duration, + bool open_ended, + double rel_time, RelativeStart rel_to) { + _defs.push_back(IntervalDef()); + IntervalDef &def = _defs.back(); + def._type = DT_ext_index; + def._ext_index = ext_index; + def._ext_name = name; + def._ext_duration = duration; + def._ext_open_ended = open_ended; + def._rel_time = rel_time; + def._rel_to = rel_to; + mark_dirty(); + + return (int)_defs.size() - 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::pop_level +// Access: Published +// Description: Finishes a level marked by a previous call to +// push_level(), and returns to the previous level. +//////////////////////////////////////////////////////////////////// +int CMetaInterval:: +pop_level() { + nassertr(_current_nesting_level > 0, -1); + + _defs.push_back(IntervalDef()); + IntervalDef &def = _defs.back(); + def._type = DT_pop_level; + _current_nesting_level--; + mark_dirty(); + + return (int)_defs.size() - 1; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::initialize +// Access: Published, Virtual +// Description: This replaces the first call to step(), and indicates +// that the interval has just begun. This may be +// overridden by derived classes that need to do some +// explicit initialization on the first call. +//////////////////////////////////////////////////////////////////// +void CMetaInterval:: +initialize(double t) { + // It may be tempting to flush the event_queue here, but don't do + // it. Those are events that must still be serviced from some + // previous interval operation. Throwing them away would be a + // mistake. + + recompute(); + _next_event_index = 0; + _active.clear(); + + int now = double_to_int_time(t); + + // Now look for events from the beginning up to the current time. + ActiveEvents new_active; + while (_next_event_index < _events.size() && + _events[_next_event_index]->_time <= now) { + PlaybackEvent *event = _events[_next_event_index]; + + // Do the indicated event. + do_event_forward(event, new_active, true); + _next_event_index++; + } + finish_events_forward(now, new_active); + + _curr_t = t; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::instant +// Access: Published, Virtual +// Description: This is called in lieu of initialize() .. step() +// .. finalize(), when everything is to happen within +// one frame. The interval should initialize itself, +// then leave itself in the final state. +//////////////////////////////////////////////////////////////////// +void CMetaInterval:: +instant() { + recompute(); + _active.clear(); + + // Apply all of the events. This just means we invoke "instant" for + // any end or instant event, ignoring the begin events. + PlaybackEvents::iterator ei; + for (ei = _events.begin(); ei != _events.end(); ++ei) { + PlaybackEvent *event = (*ei); + if (event->_type != PET_begin) { + enqueue_event(event->_n, ET_instant, true, 0); + } + } + + _next_event_index = _events.size(); + _curr_t = get_duration(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::step +// Access: Published, Virtual +// Description: Advances the time on the interval. The time may +// either increase (the normal case) or decrease +// (e.g. if the interval is being played by a slider). +//////////////////////////////////////////////////////////////////// +void CMetaInterval:: +step(double t) { + int now = double_to_int_time(t); + + // Now look for events between the last time we ran and the current + // time. + + if (_next_event_index < _events.size() && + _events[_next_event_index]->_time <= now) { + // The normal case: time is increasing. + ActiveEvents new_active; + while (_next_event_index < _events.size() && + _events[_next_event_index]->_time <= now) { + PlaybackEvent *event = _events[_next_event_index]; + + // Do the indicated event. + do_event_forward(event, new_active, false); + _next_event_index++; + } + + finish_events_forward(now, new_active); + + } else { + // A less usual case: time is decreasing. + ActiveEvents new_active; + while (_next_event_index > 0 && + _events[_next_event_index - 1]->_time > now) { + _next_event_index--; + PlaybackEvent *event = _events[_next_event_index]; + do_event_reverse(event, new_active, false); + } + + finish_events_reverse(now, new_active); + } + + _curr_t = t; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::finalize +// Access: Published, Virtual +// Description: This is called when an interval is interrupted. It +// should advance the time as if step() were called, and +// also perform whatever cleanup might be required. +//////////////////////////////////////////////////////////////////// +void CMetaInterval:: +finalize() { + // Do all remaining events. + ActiveEvents new_active; + while (_next_event_index < _events.size()) { + PlaybackEvent *event = _events[_next_event_index]; + // Do the indicated event. + do_event_forward(event, new_active, false); + _next_event_index++; + } + + _curr_t = get_duration(); + finish_events_forward(double_to_int_time(_curr_t), new_active); +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::reverse_initialize +// Access: Published, Virtual +// Description: Similar to initialize(), but this is called when the +// interval is being played backwards; it indicates that +// the interval should start at the finishing state and +// undo any intervening intervals. +//////////////////////////////////////////////////////////////////// +void CMetaInterval:: +reverse_initialize(double t) { + // It may be tempting to flush the event_queue here, but don't do + // it. Those are events that must still be serviced from some + // previous interval operation. Throwing them away would be a + // mistake. + + recompute(); + _next_event_index = _events.size(); + _active.clear(); + + int now = double_to_int_time(t); + + // Now look for events from the end down to the current time. + ActiveEvents new_active; + while (_next_event_index > 0 && + _events[_next_event_index - 1]->_time > now) { + _next_event_index--; + PlaybackEvent *event = _events[_next_event_index]; + + // Do the indicated event. + do_event_reverse(event, new_active, true); + } + finish_events_reverse(now, new_active); + + _curr_t = t; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::reverse_instant +// Access: Published, Virtual +// Description: This is called in lieu of reverse_initialize() +// .. step() .. reverse_finalize(), when everything is +// to happen within one frame. The interval should +// initialize itself, then leave itself in the initial +// state. +//////////////////////////////////////////////////////////////////// +void CMetaInterval:: +reverse_instant() { + recompute(); + _active.clear(); + + // Apply all of the events. This just means we invoke "instant" for + // any end or instant event, ignoring the begin events. + PlaybackEvents::reverse_iterator ei; + for (ei = _events.rbegin(); ei != _events.rend(); ++ei) { + PlaybackEvent *event = (*ei); + if (event->_type != PET_begin) { + enqueue_event(event->_n, ET_reverse_instant, true, 0); + } + } + + _next_event_index = 0; + _curr_t = 0.0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::reverse_finalize +// Access: Published, Virtual +// Description: Called generally following a reverse_initialize(), +// this indicates the interval should set itself to the +// initial state. +//////////////////////////////////////////////////////////////////// +void CMetaInterval:: +reverse_finalize() { + // Do all remaining events at the beginning. + ActiveEvents new_active; + + while (_next_event_index > 0) { + _next_event_index--; + PlaybackEvent *event = _events[_next_event_index]; + do_event_reverse(event, new_active, false); + } + + finish_events_reverse(0, new_active); + _curr_t = 0.0; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::write +// Access: Published, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void CMetaInterval:: +write(ostream &out, int indent_level) const { + recompute(); + + // How many digits of precision should we output for time? + int num_decimals = (int)ceil(log10(_precision)); + int total_digits = num_decimals + 4; + static const int max_digits = 32; // totally arbitrary + nassertv(total_digits <= max_digits); + char format_str[12]; + sprintf(format_str, "%%%d.%df", total_digits, num_decimals); + + indent(out, indent_level) << get_name() << ":\n"; + + int extra_indent_level = 1; + Defs::const_iterator di; + for (di = _defs.begin(); di != _defs.end(); ++di) { + const IntervalDef &def = (*di); + char time_str[max_digits + 1]; + sprintf(time_str, format_str, int_to_double_time(def._actual_begin_time)); + indent(out, indent_level) << time_str; + + switch (def._type) { + case DT_c_interval: + indent(out, extra_indent_level) + << *def._c_interval; + if (!def._c_interval->get_open_ended()) { + out << " (!oe)"; + } + out << "\n"; + break; + + case DT_ext_index: + indent(out, extra_indent_level) + << "*" << def._ext_name; + if (def._ext_duration != 0.0) { + out << " dur " << def._ext_duration; + } + if (!def._ext_open_ended) { + out << " (!oe)"; + } + out<< "\n"; + break; + + case DT_push_level: + indent(out, extra_indent_level) + << "{\n"; + extra_indent_level += 2; + break; + + case DT_pop_level: + extra_indent_level -= 2; + indent(out, extra_indent_level) + << "}\n"; + break; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::do_recompute +// Access: Protected, Virtual +// Description: Recomputes all of the events (and the duration) +// according to the set of interval defs. +//////////////////////////////////////////////////////////////////// +void CMetaInterval:: +do_recompute() { + _dirty = false; + clear_events(); + + int n = recompute_level(0, 0, _end_time); + + if (n != (int)_defs.size()) { + interval_cat.warning() + << "CMetaInterval pushes don't match pops.\n"; + } + + // We do a stable_sort() to guarantee ordering of events that have + // the same start time. These must be invoked in the order in which + // they appear. + stable_sort(_events.begin(), _events.end(), IndirectLess()); + _duration = int_to_double_time(_end_time); +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::clear_events +// Access: Private +// Description: Removes all entries from the _events list. +//////////////////////////////////////////////////////////////////// +void CMetaInterval:: +clear_events() { + PlaybackEvents::iterator ei; + for (ei = _events.begin(); ei != _events.end(); ++ei) { + PlaybackEvent *event = (*ei); + delete event; + } + _events.clear(); + _active.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::do_event_forward +// Access: Private +// Description: Process a single event in the interval, moving +// forwards in time. If the event represents a new +// begin, adds it to the new_active list; if it is an +// end, finalizes it. +// +// If is_initial is true, it is as if we are in +// initialize: instant events will be invoked only if +// they are marked open_ended. +//////////////////////////////////////////////////////////////////// +void CMetaInterval:: +do_event_forward(CMetaInterval::PlaybackEvent *event, + CMetaInterval::ActiveEvents &new_active, bool is_initial) { + switch (event->_type) { + case PET_begin: + { + nassertv(event->_begin_event == event); + nassertv(_active.find(event) == _active.end()); + bool okflag = new_active.insert(event).second; + nassertv(okflag); + } + break; + + case PET_end: + { + // Erase the event from either the new active or the current + // active lists. + int erase_count = new_active.erase(event->_begin_event); + if (erase_count != 0) { + // This interval was new this frame; we must invoke it as + // an instant event. + enqueue_event(event->_n, ET_instant, is_initial); + } else { + erase_count = _active.erase(event->_begin_event); + enqueue_event(event->_n, ET_finalize, is_initial); + } + nassertv(erase_count == 1); + } + break; + + case PET_instant: + nassertv(event->_begin_event == event); + nassertv(new_active.find(event) == new_active.end()); + nassertv(_active.find(event) == _active.end()); + enqueue_event(event->_n, ET_instant, is_initial); + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::finish_events_forward +// Access: Private +// Description: After walking through the event list and adding a +// bunch of new events to new_active, finished up by +// calling step() on all of the events still in _active +// and initialize() on all the events in new_active, +// then copying the events from new_active to active. +//////////////////////////////////////////////////////////////////// +void CMetaInterval:: +finish_events_forward(int now, CMetaInterval::ActiveEvents &new_active) { + // Do whatever's still active. + ActiveEvents::iterator ai; + for (ai = _active.begin(); ai != _active.end(); ++ai) { + PlaybackEvent *event = (*ai); + enqueue_event(event->_n, ET_step, false, now - event->_time); + } + + // Initialize whatever new intervals we came across. + for (ai = new_active.begin(); ai != new_active.end(); ++ai) { + PlaybackEvent *event = (*ai); + enqueue_event(event->_n, ET_initialize, false, now - event->_time); + bool inserted = _active.insert(event).second; + nassertv(inserted); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::do_event_reverse +// Access: Private +// Description: Process a single event in the interval, moving +// backwards in time. This undoes the indicated event. +// If the event represents a new begin, adds it to the +// new_active list; if it is an end, finalizes it. +// +// If is_initial is true, it is as if we are in +// reverse_initialize: instant events will be invoked +// only if they are marked open_ended. +//////////////////////////////////////////////////////////////////// +void CMetaInterval:: +do_event_reverse(CMetaInterval::PlaybackEvent *event, + CMetaInterval::ActiveEvents &new_active, bool is_initial) { + // Undo the indicated event. + switch (event->_type) { + case PET_begin: + { + nassertv(event->_begin_event == event); + // Erase the event from either the new active or the current + // active lists. + int erase_count = new_active.erase(event); + if (erase_count != 0) { + // This interval was new this frame; we invoke it as an + // instant event. + enqueue_event(event->_n, ET_reverse_instant, is_initial); + } else { + erase_count = _active.erase(event->_begin_event); + enqueue_event(event->_n, ET_reverse_finalize, is_initial); + } + nassertv(erase_count == 1); + } + break; + + case PET_end: + { + nassertv(new_active.find(event->_begin_event) == new_active.end()); + bool okflag = new_active.insert(event->_begin_event).second; + nassertv(okflag); + } + break; + + case PET_instant: + nassertv(event->_begin_event == event); + nassertv(_active.find(event) == _active.end()); + nassertv(new_active.find(event) == new_active.end()); + enqueue_event(event->_n, ET_reverse_instant, is_initial); + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::finish_events_reverse +// Access: Private +// Description: After walking through the event list and adding a +// bunch of new events to new_active, finishes up by +// calling step() on all of the events still in _active +// and reverse_initialize() on all the events in +// new_active, then copying the events from new_active +// to active. +//////////////////////////////////////////////////////////////////// +void CMetaInterval:: +finish_events_reverse(int now, CMetaInterval::ActiveEvents &new_active) { + // Do whatever's still active. + ActiveEvents::iterator ai; + for (ai = _active.begin(); ai != _active.end(); ++ai) { + PlaybackEvent *event = (*ai); + enqueue_event(event->_n, ET_step, false, now - event->_time); + } + + // Initialize whatever new intervals we came across. + for (ai = new_active.begin(); ai != new_active.end(); ++ai) { + PlaybackEvent *event = (*ai); + enqueue_event(event->_n, ET_reverse_initialize, false, now - event->_time); + bool inserted = _active.insert(event).second; + nassertv(inserted); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::enqueue_event +// Access: Private +// Description: Enqueues the indicated interval for invocation after +// we have finished scanning for events that need +// processing this frame. +// +// is_initial is only relevant for event types +// ET_instant or ET_reverse_instant, and indicates +// whether we are in the initialize() (or +// reverse_initialize()) call, and should therefore only +// invoke open-ended intervals. +// +// time is only relevant for ET_initialize, +// ET_reverse_initialize, and ET_step. +//////////////////////////////////////////////////////////////////// +void CMetaInterval:: +enqueue_event(int n, CInterval::EventType event_type, bool is_initial, int time) { + nassertv(n >= 0 && n < (int)_defs.size()); + const IntervalDef &def = _defs[n]; + switch (def._type) { + case DT_c_interval: + if (is_initial && + (event_type == ET_instant || event_type == ET_reverse_instant) && + !def._c_interval->get_open_ended()) { + // Ignore a non-open-ended interval that we skipped completely + // past on initialize(). + return; + } else { + if (_event_queue.empty()) { + // if the event queue is empty, we can process this C++ + // interval immediately. We only need to defer it if there + // are external (e.g. Python) intervals in the queue that need + // to be processed first. + def._c_interval->set_t(int_to_double_time(time), event_type); + return; + } + } + break; + + case DT_ext_index: + if (is_initial && + (event_type == ET_instant || event_type == ET_reverse_instant) && + !def._ext_open_ended) { + // Ignore a non-open-ended interval that we skipped completely + // past on initialize(). + return; + } + break; + + default: + nassertv(false); + return; + } + + _event_queue.push_back(EventQueueEntry(n, event_type, time)); +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::service_event_queue +// Access: Private +// Description: Invokes whatever C++ intervals might be at the head +// of the queue, and prepares for passing an external +// interval to the scripting language. +// +// The return value is true if there remains at least +// one external event to be serviced, false if all +// events are handled. +//////////////////////////////////////////////////////////////////// +bool CMetaInterval:: +service_event_queue() { + while (!_event_queue.empty()) { + const EventQueueEntry &entry = _event_queue.front(); + nassertr(entry._n >= 0 && entry._n < (int)_defs.size(), false); + const IntervalDef &def = _defs[entry._n]; + switch (def._type) { + case DT_c_interval: + // Handle the C++ event. + def._c_interval->set_t(int_to_double_time(entry._time), entry._event_type); + break; + + case DT_ext_index: + // Here's an external event; leave it there and return. + return true; + + default: + nassertr(false, false); + return false; + } + _event_queue.pop_front(); + } + + // No more events on the queue. + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::recompute_level +// Access: Private +// Description: Recursively recomputes a complete level (delimited by +// push/pop definitions). +// +// The value n on entry refers to the first entry after +// the push; the return value will reference the +// matching pop, or an index greater than the last +// element in the array if there was no matching pop. +// +// The level_begin value indicates the begin time of +// this level. On return, level_end is filled with the +// end time of this level. +//////////////////////////////////////////////////////////////////// +int CMetaInterval:: +recompute_level(int n, int level_begin, int &level_end) { + level_end = level_begin; + int previous_begin = level_begin; + int previous_end = level_begin; + + while (n < (int)_defs.size() && _defs[n]._type != DT_pop_level) { + IntervalDef &def = _defs[n]; + int begin_time, end_time; + switch (def._type) { + case DT_c_interval: + begin_time = get_begin_time(def, level_begin, previous_begin, previous_end); + def._actual_begin_time = begin_time; + end_time = begin_time + double_to_int_time(def._c_interval->get_duration()); + if (begin_time == end_time) { + _events.push_back(new PlaybackEvent(begin_time, n, PET_instant)); + } else { + PlaybackEvent *begin = new PlaybackEvent(begin_time, n, PET_begin); + PlaybackEvent *end = new PlaybackEvent(end_time, n, PET_end); + end->_begin_event = begin; + _events.push_back(begin); + _events.push_back(end); + } + break; + + case DT_ext_index: + begin_time = get_begin_time(def, level_begin, previous_begin, previous_end); + def._actual_begin_time = begin_time; + end_time = begin_time + double_to_int_time(def._ext_duration); + if (begin_time == end_time) { + _events.push_back(new PlaybackEvent(begin_time, n, PET_instant)); + } else { + PlaybackEvent *begin = new PlaybackEvent(begin_time, n, PET_begin); + PlaybackEvent *end = new PlaybackEvent(end_time, n, PET_end); + end->_begin_event = begin; + _events.push_back(begin); + _events.push_back(end); + } + break; + + case DT_push_level: + begin_time = get_begin_time(def, level_begin, previous_begin, previous_end); + def._actual_begin_time = begin_time; + n = recompute_level(n + 1, begin_time, end_time); + break; + + case DT_pop_level: + nassertr(false, _defs.size()); + break; + } + + previous_begin = begin_time; + previous_end = end_time; + level_end = max(level_end, end_time); + n++; + } + + if (n < (int)_defs.size()) { + // The final pop "begins" at the level end time, just for clarity + // on output. + IntervalDef &def = _defs[n]; + def._actual_begin_time = level_end; + } + + return n; +} + +//////////////////////////////////////////////////////////////////// +// Function: CMetaInterval::get_begin_time +// Access: Private +// Description: Returns the integer begin time indicated by the given +// IntervalDef, given the indicated level begin, +// previous begin, and previous end times. +//////////////////////////////////////////////////////////////////// +int CMetaInterval:: +get_begin_time(const CMetaInterval::IntervalDef &def, int level_begin, + int previous_begin, int previous_end) { + switch (def._rel_to) { + case RS_previous_end: + return previous_end + double_to_int_time(def._rel_time); + + case RS_previous_begin: + return previous_begin + double_to_int_time(def._rel_time); + + case RS_level_begin: + return level_begin + double_to_int_time(def._rel_time); + } + + nassertr(false, previous_end); + return previous_end; +} diff --git a/direct/src/interval/cMetaInterval.h b/direct/src/interval/cMetaInterval.h new file mode 100644 index 0000000000..70daaf78e2 --- /dev/null +++ b/direct/src/interval/cMetaInterval.h @@ -0,0 +1,190 @@ +// Filename: cMetaInterval.h +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef CMETAINTERVAL_H +#define CMETAINTERVAL_H + +#include "directbase.h" +#include "cInterval.h" + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Class : CMetaInterval +// Description : This interval contains a list of nested intervals, +// each of which has its own begin and end times. Some +// of them may overlap and some of them may not. +//////////////////////////////////////////////////////////////////// +class EXPCL_DIRECT CMetaInterval : public CInterval { +PUBLISHED: + CMetaInterval(const string &name); + virtual ~CMetaInterval(); + + enum RelativeStart { + RS_previous_end, + RS_previous_begin, + RS_level_begin, + }; + + INLINE void set_precision(double precision); + INLINE double get_precision() const; + + void clear_intervals(); + int push_level(double rel_time, RelativeStart rel_to); + int add_c_interval(CInterval *c_interval, + double rel_time, RelativeStart rel_to); + int add_ext_index(int ext_index, const string &name, + double duration, bool open_ended, + double rel_time, RelativeStart rel_to); + int pop_level(); + + enum DefType { + DT_c_interval, + DT_ext_index, + DT_push_level, + DT_pop_level + }; + + INLINE int get_num_defs() const; + INLINE DefType get_def_type(int n) const; + INLINE CInterval *get_c_interval(int n) const; + INLINE int get_ext_index(int n) const; + + virtual void initialize(double t); + virtual void instant(); + virtual void step(double t); + virtual void finalize(); + virtual void reverse_initialize(double t); + virtual void reverse_instant(); + virtual void reverse_finalize(); + + INLINE bool is_event_ready(); + INLINE int get_event_index() const; + INLINE double get_event_t() const; + INLINE EventType get_event_type() const; + INLINE void pop_event(); + + virtual void write(ostream &out, int indent_level) const; + +protected: + virtual void do_recompute(); + +private: + class IntervalDef { + public: + DefType _type; + PT(CInterval) _c_interval; + int _ext_index; + string _ext_name; + double _ext_duration; + bool _ext_open_ended; + double _rel_time; + RelativeStart _rel_to; + int _actual_begin_time; + }; + + enum PlaybackEventType { + PET_begin, + PET_end, + PET_instant + }; + + class PlaybackEvent { + public: + INLINE PlaybackEvent(int time, int n, PlaybackEventType type); + INLINE bool operator < (const PlaybackEvent &other) const; + int _time; + int _n; + PlaybackEventType _type; + PlaybackEvent *_begin_event; + }; + + class EventQueueEntry { + public: + INLINE EventQueueEntry(int n, EventType event_type, int time); + int _n; + EventType _event_type; + int _time; + }; + + typedef pvector Defs; + typedef pvector PlaybackEvents; + typedef pset ActiveEvents; + typedef pdeque EventQueue; + + INLINE int double_to_int_time(double t) const; + INLINE double int_to_double_time(int time) const; + + void clear_events(); + void do_event_forward(PlaybackEvent *event, ActiveEvents &new_active, + bool is_initial); + void finish_events_forward(int now, ActiveEvents &new_active); + void do_event_reverse(PlaybackEvent *event, ActiveEvents &new_active, + bool is_initial); + void finish_events_reverse(int now, ActiveEvents &new_active); + + void enqueue_event(int n, CInterval::EventType event_type, bool is_initial, + int time = 0); + bool service_event_queue(); + + int recompute_level(int n, int level_begin, int &level_end); + int get_begin_time(const IntervalDef &def, int level_begin, + int previous_begin, int previous_end); + + double _precision; + Defs _defs; + int _current_nesting_level; + + PlaybackEvents _events; + ActiveEvents _active; + int _end_time; + + size_t _next_event_index; + + // This is the queue of events that have occurred due to a recent + // initialize(), step(), etc., but have not yet been serviced, due + // to an embedded external (e.g. Python) interval that the scripting + // language must service. This queue should be considered precious, + // and should never be arbitrarily flushed without servicing all of + // its events. + EventQueue _event_queue; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CInterval::init_type(); + register_type(_type_handle, "CMetaInterval", + CInterval::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "cMetaInterval.I" + +#endif + diff --git a/direct/src/interval/config_interval.cxx b/direct/src/interval/config_interval.cxx new file mode 100644 index 0000000000..cd15656dca --- /dev/null +++ b/direct/src/interval/config_interval.cxx @@ -0,0 +1,65 @@ +// Filename: config_interval.cxx +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "config_interval.h" +#include "cInterval.h" +#include "cLerpInterval.h" +#include "cLerpNodePathInterval.h" +#include "cLerpAnimEffectInterval.h" +#include "cMetaInterval.h" +#include "showInterval.h" +#include "hideInterval.h" + +#include "dconfig.h" + +Configure(config_interval); +NotifyCategoryDef(interval, ""); + +ConfigureFn(config_interval) { + init_libinterval(); +} + +// Set this to the default value for set_precision() for each +// CMetaInterval created. +const double interval_precision = config_interval.GetDouble("interval-precision", 1000.0); + +//////////////////////////////////////////////////////////////////// +// Function: init_libinterval +// Description: Initializes the library. This must be called at +// least once before any of the functions or classes in +// this library can be used. Normally it will be +// called by the static initializers and need not be +// called explicitly, but special cases exist. +//////////////////////////////////////////////////////////////////// +void +init_libinterval() { + static bool initialized = false; + if (initialized) { + return; + } + initialized = true; + + CInterval::init_type(); + CLerpInterval::init_type(); + CLerpNodePathInterval::init_type(); + CLerpAnimEffectInterval::init_type(); + CMetaInterval::init_type(); + ShowInterval::init_type(); + HideInterval::init_type(); +} + diff --git a/direct/src/interval/config_interval.h b/direct/src/interval/config_interval.h new file mode 100644 index 0000000000..0670b57b0c --- /dev/null +++ b/direct/src/interval/config_interval.h @@ -0,0 +1,32 @@ +// Filename: config_interval.h +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_INTERVAL_H +#define CONFIG_INTERVAL_H + +#include "directbase.h" +#include "notifyCategoryProxy.h" +#include "dconfig.h" + +NotifyCategoryDecl(interval, EXPCL_DIRECT, EXPTP_DIRECT); + +extern const double interval_precision; + +extern EXPCL_DIRECT void init_libinterval(); + +#endif diff --git a/direct/src/interval/hideInterval.I b/direct/src/interval/hideInterval.I new file mode 100644 index 0000000000..c45549958b --- /dev/null +++ b/direct/src/interval/hideInterval.I @@ -0,0 +1,18 @@ +// Filename: hideInterval.I +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + diff --git a/direct/src/interval/hideInterval.cxx b/direct/src/interval/hideInterval.cxx new file mode 100644 index 0000000000..a0fbe3081e --- /dev/null +++ b/direct/src/interval/hideInterval.cxx @@ -0,0 +1,68 @@ +// Filename: hideInterval.cxx +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "hideInterval.h" + +int HideInterval::_unique_index; +TypeHandle HideInterval::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: HideInterval::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +HideInterval:: +HideInterval(const NodePath &node, const string &name) : + CInterval(name, 0.0, true), + _node(node) +{ + nassertv(!node.is_empty()); + if (_name.empty()) { + ostringstream name_strm; + name_strm + << "HideInterval-" << node.node()->get_name() << "-" << ++_unique_index; + _name = name_strm.str(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: HideInterval::instant +// Access: Published, Virtual +// Description: This is called in lieu of initialize() .. step() +// .. finalize(), when everything is to happen within +// one frame. The interval should initialize itself, +// then leave itself in the final state. +//////////////////////////////////////////////////////////////////// +void HideInterval:: +instant() { + _node.hide(); +} + +//////////////////////////////////////////////////////////////////// +// Function: HideInterval::reverse_instant +// Access: Published, Virtual +// Description: This is called in lieu of reverse_initialize() +// .. step() .. reverse_finalize(), when everything is +// to happen within one frame. The interval should +// initialize itself, then leave itself in the initial +// state. +//////////////////////////////////////////////////////////////////// +void HideInterval:: +reverse_instant() { + _node.show(); +} diff --git a/direct/src/interval/hideInterval.h b/direct/src/interval/hideInterval.h new file mode 100644 index 0000000000..36c2aef60f --- /dev/null +++ b/direct/src/interval/hideInterval.h @@ -0,0 +1,61 @@ +// Filename: hideInterval.h +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef HIDEINTERVAL_H +#define HIDEINTERVAL_H + +#include "directbase.h" +#include "cInterval.h" + +//////////////////////////////////////////////////////////////////// +// Class : HideInterval +// Description : An interval that calls NodePath::hide(). +//////////////////////////////////////////////////////////////////// +class EXPCL_DIRECT HideInterval : public CInterval { +PUBLISHED: + HideInterval(const NodePath &node, const string &name = string()); + + virtual void instant(); + virtual void reverse_instant(); + +private: + NodePath _node; + static int _unique_index; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CInterval::init_type(); + register_type(_type_handle, "HideInterval", + CInterval::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "hideInterval.I" + +#endif + diff --git a/direct/src/interval/lerp_helpers.h b/direct/src/interval/lerp_helpers.h new file mode 100644 index 0000000000..4867f452d3 --- /dev/null +++ b/direct/src/interval/lerp_helpers.h @@ -0,0 +1,75 @@ +// Filename: lerp_helpers.h +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef LERP_HELPERS_H +#define LERP_HELPERS_H + +#include "directbase.h" + +// +// This header file is only included within .cxx files in this +// directory. +// + +// +// The functions defined here include some trivial template functions +// for handling basic lerp computations, common to several .cxx files +// here. +// + +//////////////////////////////////////////////////////////////////// +// Function: lerp_value +// Description: Applies the linear lerp computation for a single +// parameter. +//////////////////////////////////////////////////////////////////// +template +INLINE void +lerp_value(NumericType ¤t_value, + double d, + const NumericType &starting_value, + const NumericType &ending_value) { + current_value = starting_value + d * (ending_value - starting_value); +} + +//////////////////////////////////////////////////////////////////// +// Function: lerp_value_from_prev +// Description: Applies the linear lerp computation for a single +// parameter, when the starting value is implicit. +// +// This computes the new value based on assuming the +// prev_value represents the value computed at delta +// prev_d. +//////////////////////////////////////////////////////////////////// +template +INLINE void +lerp_value_from_prev(NumericType ¤t_value, + double d, double prev_d, + const NumericType &prev_value, + const NumericType &ending_value) { + if (prev_d == 1.0) { + current_value = ending_value; + } else { + NumericType starting_value = + (prev_value - prev_d * ending_value) / (1.0 - prev_d); + current_value = starting_value + d * (ending_value - starting_value); + } +} + + +#endif + diff --git a/direct/src/interval/showInterval.I b/direct/src/interval/showInterval.I new file mode 100644 index 0000000000..1780fe4747 --- /dev/null +++ b/direct/src/interval/showInterval.I @@ -0,0 +1,18 @@ +// Filename: showInterval.I +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + diff --git a/direct/src/interval/showInterval.cxx b/direct/src/interval/showInterval.cxx new file mode 100644 index 0000000000..6472ac12c9 --- /dev/null +++ b/direct/src/interval/showInterval.cxx @@ -0,0 +1,68 @@ +// Filename: showInterval.cxx +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#include "showInterval.h" + +int ShowInterval::_unique_index; +TypeHandle ShowInterval::_type_handle; + +//////////////////////////////////////////////////////////////////// +// Function: ShowInterval::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +ShowInterval:: +ShowInterval(const NodePath &node, const string &name) : + CInterval(name, 0.0, true), + _node(node) +{ + nassertv(!node.is_empty()); + if (_name.empty()) { + ostringstream name_strm; + name_strm + << "ShowInterval-" << node.node()->get_name() << "-" << ++_unique_index; + _name = name_strm.str(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: ShowInterval::instant +// Access: Published, Virtual +// Description: This is called in lieu of initialize() .. step() +// .. finalize(), when everything is to happen within +// one frame. The interval should initialize itself, +// then leave itself in the final state. +//////////////////////////////////////////////////////////////////// +void ShowInterval:: +instant() { + _node.show(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ShowInterval::reverse_instant +// Access: Published, Virtual +// Description: This is called in lieu of reverse_initialize() +// .. step() .. reverse_finalize(), when everything is +// to happen within one frame. The interval should +// initialize itself, then leave itself in the initial +// state. +//////////////////////////////////////////////////////////////////// +void ShowInterval:: +reverse_instant() { + _node.hide(); +} diff --git a/direct/src/interval/showInterval.h b/direct/src/interval/showInterval.h new file mode 100644 index 0000000000..4f39aff64f --- /dev/null +++ b/direct/src/interval/showInterval.h @@ -0,0 +1,61 @@ +// Filename: showInterval.h +// Created by: drose (27Aug02) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#ifndef SHOWINTERVAL_H +#define SHOWINTERVAL_H + +#include "directbase.h" +#include "cInterval.h" + +//////////////////////////////////////////////////////////////////// +// Class : ShowInterval +// Description : An interval that calls NodePath::show(). +//////////////////////////////////////////////////////////////////// +class EXPCL_DIRECT ShowInterval : public CInterval { +PUBLISHED: + ShowInterval(const NodePath &node, const string &name = string()); + + virtual void instant(); + virtual void reverse_instant(); + +private: + NodePath _node; + static int _unique_index; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + CInterval::init_type(); + register_type(_type_handle, "ShowInterval", + CInterval::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "showInterval.I" + +#endif +