diff options
author | Benoit Bolsee <benoit.bolsee@online.be> | 2008-09-24 00:07:15 +0400 |
---|---|---|
committer | Benoit Bolsee <benoit.bolsee@online.be> | 2008-09-24 00:07:15 +0400 |
commit | 005c5e63711e92677547ec2477238fd1f016fb30 (patch) | |
tree | 4c195c8ffe44d96757c42b7aebbbcb4c3b407944 /source/gameengine | |
parent | 3cf87f4c20c8766bf5f58c3800a8bbd67091f1a4 (diff) |
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.
Diffstat (limited to 'source/gameengine')
-rw-r--r-- | source/gameengine/Ketsji/KX_IPO_SGController.cpp | 115 | ||||
-rw-r--r-- | source/gameengine/Ketsji/KX_IPO_SGController.h | 6 |
2 files changed, 85 insertions, 36 deletions
diff --git a/source/gameengine/Ketsji/KX_IPO_SGController.cpp b/source/gameengine/Ketsji/KX_IPO_SGController.cpp index 6223643f75a..67d54cf0b0b 100644 --- a/source/gameengine/Ketsji/KX_IPO_SGController.cpp +++ b/source/gameengine/Ketsji/KX_IPO_SGController.cpp @@ -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? diff --git a/source/gameengine/Ketsji/KX_IPO_SGController.h b/source/gameengine/Ketsji/KX_IPO_SGController.h index 0bd8980f11c..031b74294ce 100644 --- a/source/gameengine/Ketsji/KX_IPO_SGController.h +++ b/source/gameengine/Ketsji/KX_IPO_SGController.h @@ -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; |