mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 10:54:24 -04:00
much better slerp algorithm
This commit is contained in:
parent
52a0c84fce
commit
f2c3f1a18e
@ -83,7 +83,7 @@ set_end_pos(const LVecBase3f &pos) {
|
|||||||
INLINE void CLerpNodePathInterval::
|
INLINE void CLerpNodePathInterval::
|
||||||
set_start_hpr(const LVecBase3f &hpr) {
|
set_start_hpr(const LVecBase3f &hpr) {
|
||||||
_start_hpr = hpr;
|
_start_hpr = hpr;
|
||||||
_flags |= F_start_hpr;
|
_flags = (_flags & ~(F_slerp_setup | F_start_quat)) | F_start_hpr;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -135,7 +135,7 @@ set_end_hpr(const LQuaternionf &quat) {
|
|||||||
INLINE void CLerpNodePathInterval::
|
INLINE void CLerpNodePathInterval::
|
||||||
set_start_quat(const LQuaternionf &quat) {
|
set_start_quat(const LQuaternionf &quat) {
|
||||||
_start_quat = quat;
|
_start_quat = quat;
|
||||||
_flags |= F_start_quat;
|
_flags = (_flags & ~(F_slerp_setup | F_start_hpr)) | F_start_quat;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -158,7 +158,7 @@ set_start_quat(const LQuaternionf &quat) {
|
|||||||
INLINE void CLerpNodePathInterval::
|
INLINE void CLerpNodePathInterval::
|
||||||
set_end_quat(const LVecBase3f &hpr) {
|
set_end_quat(const LVecBase3f &hpr) {
|
||||||
_end_quat.set_hpr(hpr);
|
_end_quat.set_hpr(hpr);
|
||||||
_flags = (_flags & ~F_end_hpr) | F_end_quat;
|
_flags = (_flags & ~(F_slerp_setup | F_end_hpr)) | F_end_quat;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
@ -175,7 +175,7 @@ set_end_quat(const LVecBase3f &hpr) {
|
|||||||
INLINE void CLerpNodePathInterval::
|
INLINE void CLerpNodePathInterval::
|
||||||
set_end_quat(const LQuaternionf &quat) {
|
set_end_quat(const LQuaternionf &quat) {
|
||||||
_end_quat = quat;
|
_end_quat = quat;
|
||||||
_flags = (_flags & ~F_end_hpr) | F_end_quat;
|
_flags = (_flags & ~(F_slerp_setup | F_end_hpr)) | F_end_quat;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
@ -66,7 +66,8 @@ CLerpNodePathInterval(const string &name, double duration,
|
|||||||
CLerpInterval(name, duration, blend_type),
|
CLerpInterval(name, duration, blend_type),
|
||||||
_node(node),
|
_node(node),
|
||||||
_other(other),
|
_other(other),
|
||||||
_flags(0)
|
_flags(0),
|
||||||
|
_slerp(NULL)
|
||||||
{
|
{
|
||||||
if (bake_in_start) {
|
if (bake_in_start) {
|
||||||
_flags |= F_bake_in_start;
|
_flags |= F_bake_in_start;
|
||||||
@ -181,23 +182,35 @@ priv_step(double t) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((_flags & F_end_quat) != 0) {
|
if ((_flags & F_end_quat) != 0) {
|
||||||
if ((_flags & F_start_quat) != 0) {
|
if ((_flags & F_slerp_setup) == 0) {
|
||||||
lerp_value(quat, d, _start_quat, _end_quat);
|
if ((_flags & F_start_quat) != 0) {
|
||||||
|
setup_slerp();
|
||||||
|
|
||||||
} else if ((_flags & F_start_hpr) != 0) {
|
} else if ((_flags & F_start_hpr) != 0) {
|
||||||
_start_quat.set_hpr(_start_hpr);
|
_start_quat.set_hpr(_start_hpr);
|
||||||
_flags |= F_start_quat;
|
_flags |= F_start_quat;
|
||||||
lerp_value(quat, d, _start_quat, _end_quat);
|
setup_slerp();
|
||||||
|
|
||||||
} else if ((_flags & F_bake_in_start) != 0) {
|
} else if ((_flags & F_bake_in_start) != 0) {
|
||||||
set_start_quat(transform->get_quat());
|
set_start_quat(transform->get_quat());
|
||||||
lerp_value(quat, d, _start_quat, _end_quat);
|
setup_slerp();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
quat = transform->get_quat();
|
if (_prev_d == 1.0) {
|
||||||
lerp_value_from_prev(quat, d, _prev_d, quat, _end_quat);
|
_start_quat = _end_quat;
|
||||||
|
} else {
|
||||||
|
LQuaternionf prev_value = transform->get_quat();
|
||||||
|
_start_quat = (prev_value - _prev_d * _end_quat) / (1.0 - _prev_d);
|
||||||
|
}
|
||||||
|
setup_slerp();
|
||||||
|
|
||||||
|
// In this case, clear the slerp_setup flag because we need
|
||||||
|
// to re-setup the slerp each time.
|
||||||
|
_flags &= ~F_slerp_setup;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
quat.normalize();
|
nassertv(_slerp != NULL);
|
||||||
|
(this->*_slerp)(quat, d);
|
||||||
}
|
}
|
||||||
if ((_flags & F_end_scale) != 0) {
|
if ((_flags & F_end_scale) != 0) {
|
||||||
if ((_flags & F_start_scale) != 0) {
|
if ((_flags & F_start_scale) != 0) {
|
||||||
@ -558,3 +571,118 @@ output(ostream &out) const {
|
|||||||
|
|
||||||
out << " dur " << get_duration();
|
out << " dur " << get_duration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: CLerpNodePathInterval::setup_slerp
|
||||||
|
// Access: Private
|
||||||
|
// Description: Sets up a spherical lerp from _start_quat to
|
||||||
|
// _end_quat. This precomputes some important values
|
||||||
|
// (like the angle between the quaternions) and sets up
|
||||||
|
// the _slerp method pointer.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void CLerpNodePathInterval::
|
||||||
|
setup_slerp() {
|
||||||
|
if (_start_quat.dot(_end_quat) < 0.0f) {
|
||||||
|
// Make sure both quaternions are on the same side.
|
||||||
|
_start_quat = -_start_quat;
|
||||||
|
}
|
||||||
|
|
||||||
|
_slerp_angle = _start_quat.angle_rad(_end_quat);
|
||||||
|
|
||||||
|
if (_slerp_angle < 0.1f) {
|
||||||
|
// If the angle is small, use sin(angle)/angle as the denominator,
|
||||||
|
// to provide better behavior with small divisors. This is Don
|
||||||
|
// Hatch's suggestion from http://www.hadron.org/~hatch/rightway.php .
|
||||||
|
_slerp_denom = csin_over_x(_slerp_angle);
|
||||||
|
_slerp = &CLerpNodePathInterval::slerp_angle_0;
|
||||||
|
|
||||||
|
} else if (_slerp_angle > 3.14) {
|
||||||
|
// If the angle is close to 180 degrees, the lerp is ambiguous.
|
||||||
|
// which plane should we lerp through? Better pick an
|
||||||
|
// intermediate point to resolve the ambiguity up front.
|
||||||
|
|
||||||
|
// We pick it by choosing a linear point between the quats and
|
||||||
|
// normalizing it out; this will give an arbitrary point when the
|
||||||
|
// angle is exactly 180, but will behave sanely as the angle
|
||||||
|
// approaches 180.
|
||||||
|
_slerp_c = (_start_quat + _end_quat);
|
||||||
|
_slerp_c.normalize();
|
||||||
|
_slerp_angle = _end_quat.angle_rad(_slerp_c);
|
||||||
|
_slerp_denom = csin(_slerp_angle);
|
||||||
|
|
||||||
|
_slerp = &CLerpNodePathInterval::slerp_angle_180;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Otherwise, use the original Shoemake equation for spherical
|
||||||
|
// lerp.
|
||||||
|
_slerp_denom = csin(_slerp_angle);
|
||||||
|
_slerp = &CLerpNodePathInterval::slerp_basic;
|
||||||
|
}
|
||||||
|
|
||||||
|
_flags |= F_slerp_setup;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: CLerpNodePathInterval::slerp_basic
|
||||||
|
// Access: Private
|
||||||
|
// Description: Implements Ken Shoemake's spherical lerp equation.
|
||||||
|
// This is appropriate when the angle between the
|
||||||
|
// quaternions is not near one extreme or the other.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void CLerpNodePathInterval::
|
||||||
|
slerp_basic(LQuaternionf &result, float t) const {
|
||||||
|
float ti = 1.0f - t;
|
||||||
|
float ta = t * _slerp_angle;
|
||||||
|
float tia = ti * _slerp_angle;
|
||||||
|
|
||||||
|
result = (csin(tia) * _start_quat + csin(ta) * _end_quat) / _slerp_denom;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: CLerpNodePathInterval::slerp_angle_0
|
||||||
|
// Access: Private
|
||||||
|
// Description: Implements Don Hatch's modified spherical lerp
|
||||||
|
// equation, appropriate for when the angle between the
|
||||||
|
// quaternions approaches zero.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void CLerpNodePathInterval::
|
||||||
|
slerp_angle_0(LQuaternionf &result, float t) const {
|
||||||
|
float ti = 1.0f - t;
|
||||||
|
float ta = t * _slerp_angle;
|
||||||
|
float tia = ti * _slerp_angle;
|
||||||
|
|
||||||
|
result = (csin_over_x(tia) * ti * _start_quat + csin_over_x(ta) * t * _end_quat) / _slerp_denom;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Function: CLerpNodePathInterval::slerp_angle_180
|
||||||
|
// Access: Private
|
||||||
|
// Description: Implements a two-part slerp, to an intermediate point
|
||||||
|
// and out again, appropriate for when the angle between
|
||||||
|
// the quaternions approaches 180 degrees.
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
void CLerpNodePathInterval::
|
||||||
|
slerp_angle_180(LQuaternionf &result, float t) const {
|
||||||
|
if (t < 0.5) {
|
||||||
|
// The first half of the lerp: _start_quat to _slerp_c.
|
||||||
|
|
||||||
|
t *= 2.0f;
|
||||||
|
|
||||||
|
float ti = 1.0f - t;
|
||||||
|
float ta = t * _slerp_angle;
|
||||||
|
float tia = ti * _slerp_angle;
|
||||||
|
|
||||||
|
result = (csin(tia) * _start_quat + csin(ta) * _slerp_c) / _slerp_denom;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// The second half of the lerp: _slerp_c to _end_quat.
|
||||||
|
t = t * 2.0f - 1.0f;
|
||||||
|
|
||||||
|
float ti = 1.0f - t;
|
||||||
|
float ta = t * _slerp_angle;
|
||||||
|
float tia = ti * _slerp_angle;
|
||||||
|
|
||||||
|
result = (csin(tia) * _slerp_c + csin(ta) * _end_quat) / _slerp_denom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -66,28 +66,32 @@ PUBLISHED:
|
|||||||
virtual void output(ostream &out) const;
|
virtual void output(ostream &out) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void setup_slerp();
|
||||||
|
|
||||||
NodePath _node;
|
NodePath _node;
|
||||||
NodePath _other;
|
NodePath _other;
|
||||||
|
|
||||||
enum Flags {
|
enum Flags {
|
||||||
F_end_pos = 0x0001,
|
F_end_pos = 0x000001,
|
||||||
F_end_hpr = 0x0002,
|
F_end_hpr = 0x000002,
|
||||||
F_end_quat = 0x0004,
|
F_end_quat = 0x000004,
|
||||||
F_end_scale = 0x0008,
|
F_end_scale = 0x000008,
|
||||||
F_end_color = 0x0010,
|
F_end_color = 0x000010,
|
||||||
F_end_color_scale = 0x0020,
|
F_end_color_scale = 0x000020,
|
||||||
F_end_shear = 0x0040,
|
F_end_shear = 0x000040,
|
||||||
|
|
||||||
F_start_pos = 0x0080,
|
F_start_pos = 0x000080,
|
||||||
F_start_hpr = 0x0100,
|
F_start_hpr = 0x000100,
|
||||||
F_start_quat = 0x0200,
|
F_start_quat = 0x000200,
|
||||||
F_start_scale = 0x0400,
|
F_start_scale = 0x000400,
|
||||||
F_start_color = 0x0800,
|
F_start_color = 0x000800,
|
||||||
F_start_color_scale = 0x1000,
|
F_start_color_scale = 0x001000,
|
||||||
F_start_shear = 0x2000,
|
F_start_shear = 0x002000,
|
||||||
|
|
||||||
F_fluid = 0x4000,
|
F_fluid = 0x004000,
|
||||||
F_bake_in_start = 0x8000,
|
F_bake_in_start = 0x008000,
|
||||||
|
|
||||||
|
F_slerp_setup = 0x010000,
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned int _flags;
|
unsigned int _flags;
|
||||||
@ -100,6 +104,16 @@ private:
|
|||||||
LVecBase4f _start_color_scale, _end_color_scale;
|
LVecBase4f _start_color_scale, _end_color_scale;
|
||||||
|
|
||||||
double _prev_d;
|
double _prev_d;
|
||||||
|
float _slerp_angle;
|
||||||
|
float _slerp_denom;
|
||||||
|
LQuaternionf _slerp_c;
|
||||||
|
|
||||||
|
void slerp_basic(LQuaternionf &result, float t) const;
|
||||||
|
void slerp_angle_0(LQuaternionf &result, float t) const;
|
||||||
|
void slerp_angle_180(LQuaternionf &result, float t) const;
|
||||||
|
|
||||||
|
// Define a pointer to one of the above three methods.
|
||||||
|
void (CLerpNodePathInterval::*_slerp)(LQuaternionf &result, float t) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static TypeHandle get_class_type() {
|
static TypeHandle get_class_type() {
|
||||||
|
@ -749,7 +749,6 @@ class ShowBase(DirectObject.DirectObject):
|
|||||||
lens.setAspectRatio(aspectRatio)
|
lens.setAspectRatio(aspectRatio)
|
||||||
|
|
||||||
camNode.setLens(lens)
|
camNode.setLens(lens)
|
||||||
camNode.setScene(scene)
|
|
||||||
|
|
||||||
# self.camera is the parent node of all cameras: a node that
|
# self.camera is the parent node of all cameras: a node that
|
||||||
# we can move around to move all cameras as a group.
|
# we can move around to move all cameras as a group.
|
||||||
@ -791,7 +790,6 @@ class ShowBase(DirectObject.DirectObject):
|
|||||||
lens.setFilmOffset((right + left) * 0.5, (top + bottom) * 0.5)
|
lens.setFilmOffset((right + left) * 0.5, (top + bottom) * 0.5)
|
||||||
lens.setNearFar(-1000, 1000)
|
lens.setNearFar(-1000, 1000)
|
||||||
cam2dNode.setLens(lens)
|
cam2dNode.setLens(lens)
|
||||||
cam2dNode.setScene(self.render2d)
|
|
||||||
|
|
||||||
# self.camera2d is the analog of self.camera, although it's
|
# self.camera2d is the analog of self.camera, although it's
|
||||||
# not as clear how useful it is.
|
# not as clear how useful it is.
|
||||||
@ -825,7 +823,6 @@ class ShowBase(DirectObject.DirectObject):
|
|||||||
lens.setFilmOffset((right + left) * 0.5, (top + bottom) * 0.5)
|
lens.setFilmOffset((right + left) * 0.5, (top + bottom) * 0.5)
|
||||||
lens.setNearFar(-1000, 1000)
|
lens.setNearFar(-1000, 1000)
|
||||||
cam2dNode.setLens(lens)
|
cam2dNode.setLens(lens)
|
||||||
cam2dNode.setScene(self.render2dp)
|
|
||||||
|
|
||||||
# self.camera2d is the analog of self.camera, although it's
|
# self.camera2d is the analog of self.camera, although it's
|
||||||
# not as clear how useful it is.
|
# not as clear how useful it is.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user