forked from bartvdbraak/blender
BGE bug #17657 fixed: dRotY doesn't work properly after 90 degrees rotation.
This problem is caused by discontinuities in the conversion orientation matrix -> euler angles: the angle sign can switch and thus the direction of the rotation produced by the dRot Ipo. To avoid this bug, the matrix->euler conversion must be avoided during the game. I took the following approach that is compatible with Blender (identical effect in the game and in the 3D view): - no change in Add mode: Rot and dRot are treated as additional rotation to the orientation at the start of the Ipo. There is no matrix->euler conversion and thus no discontinuities. - Rot Ipo are treated as absolute rotation. All 3 axis should be specified but if they are not, the startup object orientation will be used to set the unspecified axis. By doing a matrix-> euler conversion once at the start, the discontinuities are avoided. If there are also dRot curves, they are treated as delta of the corresponding Rot curve or startup angle. - dRot Ipo are treated as Add mode in Local axis. Note about Add mode: Rot and dRot curves are treated identically during the game. However, only dRot curves make sense because they don't interfere with the object orientation in the 3D view.
This commit is contained in:
parent
3cf87f4c20
commit
005c5e6371
@ -59,7 +59,9 @@ KX_IpoSGController::KX_IpoSGController()
|
||||
m_ipo_local(false),
|
||||
m_modified(true),
|
||||
m_ipo_start_initialized(false),
|
||||
m_ipotime(1.0)
|
||||
m_ipotime(1.0),
|
||||
m_ipo_start_euler(0.0,0.0,0.0),
|
||||
m_ipo_euler_initialized(false)
|
||||
{
|
||||
m_game_object = NULL;
|
||||
for (int i=0; i < KX_MAX_IPO_CHANNELS; i++)
|
||||
@ -136,6 +138,11 @@ bool KX_IpoSGController::Update(double currentTime)
|
||||
m_ipo_start_orient = ob->GetLocalOrientation();
|
||||
m_ipo_start_scale = ob->GetLocalScale();
|
||||
m_ipo_start_initialized = true;
|
||||
if (!m_ipo_euler_initialized) {
|
||||
// do it only once to avoid angle discontinuities
|
||||
m_ipo_start_orient.getEuler(m_ipo_start_euler[0], m_ipo_start_euler[1], m_ipo_start_euler[2]);
|
||||
m_ipo_euler_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
//modifies position?
|
||||
@ -199,51 +206,87 @@ bool KX_IpoSGController::Update(double currentTime)
|
||||
ob->GetWorldOrientation() * m_ipo_xform.GetEulerAngles() :
|
||||
m_ipo_xform.GetEulerAngles(), false);
|
||||
}
|
||||
} else {
|
||||
double yaw=0, pitch=0, roll=0; //final Euler angles
|
||||
double tempYaw=0, tempPitch=0, tempRoll=0; //temp holders
|
||||
if (!m_ipo_add)
|
||||
ob->GetLocalOrientation().getEuler(yaw, pitch, roll);
|
||||
} else if (m_ipo_add) {
|
||||
if (m_ipo_start_initialized) {
|
||||
double yaw=0, pitch=0, roll=0; //delta Euler angles
|
||||
|
||||
//RotX and dRotX
|
||||
if (m_ipo_channels_active[OB_ROT_X]) {
|
||||
yaw = (m_ipo_channels_active[OB_DROT_X] ? (m_ipo_xform.GetEulerAngles()[0] + m_ipo_xform.GetDeltaEulerAngles()[0]) : m_ipo_xform.GetEulerAngles()[0] );
|
||||
}
|
||||
else if (m_ipo_channels_active[OB_DROT_X] && m_ipo_start_initialized) {
|
||||
if (!m_ipo_add)
|
||||
m_ipo_start_orient.getEuler(tempYaw, tempPitch, tempRoll);
|
||||
yaw = tempYaw + m_ipo_xform.GetDeltaEulerAngles()[0];
|
||||
}
|
||||
//RotX and dRotX
|
||||
if (m_ipo_channels_active[OB_ROT_X])
|
||||
yaw += m_ipo_xform.GetEulerAngles()[0];
|
||||
if (m_ipo_channels_active[OB_DROT_X])
|
||||
yaw += m_ipo_xform.GetDeltaEulerAngles()[0];
|
||||
|
||||
//RotY dRotY
|
||||
if (m_ipo_channels_active[OB_ROT_Y])
|
||||
pitch += m_ipo_xform.GetEulerAngles()[1];
|
||||
if (m_ipo_channels_active[OB_DROT_Y])
|
||||
pitch += m_ipo_xform.GetDeltaEulerAngles()[1];
|
||||
|
||||
//RotZ and dRotZ
|
||||
if (m_ipo_channels_active[OB_ROT_Z])
|
||||
roll += m_ipo_xform.GetEulerAngles()[2];
|
||||
if (m_ipo_channels_active[OB_DROT_Z])
|
||||
roll += m_ipo_xform.GetDeltaEulerAngles()[2];
|
||||
|
||||
//RotY dRotY
|
||||
if (m_ipo_channels_active[OB_ROT_Y]) {
|
||||
pitch = (m_ipo_channels_active[OB_DROT_Y] ? (m_ipo_xform.GetEulerAngles()[1] + m_ipo_xform.GetDeltaEulerAngles()[1]) : m_ipo_xform.GetEulerAngles()[1] );
|
||||
}
|
||||
else if (m_ipo_channels_active[OB_DROT_Y] && m_ipo_start_initialized) {
|
||||
if (!m_ipo_add)
|
||||
m_ipo_start_orient.getEuler(tempYaw, tempPitch, tempRoll);
|
||||
pitch = tempPitch + m_ipo_xform.GetDeltaEulerAngles()[1];
|
||||
}
|
||||
|
||||
//RotZ and dRotZ
|
||||
if (m_ipo_channels_active[OB_ROT_Z]) {
|
||||
roll = (m_ipo_channels_active[OB_DROT_Z] ? (m_ipo_xform.GetEulerAngles()[2] + m_ipo_xform.GetDeltaEulerAngles()[2]) : m_ipo_xform.GetEulerAngles()[2] );
|
||||
}
|
||||
else if (m_ipo_channels_active[OB_DROT_Z] && m_ipo_start_initialized) {
|
||||
if (!m_ipo_add)
|
||||
m_ipo_start_orient.getEuler(tempYaw, tempPitch, tempRoll);
|
||||
roll = tempRoll + m_ipo_xform.GetDeltaEulerAngles()[2];
|
||||
}
|
||||
if (m_ipo_add) {
|
||||
MT_Matrix3x3 rotation(MT_Vector3(yaw, pitch, roll));
|
||||
if (m_ipo_local)
|
||||
rotation = m_ipo_start_orient * rotation;
|
||||
else
|
||||
rotation = rotation * m_ipo_start_orient;
|
||||
ob->SetLocalOrientation(rotation);
|
||||
} else {
|
||||
}
|
||||
} else if (m_ipo_channels_active[OB_ROT_X] || m_ipo_channels_active[OB_ROT_Y] || m_ipo_channels_active[OB_ROT_Z]) {
|
||||
if (m_ipo_euler_initialized) {
|
||||
// assume all channel absolute
|
||||
// All 3 channels should be specified but if they are not, we will take
|
||||
// the value at the start of the game to avoid angle sign reversal
|
||||
double yaw=m_ipo_start_euler[0], pitch=m_ipo_start_euler[1], roll=m_ipo_start_euler[2];
|
||||
|
||||
//RotX and dRotX
|
||||
if (m_ipo_channels_active[OB_ROT_X]) {
|
||||
yaw = (m_ipo_channels_active[OB_DROT_X] ? (m_ipo_xform.GetEulerAngles()[0] + m_ipo_xform.GetDeltaEulerAngles()[0]) : m_ipo_xform.GetEulerAngles()[0] );
|
||||
}
|
||||
else if (m_ipo_channels_active[OB_DROT_X]) {
|
||||
yaw += m_ipo_xform.GetDeltaEulerAngles()[0];
|
||||
}
|
||||
|
||||
//RotY dRotY
|
||||
if (m_ipo_channels_active[OB_ROT_Y]) {
|
||||
pitch = (m_ipo_channels_active[OB_DROT_Y] ? (m_ipo_xform.GetEulerAngles()[1] + m_ipo_xform.GetDeltaEulerAngles()[1]) : m_ipo_xform.GetEulerAngles()[1] );
|
||||
}
|
||||
else if (m_ipo_channels_active[OB_DROT_Y]) {
|
||||
pitch += m_ipo_xform.GetDeltaEulerAngles()[1];
|
||||
}
|
||||
|
||||
//RotZ and dRotZ
|
||||
if (m_ipo_channels_active[OB_ROT_Z]) {
|
||||
roll = (m_ipo_channels_active[OB_DROT_Z] ? (m_ipo_xform.GetEulerAngles()[2] + m_ipo_xform.GetDeltaEulerAngles()[2]) : m_ipo_xform.GetEulerAngles()[2] );
|
||||
}
|
||||
else if (m_ipo_channels_active[OB_DROT_Z]) {
|
||||
roll += m_ipo_xform.GetDeltaEulerAngles()[2];
|
||||
}
|
||||
ob->SetLocalOrientation(MT_Vector3(yaw, pitch, roll));
|
||||
}
|
||||
} else if (m_ipo_start_initialized) {
|
||||
// only DROT, treat as Add
|
||||
double yaw=0, pitch=0, roll=0; //delta Euler angles
|
||||
|
||||
//dRotX
|
||||
if (m_ipo_channels_active[OB_DROT_X])
|
||||
yaw = m_ipo_xform.GetDeltaEulerAngles()[0];
|
||||
|
||||
//dRotY
|
||||
if (m_ipo_channels_active[OB_DROT_Y])
|
||||
pitch = m_ipo_xform.GetDeltaEulerAngles()[1];
|
||||
|
||||
//dRotZ
|
||||
if (m_ipo_channels_active[OB_DROT_Z])
|
||||
roll = m_ipo_xform.GetDeltaEulerAngles()[2];
|
||||
|
||||
// dRot are always local
|
||||
MT_Matrix3x3 rotation(MT_Vector3(yaw, pitch, roll));
|
||||
rotation = m_ipo_start_orient * rotation;
|
||||
ob->SetLocalOrientation(rotation);
|
||||
}
|
||||
}
|
||||
//modifies scale?
|
||||
|
@ -72,6 +72,12 @@ class KX_IpoSGController : public SG_Controller
|
||||
/** if IPO initial position has been set for local normal IPO */
|
||||
bool m_ipo_start_initialized;
|
||||
|
||||
/** Euler angles at the start of the game, needed for incomplete ROT Ipo curves */
|
||||
class MT_Vector3 m_ipo_start_euler;
|
||||
|
||||
/** true is m_ipo_start_euler has been initialized */
|
||||
bool m_ipo_euler_initialized;
|
||||
|
||||
/** A reference to the original game object. */
|
||||
class KX_GameObject* m_game_object;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user