Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Bolsee <benoit.bolsee@online.be>2008-09-24 00:07:15 +0400
committerBenoit Bolsee <benoit.bolsee@online.be>2008-09-24 00:07:15 +0400
commit005c5e63711e92677547ec2477238fd1f016fb30 (patch)
tree4c195c8ffe44d96757c42b7aebbbcb4c3b407944
parent3cf87f4c20c8766bf5f58c3800a8bbd67091f1a4 (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.
-rw-r--r--source/gameengine/Ketsji/KX_IPO_SGController.cpp115
-rw-r--r--source/gameengine/Ketsji/KX_IPO_SGController.h6
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;