enable intra-frame animation interpolation
This commit is contained in:
parent
a7afebd785
commit
731ab7b576
|
|
@ -1149,6 +1149,67 @@ class Actor(DirectObject, NodePath):
|
|||
for control in self.getAnimControls(animName, partName, lodName):
|
||||
control.pose(frame)
|
||||
|
||||
def setBlend(self, animBlend = None, frameBlend = None,
|
||||
blendType = None, partName = None):
|
||||
"""
|
||||
Changes the way the Actor handles blending of multiple
|
||||
different animations, and/or interpolation between consecutive
|
||||
frames.
|
||||
|
||||
The animBlend and frameBlend parameters are boolean flags.
|
||||
You may set either or both to True or False. If you do not
|
||||
specify them, they do not change from the previous value.
|
||||
|
||||
When animBlend is True, multiple different animations may
|
||||
simultaneously be playing on the Actor. This means you may
|
||||
call play(), loop(), or pose() on multiple animations and have
|
||||
all of them contribute to the final pose each frame.
|
||||
|
||||
In this mode (that is, when animBlend is True), starting a
|
||||
particular animation with play(), loop(), or pose() does not
|
||||
implicitly make the animation visible; you must also call
|
||||
setControlEffect() for each animation you wish to use to
|
||||
indicate how much each animation contributes to the final
|
||||
pose.
|
||||
|
||||
The frameBlend flag is unrelated to playing multiple
|
||||
animations. It controls whether the Actor smoothly
|
||||
interpolates between consecutive frames of its animation (when
|
||||
the flag is True) or holds each frame until the next one is
|
||||
ready (when the flag is False). The default value of
|
||||
frameBlend is controlled by the interpolate-frames Config.prc
|
||||
variable.
|
||||
|
||||
In either case, you may also specify blendType, which controls
|
||||
the precise algorithm used to blend two or more different
|
||||
matrix values into a final result. Different skeleton
|
||||
hierarchies may benefit from different algorithms. The
|
||||
default blendType is controlled by the anim-blend-type
|
||||
Config.prc variable.
|
||||
"""
|
||||
bundles = []
|
||||
|
||||
for lodName, bundleDict in self.__partBundleDict.items():
|
||||
if partName == None:
|
||||
for partBundle in bundleDict.values():
|
||||
bundles.append(partBundle.node().getBundle())
|
||||
|
||||
else:
|
||||
truePartName = self.__subpartDict.get(partName, [partName])[0]
|
||||
partBundle = bundleDict.get(truePartName)
|
||||
if partBundle != None:
|
||||
bundles.append(partBundle.node().getBundle())
|
||||
else:
|
||||
Actor.notify.warning("Couldn't find part: %s" % (partName))
|
||||
|
||||
for bundle in bundles:
|
||||
if blendType != None:
|
||||
bundle.setBlendType(blendType)
|
||||
if animBlend != None:
|
||||
bundle.setAnimBlendFlag(animBlend)
|
||||
if frameBlend != None:
|
||||
bundle.setFrameBlendFlag(frameBlend)
|
||||
|
||||
def enableBlend(self, blendType = PartBundle.BTNormalizedLinear, partName = None):
|
||||
"""
|
||||
Enables blending of multiple animations simultaneously.
|
||||
|
|
@ -1161,25 +1222,19 @@ class Actor(DirectObject, NodePath):
|
|||
animation visible; you must also call setControlEffect() for
|
||||
each animation you wish to use to indicate how much each
|
||||
animation contributes to the final pose.
|
||||
|
||||
This method is deprecated. You should use setBlend() instead.
|
||||
"""
|
||||
for lodName, bundleDict in self.__partBundleDict.items():
|
||||
if partName == None:
|
||||
for partBundle in bundleDict.values():
|
||||
partBundle.node().getBundle().setBlendType(blendType)
|
||||
else:
|
||||
truePartName = self.__subpartDict.get(partName, [partName])[0]
|
||||
partBundle = bundleDict.get(truePartName)
|
||||
if partBundle != None:
|
||||
partBundle.node().getBundle().setBlendType(blendType)
|
||||
else:
|
||||
Actor.notify.warning("Couldn't find part: %s" % (partName))
|
||||
self.setBlend(animBlend = True, blendType = blendType, partName = partName)
|
||||
|
||||
def disableBlend(self, partName = None):
|
||||
"""
|
||||
Restores normal one-animation-at-a-time operation after a
|
||||
previous call to enableBlend().
|
||||
|
||||
This method is deprecated. You should use setBlend() instead.
|
||||
"""
|
||||
self.enableBlend(PartBundle.BTSingle, partName)
|
||||
self.setBlend(animBlend = False, partName = partName)
|
||||
|
||||
def setControlEffect(self, animName, effect,
|
||||
partName = None, lodName = None):
|
||||
|
|
@ -1187,7 +1242,7 @@ class Actor(DirectObject, NodePath):
|
|||
Sets the amount by which the named animation contributes to
|
||||
the overall pose. This controls blending of multiple
|
||||
animations; it only makes sense to call this after a previous
|
||||
call to enableBlend().
|
||||
call to setBlend(animBlend = True).
|
||||
"""
|
||||
for control in self.getAnimControls(animName, partName, lodName):
|
||||
control.getPart().setControlEffect(control, effect)
|
||||
|
|
|
|||
|
|
@ -83,6 +83,13 @@ PRC_DESC("Set this false to disable reading of compressed animation channels, "
|
|||
"might want to do this would be to speed load time when you don't "
|
||||
"care about what the animation looks like."));
|
||||
|
||||
ConfigVariableBool interpolate_frames
|
||||
("interpolate-frames", false,
|
||||
PRC_DESC("Set this true to interpolate character animations between frames, "
|
||||
"or false to hold each frame until the next one is ready. This can "
|
||||
"also be changed on a per-character basis with "
|
||||
"PartBundle::set_frame_blend_flag()."));
|
||||
|
||||
|
||||
ConfigureFn(config_chan) {
|
||||
AnimBundle::init_type();
|
||||
|
|
|
|||
|
|
@ -30,5 +30,6 @@ NotifyCategoryDecl(chan, EXPCL_PANDA, EXPTP_PANDA);
|
|||
EXPCL_PANDA extern ConfigVariableBool compress_channels;
|
||||
EXPCL_PANDA extern ConfigVariableInt compress_chan_quality;
|
||||
EXPCL_PANDA extern ConfigVariableBool read_compressed_channels;
|
||||
EXPCL_PANDA extern ConfigVariableBool interpolate_frames;
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ get_blend_value(const PartBundle *root) {
|
|||
// No channel is bound; supply the default value.
|
||||
_value = _initial_value;
|
||||
|
||||
} else if (cdata->_blend.size() == 1) {
|
||||
} else if (cdata->_blend.size() == 1 && !cdata->_frame_blend_flag) {
|
||||
// A single value, the normal case.
|
||||
AnimControl *control = (*cdata->_blend.begin()).first;
|
||||
|
||||
|
|
@ -85,10 +85,11 @@ get_blend_value(const PartBundle *root) {
|
|||
}
|
||||
|
||||
} else {
|
||||
// A blend of two or more values.
|
||||
// A blend of two or more values, either between multiple
|
||||
// different animations, or between consecutive frames of the same
|
||||
// animation (or both).
|
||||
|
||||
switch (cdata->_blend_type) {
|
||||
case PartBundle::BT_single:
|
||||
case PartBundle::BT_linear:
|
||||
{
|
||||
// An ordinary, linear blend.
|
||||
|
|
@ -107,8 +108,18 @@ get_blend_value(const PartBundle *root) {
|
|||
if (channel != (ChannelType *)NULL) {
|
||||
ValueType v;
|
||||
channel->get_value(control->get_frame(), v);
|
||||
|
||||
_value += v * effect;
|
||||
|
||||
if (!cdata->_frame_blend_flag) {
|
||||
// Hold the current frame until the next one is ready.
|
||||
_value += v * effect;
|
||||
} else {
|
||||
// Blend between successive frames.
|
||||
float frac = (float)control->get_frac();
|
||||
_value += v * (effect * (1.0f - frac));
|
||||
|
||||
channel->get_value(control->get_next_frame(), v);
|
||||
_value += v * (effect * frac);
|
||||
}
|
||||
net += effect;
|
||||
}
|
||||
}
|
||||
|
|
@ -144,15 +155,35 @@ get_blend_value(const PartBundle *root) {
|
|||
nassertv(channel_index >= 0 && channel_index < (int)_channels.size());
|
||||
ChannelType *channel = DCAST(ChannelType, _channels[channel_index]);
|
||||
if (channel != (ChannelType *)NULL) {
|
||||
int frame = control->get_frame();
|
||||
ValueType v;
|
||||
channel->get_value_no_scale_shear(control->get_frame(), v);
|
||||
LVecBase3f iscale, ishear;
|
||||
channel->get_scale(control->get_frame(), iscale);
|
||||
channel->get_shear(control->get_frame(), ishear);
|
||||
channel->get_value_no_scale_shear(frame, v);
|
||||
channel->get_scale(frame, iscale);
|
||||
channel->get_shear(frame, ishear);
|
||||
|
||||
_value += v * effect;
|
||||
scale += iscale * effect;
|
||||
shear += ishear * effect;
|
||||
if (!cdata->_frame_blend_flag) {
|
||||
// Hold the current frame until the next one is ready.
|
||||
_value += v * effect;
|
||||
scale += iscale * effect;
|
||||
shear += ishear * effect;
|
||||
} else {
|
||||
// Blend between successive frames.
|
||||
float frac = (float)control->get_frac();
|
||||
float e0 = effect * (1.0f - frac);
|
||||
_value += v * e0;
|
||||
scale += iscale * e0;
|
||||
shear += ishear * e0;
|
||||
|
||||
int next_frame = control->get_next_frame();
|
||||
channel->get_value_no_scale_shear(next_frame, v);
|
||||
channel->get_scale(next_frame, iscale);
|
||||
channel->get_shear(next_frame, ishear);
|
||||
float e1 = effect * frac;
|
||||
_value += v * e1;
|
||||
scale += iscale * e1;
|
||||
shear += ishear * e1;
|
||||
}
|
||||
net += effect;
|
||||
}
|
||||
}
|
||||
|
|
@ -193,16 +224,41 @@ get_blend_value(const PartBundle *root) {
|
|||
nassertv(channel_index >= 0 && channel_index < (int)_channels.size());
|
||||
ChannelType *channel = DCAST(ChannelType, _channels[channel_index]);
|
||||
if (channel != (ChannelType *)NULL) {
|
||||
int frame = control->get_frame();
|
||||
LVecBase3f iscale, ihpr, ipos, ishear;
|
||||
channel->get_scale(control->get_frame(), iscale);
|
||||
channel->get_hpr(control->get_frame(), ihpr);
|
||||
channel->get_pos(control->get_frame(), ipos);
|
||||
channel->get_shear(control->get_frame(), ishear);
|
||||
channel->get_scale(frame, iscale);
|
||||
channel->get_hpr(frame, ihpr);
|
||||
channel->get_pos(frame, ipos);
|
||||
channel->get_shear(frame, ishear);
|
||||
|
||||
scale += iscale * effect;
|
||||
hpr += ihpr * effect;
|
||||
pos += ipos * effect;
|
||||
shear += ishear * effect;
|
||||
if (!cdata->_frame_blend_flag) {
|
||||
// Hold the current frame until the next one is ready.
|
||||
scale += iscale * effect;
|
||||
hpr += ihpr * effect;
|
||||
pos += ipos * effect;
|
||||
shear += ishear * effect;
|
||||
} else {
|
||||
// Blend between successive frames.
|
||||
float frac = (float)control->get_frac();
|
||||
float e0 = effect * (1.0f - frac);
|
||||
|
||||
scale += iscale * e0;
|
||||
hpr += ihpr * e0;
|
||||
pos += ipos * e0;
|
||||
shear += ishear * e0;
|
||||
|
||||
int next_frame = control->get_next_frame();
|
||||
channel->get_scale(next_frame, iscale);
|
||||
channel->get_hpr(next_frame, ihpr);
|
||||
channel->get_pos(next_frame, ipos);
|
||||
channel->get_shear(next_frame, ishear);
|
||||
float e1 = effect * frac;
|
||||
|
||||
scale += iscale * e1;
|
||||
hpr += ihpr * e1;
|
||||
pos += ipos * e1;
|
||||
shear += ishear * e1;
|
||||
}
|
||||
net += effect;
|
||||
}
|
||||
}
|
||||
|
|
@ -241,17 +297,43 @@ get_blend_value(const PartBundle *root) {
|
|||
nassertv(channel_index >= 0 && channel_index < (int)_channels.size());
|
||||
ChannelType *channel = DCAST(ChannelType, _channels[channel_index]);
|
||||
if (channel != (ChannelType *)NULL) {
|
||||
int frame = control->get_frame();
|
||||
LVecBase3f iscale, ipos, ishear;
|
||||
LQuaternionf iquat;
|
||||
channel->get_scale(control->get_frame(), iscale);
|
||||
channel->get_quat(control->get_frame(), iquat);
|
||||
channel->get_pos(control->get_frame(), ipos);
|
||||
channel->get_shear(control->get_frame(), ishear);
|
||||
channel->get_scale(frame, iscale);
|
||||
channel->get_quat(frame, iquat);
|
||||
channel->get_pos(frame, ipos);
|
||||
channel->get_shear(frame, ishear);
|
||||
|
||||
scale += iscale * effect;
|
||||
quat += iquat * effect;
|
||||
pos += ipos * effect;
|
||||
shear += ishear * effect;
|
||||
if (!cdata->_frame_blend_flag) {
|
||||
// Hold the current frame until the next one is ready.
|
||||
scale += iscale * effect;
|
||||
quat += iquat * effect;
|
||||
pos += ipos * effect;
|
||||
shear += ishear * effect;
|
||||
|
||||
} else {
|
||||
// Blend between successive frames.
|
||||
float frac = (float)control->get_frac();
|
||||
float e0 = effect * (1.0f - frac);
|
||||
|
||||
scale += iscale * e0;
|
||||
quat += iquat * e0;
|
||||
pos += ipos * e0;
|
||||
shear += ishear * e0;
|
||||
|
||||
int next_frame = control->get_next_frame();
|
||||
channel->get_scale(next_frame, iscale);
|
||||
channel->get_quat(next_frame, iquat);
|
||||
channel->get_pos(next_frame, ipos);
|
||||
channel->get_shear(next_frame, ishear);
|
||||
float e1 = effect * frac;
|
||||
|
||||
scale += iscale * e1;
|
||||
quat += iquat * e1;
|
||||
pos += ipos * e1;
|
||||
shear += ishear * e1;
|
||||
}
|
||||
net += effect;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ get_blend_value(const PartBundle *root) {
|
|||
// No channel is bound; supply the default value.
|
||||
_value = _initial_value;
|
||||
|
||||
} else if (cdata->_blend.size() == 1) {
|
||||
} else if (cdata->_blend.size() == 1 && !cdata->_frame_blend_flag) {
|
||||
// A single value, the normal case.
|
||||
AnimControl *control = (*cdata->_blend.begin()).first;
|
||||
|
||||
|
|
@ -87,7 +87,17 @@ get_blend_value(const PartBundle *root) {
|
|||
ValueType v;
|
||||
channel->get_value(control->get_frame(), v);
|
||||
|
||||
_value += v * effect;
|
||||
if (!cdata->_frame_blend_flag) {
|
||||
// Hold the current frame until the next one is ready.
|
||||
_value += v * effect;
|
||||
} else {
|
||||
// Blend between successive frames.
|
||||
float frac = (float)control->get_frac();
|
||||
_value += v * (effect * (1.0f - frac));
|
||||
|
||||
channel->get_value(control->get_next_frame(), v);
|
||||
_value += v * (effect * frac);
|
||||
}
|
||||
net += effect;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,11 +17,31 @@
|
|||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: PartBundle::set_blend_type
|
||||
// Access: Published
|
||||
// Description: Defines the algorithm that is used when blending
|
||||
// multiple frames or multiple animations together, when
|
||||
// either anim_blend_flag or frame_blend_flag is set
|
||||
// to true.
|
||||
//
|
||||
// See partBundle.h for a description of the meaning of
|
||||
// each of the BlendType values.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void PartBundle::
|
||||
set_blend_type(PartBundle::BlendType bt) {
|
||||
nassertv(Thread::get_current_pipeline_stage() == 0);
|
||||
CDWriter cdata(_cycler);
|
||||
cdata->_blend_type = bt;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: PartBundle::get_blend_type
|
||||
// Access: Published
|
||||
// Description: Returns the way the character responds to multiple
|
||||
// animations being bound simultaneously.
|
||||
// Description: Returns the algorithm that is used when blending
|
||||
// multiple frames or multiple animations together, when
|
||||
// either anim_blend_flag or frame_blend_flag is set
|
||||
// to true.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE PartBundle::BlendType PartBundle::
|
||||
get_blend_type() const {
|
||||
|
|
@ -29,6 +49,62 @@ get_blend_type() const {
|
|||
return cdata->_blend_type;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: PartBundle::get_anim_blend_flag
|
||||
// Access: Published
|
||||
// Description: Returns whether the character allows multiple
|
||||
// different animations to be bound simultaneously. See
|
||||
// set_anim_blend_flag().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool PartBundle::
|
||||
get_anim_blend_flag() const {
|
||||
CDReader cdata(_cycler);
|
||||
return cdata->_anim_blend_flag;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: PartBundle::set_frame_blend_flag
|
||||
// Access: Published
|
||||
// Description: Specifies whether the character interpolates (blends)
|
||||
// between two sequential frames of an active animation,
|
||||
// showing a smooth intra-frame motion, or whether it
|
||||
// holds each frame until the next frame is ready,
|
||||
// showing precisely the specified animation.
|
||||
//
|
||||
// When this value is false, the character holds each
|
||||
// frame until the next is ready. When this is true,
|
||||
// the character will interpolate between two
|
||||
// consecutive frames of animation for each frame the
|
||||
// animation is onscreen, according to the amount of
|
||||
// time elapsed between the frames.
|
||||
//
|
||||
// The default value of this flag is determined by the
|
||||
// interpolate-frames Config.prc variable.
|
||||
//
|
||||
// Use set_blend_type() to change the algorithm that the
|
||||
// character uses to interpolate matrix positions.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void PartBundle::
|
||||
set_frame_blend_flag(bool frame_blend_flag) {
|
||||
nassertv(Thread::get_current_pipeline_stage() == 0);
|
||||
CDWriter cdata(_cycler);
|
||||
cdata->_frame_blend_flag = frame_blend_flag;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: PartBundle::get_frame_blend_flag
|
||||
// Access: Published
|
||||
// Description: Returns whether the character interpolates (blends)
|
||||
// between two sequential animation frames, or whether
|
||||
// it holds the current frame until the next one is
|
||||
// ready. See set_frame_blend_flag().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool PartBundle::
|
||||
get_frame_blend_flag() const {
|
||||
CDReader cdata(_cycler);
|
||||
return cdata->_frame_blend_flag;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: PartBundle::get_node
|
||||
// Access: Published
|
||||
|
|
@ -50,11 +126,11 @@ get_node() const {
|
|||
// Zero indicates the animation does not affect the
|
||||
// character, and one means it does.
|
||||
//
|
||||
// If the blend_type is not BT_single (see
|
||||
// set_blend_type()), it is possible to have multiple
|
||||
// AnimControls in effect simultaneously. In this case,
|
||||
// the effect is a weight that indicates the relative
|
||||
// importance of each AnimControl to the final
|
||||
// If the _anim_blend_flag is not false (see
|
||||
// set_anim_blend_flag()), it is possible to have
|
||||
// multiple AnimControls in effect simultaneously. In
|
||||
// this case, the effect is a weight that indicates the
|
||||
// relative importance of each AnimControl to the final
|
||||
// animation.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void PartBundle::
|
||||
|
|
|
|||
|
|
@ -22,16 +22,24 @@
|
|||
#include "animControl.h"
|
||||
#include "config_chan.h"
|
||||
#include "bitArray.h"
|
||||
|
||||
#include "string_utils.h"
|
||||
#include "indent.h"
|
||||
#include "datagram.h"
|
||||
#include "datagramIterator.h"
|
||||
#include "bamReader.h"
|
||||
#include "bamWriter.h"
|
||||
#include "configVariableEnum.h"
|
||||
|
||||
TypeHandle PartBundle::_type_handle;
|
||||
|
||||
|
||||
static ConfigVariableEnum<PartBundle::BlendType> anim_blend_type
|
||||
("anim-blend-type", PartBundle::BT_normalized_linear,
|
||||
PRC_DESC("The default blend type to use for blending animations between "
|
||||
"frames, or between multiple animations. See interpolate-frames, "
|
||||
"and also PartBundle::set_anim_blend_flag() and "
|
||||
"PartBundle::set_frame_blend_flag()."));
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: PartBundle::Copy Constructor
|
||||
|
|
@ -71,37 +79,33 @@ make_copy() const {
|
|||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: PartBundle::set_blend_type
|
||||
// Function: PartBundle::set_anim_blend_flag
|
||||
// Access: Published
|
||||
// Description: Defines the way the character responds to multiple
|
||||
// set_control_effect()). By default, the blend_type is
|
||||
// BT_single, which disallows multiple animations. In
|
||||
// BT_single mode, it is not necessary to explicitly set
|
||||
// the control_effect when starting an animation;
|
||||
// starting the animation will implicitly remove the
|
||||
// control_effect from the previous animation and set it
|
||||
// on the current one.
|
||||
// calls to set_control_effect()). By default, this
|
||||
// flag is set false, which disallows multiple
|
||||
// animations. When this flag is false, it is not
|
||||
// necessary to explicitly set the control_effect when
|
||||
// starting an animation; starting the animation will
|
||||
// implicitly remove the control_effect from the
|
||||
// previous animation and set it on the current one.
|
||||
//
|
||||
// However, if the blend_type is set to any other value,
|
||||
// the control_effect must be explicitly set via
|
||||
// set_control_effect() whenever an animation is to
|
||||
// affect the character.
|
||||
//
|
||||
// See partBundle.h for a description of the meaning of
|
||||
// each of the BlendType values.
|
||||
// However, if this flag is set true, the control_effect
|
||||
// must be explicitly set via set_control_effect()
|
||||
// whenever an animation is to affect the character.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void PartBundle::
|
||||
set_blend_type(BlendType bt) {
|
||||
set_anim_blend_flag(bool anim_blend_flag) {
|
||||
nassertv(Thread::get_current_pipeline_stage() == 0);
|
||||
|
||||
CDLockedReader cdata(_cycler);
|
||||
if (cdata->_blend_type != bt) {
|
||||
if (cdata->_anim_blend_flag != anim_blend_flag) {
|
||||
CDWriter cdataw(_cycler, cdata);
|
||||
cdataw->_blend_type = bt;
|
||||
cdataw->_anim_blend_flag = anim_blend_flag;
|
||||
|
||||
if (cdataw->_blend_type == BT_single && cdataw->_blend.size() > 1) {
|
||||
// If we just changed to a single blend type, i.e. no blending,
|
||||
// we should eliminate all the AnimControls other than the
|
||||
if (!anim_blend_flag && cdataw->_blend.size() > 1) {
|
||||
// If we just changed to disallow animation blending, we should
|
||||
// eliminate all the AnimControls other than the
|
||||
// most-recently-added one.
|
||||
|
||||
nassertv(cdataw->_last_control_set != NULL);
|
||||
|
|
@ -245,6 +249,12 @@ update() {
|
|||
{
|
||||
CDReader cdata(_cycler, current_thread);
|
||||
anim_changed = cdata->_anim_changed;
|
||||
|
||||
if (cdata->_frame_blend_flag) {
|
||||
// If the intra-frame blend flag is on, we will just assume the
|
||||
// animation changes every time we call update().
|
||||
anim_changed = true;
|
||||
}
|
||||
}
|
||||
bool any_changed = do_update(this, NULL, false, anim_changed, current_thread);
|
||||
|
||||
|
|
@ -300,9 +310,10 @@ control_activated(AnimControl *control) {
|
|||
nassertv(control->get_part() == this);
|
||||
|
||||
CDLockedReader cdata(_cycler);
|
||||
// If (and only if) our blend type is BT_single, which means no
|
||||
// blending, then starting an animation implicitly enables it.
|
||||
if (cdata->_blend_type == BT_single) {
|
||||
|
||||
// If (and only if) our anim_blend_flag is false, then starting an
|
||||
// animation implicitly enables it.
|
||||
if (!cdata->_anim_blend_flag) {
|
||||
CDWriter cdataw(_cycler, cdata);
|
||||
do_set_control_effect(control, 1.0f, cdataw);
|
||||
}
|
||||
|
|
@ -330,9 +341,9 @@ do_set_control_effect(AnimControl *control, float effect, CData *cdata) {
|
|||
} else {
|
||||
// Otherwise we define it.
|
||||
|
||||
// If we currently have BT_single, we only allow one AnimControl
|
||||
// at a time. Stop all of the other AnimControls.
|
||||
if (cdata->_blend_type == BT_single) {
|
||||
// If anim_blend_flag is false, we only allow one AnimControl at a
|
||||
// time. Stop all of the other AnimControls.
|
||||
if (!cdata->_anim_blend_flag) {
|
||||
clear_and_stop_intersecting(control, cdata);
|
||||
}
|
||||
|
||||
|
|
@ -388,9 +399,9 @@ recompute_net_blend(CData *cdata) {
|
|||
// Description: Removes and stops all the currently activated
|
||||
// AnimControls that animate some joints also animated
|
||||
// by the indicated AnimControl. This is a special
|
||||
// internal function that's only called when _blend_type
|
||||
// is BT_single, to automatically stop all the other
|
||||
// currently-executing animations.
|
||||
// internal function that's only called when
|
||||
// _anim_blend_flag is false, to automatically stop all
|
||||
// the other currently-executing animations.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void PartBundle::
|
||||
clear_and_stop_intersecting(AnimControl *control, CData *cdata) {
|
||||
|
|
@ -470,7 +481,9 @@ register_with_read_factory()
|
|||
////////////////////////////////////////////////////////////////////
|
||||
PartBundle::CData::
|
||||
CData() {
|
||||
_blend_type = BT_single;
|
||||
_blend_type = anim_blend_type;
|
||||
_anim_blend_flag = false;
|
||||
_frame_blend_flag = interpolate_frames;
|
||||
_last_control_set = NULL;
|
||||
_net_blend = 0.0f;
|
||||
_anim_changed = false;
|
||||
|
|
@ -503,3 +516,58 @@ CycleData *PartBundle::CData::
|
|||
make_copy() const {
|
||||
return new CData(*this);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: PartBundle::BlendType output operator
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
ostream &
|
||||
operator << (ostream &out, PartBundle::BlendType blend_type) {
|
||||
switch (blend_type) {
|
||||
case PartBundle::BT_linear:
|
||||
return out << "linear";
|
||||
|
||||
case PartBundle::BT_normalized_linear:
|
||||
return out << "normalized_linear";
|
||||
|
||||
case PartBundle::BT_componentwise:
|
||||
return out << "componentwise";
|
||||
|
||||
case PartBundle::BT_componentwise_quat:
|
||||
return out << "componentwise_quat";
|
||||
}
|
||||
|
||||
chan_cat->error()
|
||||
<< "Invalid BlendType value: " << (int)blend_type << "\n";
|
||||
nassertr(false, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: PartBundle::BlendType input operator
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
istream &
|
||||
operator >> (istream &in, PartBundle::BlendType &blend_type) {
|
||||
string word;
|
||||
in >> word;
|
||||
|
||||
if (cmp_nocase_uh(word, "linear") == 0) {
|
||||
blend_type = PartBundle::BT_linear;
|
||||
|
||||
} else if (cmp_nocase_uh(word, "normalized_linear") == 0) {
|
||||
blend_type = PartBundle::BT_normalized_linear;
|
||||
|
||||
} else if (cmp_nocase_uh(word, "componentwise") == 0) {
|
||||
blend_type = PartBundle::BT_componentwise;
|
||||
|
||||
} else if (cmp_nocase_uh(word, "componentwise_quat") == 0) {
|
||||
blend_type = PartBundle::BT_componentwise_quat;
|
||||
|
||||
} else {
|
||||
chan_cat->error()
|
||||
<< "Invalid BlendType string: " << word << "\n";
|
||||
blend_type = PartBundle::BT_linear;
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,18 +60,9 @@ PUBLISHED:
|
|||
|
||||
// This is the parameter to set_blend_type() and specifies the kind
|
||||
// of blending operation to be performed when multiple controls are
|
||||
// in effect simultaneously (see set_control_effect()).
|
||||
// in effect simultaneously (see set_control_effect()) or between
|
||||
// sequential frames of the animation.
|
||||
enum BlendType {
|
||||
|
||||
// BT_single means no blending is performed. Only one AnimControl
|
||||
// is allowed to be set at a time in set_control_effect(). In
|
||||
// this mode, and this mode only, activating a particular
|
||||
// AnimControl (via play(), loop(), or pose()) will implicitly set
|
||||
// the bundle's control_effect to 100% of that particular
|
||||
// AnimControl, removing any AnimControls previously set. In
|
||||
// other modes, the control_effect must be set manually.
|
||||
BT_single,
|
||||
|
||||
// BT_linear does a componentwise average of all blended matrices,
|
||||
// which is a linear blend. The result of this is that if a
|
||||
// particular vertex would have been at point P in one animation
|
||||
|
|
@ -100,9 +91,15 @@ PUBLISHED:
|
|||
BT_componentwise_quat,
|
||||
};
|
||||
|
||||
void set_blend_type(BlendType bt);
|
||||
INLINE void set_blend_type(BlendType bt);
|
||||
INLINE BlendType get_blend_type() const;
|
||||
|
||||
void set_anim_blend_flag(bool anim_blend_flag);
|
||||
INLINE bool get_anim_blend_flag() const;
|
||||
|
||||
INLINE void set_frame_blend_flag(bool frame_blend_flag);
|
||||
INLINE bool get_frame_blend_flag() const;
|
||||
|
||||
INLINE PartBundleNode *get_node() const;
|
||||
|
||||
void clear_control_effects();
|
||||
|
|
@ -147,6 +144,8 @@ private:
|
|||
}
|
||||
|
||||
BlendType _blend_type;
|
||||
bool _anim_blend_flag;
|
||||
bool _frame_blend_flag;
|
||||
AnimControl *_last_control_set;
|
||||
ChannelBlend _blend;
|
||||
float _net_blend;
|
||||
|
|
@ -193,6 +192,9 @@ inline ostream &operator <<(ostream &out, const PartBundle &bundle) {
|
|||
return out;
|
||||
}
|
||||
|
||||
ostream &operator <<(ostream &out, PartBundle::BlendType blend_type);
|
||||
istream &operator >>(istream &in, PartBundle::BlendType &blend_type);
|
||||
|
||||
#include "partBundle.I"
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ Character(const Character ©) :
|
|||
// update our _parts list.
|
||||
|
||||
copy_joints(get_bundle(), copy.get_bundle());
|
||||
|
||||
_last_auto_update = -1.0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -154,15 +156,18 @@ cull_callback(CullTraverser *, CullTraverserData &) {
|
|||
// optimization later, to handle characters that might animate
|
||||
// themselves in front of the view frustum.
|
||||
|
||||
PStatTimer timer(_joints_pcollector);
|
||||
double now = ClockObject::get_global_clock()->get_frame_time();
|
||||
if (now != _last_auto_update) {
|
||||
_last_auto_update = now;
|
||||
|
||||
if (char_cat.is_spam()) {
|
||||
double now = ClockObject::get_global_clock()->get_frame_time();
|
||||
char_cat.spam() << "Animating " << *this << " at time " << now << "\n";
|
||||
PStatTimer timer(_joints_pcollector);
|
||||
if (char_cat.is_spam()) {
|
||||
char_cat.spam() << "Animating " << *this << " at time " << now << "\n";
|
||||
}
|
||||
|
||||
do_update();
|
||||
}
|
||||
|
||||
do_update();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -119,6 +119,8 @@ private:
|
|||
typedef vector_PartGroupStar Parts;
|
||||
Parts _parts;
|
||||
|
||||
double _last_auto_update;
|
||||
|
||||
// Statistics
|
||||
PStatCollector _joints_pcollector;
|
||||
PStatCollector _skinning_pcollector;
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ pingpong(bool restart, double from, double to) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void AnimInterface::
|
||||
stop() {
|
||||
pose(get_frame());
|
||||
pose(get_full_fframe());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -129,7 +129,7 @@ stop() {
|
|||
// it there.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void AnimInterface::
|
||||
pose(int frame) {
|
||||
pose(double frame) {
|
||||
{
|
||||
CDWriter cdata(_cycler);
|
||||
cdata->pose(frame);
|
||||
|
|
@ -192,7 +192,30 @@ get_frame() const {
|
|||
if (num_frames <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return cmod(get_full_frame(), num_frames);
|
||||
CDReader cdata(_cycler);
|
||||
return cmod(cdata->get_full_frame(0), num_frames);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: AnimInterface::get_next_frame
|
||||
// Access: Published
|
||||
// Description: Returns the current integer frame number + 1,
|
||||
// constrained to the range 0 <= f < get_num_frames().
|
||||
//
|
||||
// If the play mode is PM_play, this will clamp to the
|
||||
// same value as get_frame() at the end of the
|
||||
// animation. If the play mode is any other value, this
|
||||
// will wrap around to frame 0 at the end of the
|
||||
// animation.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE int AnimInterface::
|
||||
get_next_frame() const {
|
||||
int num_frames = get_num_frames();
|
||||
if (num_frames <= 0) {
|
||||
return 0;
|
||||
}
|
||||
CDReader cdata(_cycler);
|
||||
return cmod(cdata->get_full_frame(1), num_frames);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -229,7 +252,7 @@ get_frac() const {
|
|||
INLINE int AnimInterface::
|
||||
get_full_frame() const {
|
||||
CDReader cdata(_cycler);
|
||||
return cdata->get_full_frame();
|
||||
return cdata->get_full_frame(0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -308,7 +331,7 @@ set_num_frames(int num_frames) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
INLINE double AnimInterface::CData::
|
||||
get_frac() const {
|
||||
return get_full_fframe() - (double)get_full_frame();
|
||||
return get_full_fframe() - (double)get_full_frame(0);
|
||||
}
|
||||
|
||||
INLINE ostream &
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@ fillin(DatagramIterator &scan, BamReader *) {
|
|||
void AnimInterface::CData::
|
||||
play(double from, double to) {
|
||||
if (from >= to) {
|
||||
pose((int)from);
|
||||
pose(from);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -259,7 +259,7 @@ play(double from, double to) {
|
|||
void AnimInterface::CData::
|
||||
loop(bool restart, double from, double to) {
|
||||
if (from >= to) {
|
||||
pose((int)from);
|
||||
pose(from);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -292,7 +292,7 @@ loop(bool restart, double from, double to) {
|
|||
void AnimInterface::CData::
|
||||
pingpong(bool restart, double from, double to) {
|
||||
if (from >= to) {
|
||||
pose((int)from);
|
||||
pose(from);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -322,20 +322,21 @@ pingpong(bool restart, double from, double to) {
|
|||
// it there.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void AnimInterface::CData::
|
||||
pose(int frame) {
|
||||
pose(double frame) {
|
||||
_play_mode = PM_pose;
|
||||
_start_time = ClockObject::get_global_clock()->get_frame_time();
|
||||
_start_frame = (double)frame;
|
||||
_start_frame = frame;
|
||||
_play_frames = 0.0;
|
||||
_from_frame = frame;
|
||||
_to_frame = frame;
|
||||
_from_frame = (int)floor(frame);
|
||||
_to_frame = (int)floor(frame);
|
||||
_paused_f = 0.0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: AnimInterface::CData::get_full_frame
|
||||
// Access: Public
|
||||
// Description: Returns the current integer frame number.
|
||||
// Description: Returns the current integer frame number, plus the
|
||||
// indicated increment.
|
||||
//
|
||||
// Unlike the value returned by get_frame(), this frame
|
||||
// number may extend beyond the range of
|
||||
|
|
@ -347,8 +348,8 @@ pose(int frame) {
|
|||
// to_frame in the play() method.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
int AnimInterface::CData::
|
||||
get_full_frame() const {
|
||||
int frame = (int)floor(get_full_fframe());
|
||||
get_full_frame(int increment) const {
|
||||
int frame = (int)floor(get_full_fframe()) + increment;
|
||||
if (_play_mode == PM_play) {
|
||||
// In play mode, we never let the return value exceed
|
||||
// (_from_frame, _to_frame).
|
||||
|
|
@ -435,19 +436,19 @@ void AnimInterface::CData::
|
|||
output(ostream &out) const {
|
||||
switch (_play_mode) {
|
||||
case PM_pose:
|
||||
out << "pose, frame " << get_full_frame();
|
||||
out << "pose, frame " << get_full_fframe();
|
||||
return;
|
||||
|
||||
case PM_play:
|
||||
out << "play, frame " << get_full_frame();
|
||||
out << "play, frame " << get_full_fframe();
|
||||
return;
|
||||
|
||||
case PM_loop:
|
||||
out << "loop, frame " << get_full_frame();
|
||||
out << "loop, frame " << get_full_fframe();
|
||||
return;
|
||||
|
||||
case PM_pingpong:
|
||||
out << "pingpong, frame " << get_full_frame();
|
||||
out << "pingpong, frame " << get_full_fframe();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ PUBLISHED:
|
|||
INLINE void pingpong(bool restart);
|
||||
INLINE void pingpong(bool restart, double from, double to);
|
||||
INLINE void stop();
|
||||
INLINE void pose(int frame);
|
||||
INLINE void pose(double frame);
|
||||
|
||||
INLINE void set_play_rate(double play_rate);
|
||||
INLINE double get_play_rate() const;
|
||||
|
|
@ -62,6 +62,7 @@ PUBLISHED:
|
|||
virtual int get_num_frames() const;
|
||||
|
||||
INLINE int get_frame() const;
|
||||
INLINE int get_next_frame() const;
|
||||
INLINE double get_frac() const;
|
||||
INLINE int get_full_frame() const;
|
||||
INLINE double get_full_fframe() const;
|
||||
|
|
@ -102,10 +103,10 @@ private:
|
|||
void play(double from, double to);
|
||||
void loop(bool restart, double from, double to);
|
||||
void pingpong(bool restart, double from, double to);
|
||||
void pose(int frame);
|
||||
void pose(double frame);
|
||||
|
||||
INLINE double get_frac() const;
|
||||
int get_full_frame() const;
|
||||
int get_full_frame(int increment) const;
|
||||
double get_full_fframe() const;
|
||||
bool is_playing() const;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue