BGE patch: Add min/max parameters to orientation constraint actuator
authorBenoit Bolsee <benoit.bolsee@online.be>
Tue, 22 Jul 2008 23:05:06 +0000 (23:05 +0000)
committerBenoit Bolsee <benoit.bolsee@online.be>
Tue, 22 Jul 2008 23:05:06 +0000 (23:05 +0000)
The min/max parameters define a minimum/maximum angle
that the object axis can have with the reference
direction without being constrainted. The angle is
expressed in degree and is limited to 0-180 range.
The min/max parameters define a conical free zone
around the reference direction.

If the object axis is outside that free zone, the
actuator will tend to put it back using as a temporary
reference direction the vector that is exactly at
min or max degree of the reference direction
(depending if the axis angle is below the minimum
or above the maximum) and is located in the plane
formed by the axis and the reference direction.

With a low damping value, this is equivalent to
clamping the axis orientation within min/max degree
of the reference direction.

Backward compatibility corresponds to the absence
of free zone: min = max = 0.

source/blender/src/buttons_logic.c
source/gameengine/Converter/KX_ConvertActuators.cpp
source/gameengine/Ketsji/KX_ConstraintActuator.cpp
source/gameengine/Ketsji/KX_ConstraintActuator.h

index 441d00ffc30cc9d173a833ec50eb8d63c253a9b3..6b29a05bd22145dcfce622b0d4e46a5d2b14e142 100644 (file)
@@ -2062,7 +2062,7 @@ static short draw_actuatorbuttons(Object *ob, bActuator *act, uiBlock *block, sh
                        coa->time = 0;
                        uiDefButS(block, MENU, 1, str,          xco+10, yco-65, 70, 19, &coa->flag, 0.0, 0.0, 0, 0, "");
                
-                       uiDefButS(block, NUM,           0, "Damp:",     xco+10, yco-45, 70, 19, &coa->damp, 0.0, 100.0, 0, 0, "");
+                       uiDefButS(block, NUM,           0, "damp",      xco+10, yco-45, 70, 19, &coa->damp, 0.0, 100.0, 0, 0, "Damping factor: time constant (in frame) of low pass filter");
                        uiDefBut(block, LABEL,                  0, "Min",       xco+80, yco-45, (width-90)/2, 19, NULL, 0.0, 0.0, 0, 0, "");
                        uiDefBut(block, LABEL,                  0, "Max",       xco+80+(width-90)/2, yco-45, (width-90)/2, 19, NULL, 0.0, 0.0, 0, 0, "");
 
@@ -2084,7 +2084,7 @@ static short draw_actuatorbuttons(Object *ob, bActuator *act, uiBlock *block, sh
                        str= "Direction %t|None %x0|X axis %x1|Y axis %x2|Z axis %x4|-X axis %x8|-Y axis %x16|-Z axis %x32";
                        uiDefButS(block, MENU, B_REDR, str,             xco+10, yco-65, 70, 19, &coa->mode, 0.0, 0.0, 0, 0, "Set the direction of the ray");
                
-                       uiDefButS(block, NUM,           0, "Damp:",     xco+10, yco-45, 70, 19, &coa->damp, 0.0, 100.0, 0, 0, "");
+                       uiDefButS(block, NUM,           0, "damp",      xco+10, yco-45, 70, 19, &coa->damp, 0.0, 100.0, 0, 0, "Damping factor: time constant (in frame) of low pass filter");
                        uiDefBut(block, LABEL,                  0, "Range",     xco+80, yco-45, (width-115)/2, 19, NULL, 0.0, 0.0, 0, 0, "Set the maximum length of ray");
                        uiDefButBitS(block, TOG, ACT_CONST_DISTANCE, B_REDR, "Dist",    xco+80+(width-115)/2, yco-45, (width-115)/2, 19, &coa->flag, 0.0, 0.0, 0, 0, "Force distance of object to point of impact of ray");
 
@@ -2124,7 +2124,7 @@ static short draw_actuatorbuttons(Object *ob, bActuator *act, uiBlock *block, sh
                        str= "Direction %t|None %x0|X axis %x1|Y axis %x2|Z axis %x4";
                        uiDefButS(block, MENU, B_REDR, str,             xco+10, yco-65, 70, 19, &coa->mode, 0.0, 0.0, 0, 0, "Select the axis to be aligned along the reference direction");
                
-                       uiDefButS(block, NUM,           0, "Damp:",     xco+10, yco-45, 70, 19, &coa->damp, 0.0, 100.0, 0, 0, "");
+                       uiDefButS(block, NUM,           0, "damp",      xco+10, yco-45, 70, 19, &coa->damp, 0.0, 100.0, 0, 0, "Damping factor: time constant (in frame) of low pass filter");
                        uiDefBut(block, LABEL,                  0, "X", xco+80, yco-45, (width-115)/3, 19, NULL, 0.0, 0.0, 0, 0, "");
                        uiDefBut(block, LABEL,                  0, "Y", xco+80+(width-115)/3, yco-45, (width-115)/3, 19, NULL, 0.0, 0.0, 0, 0, "");
                        uiDefBut(block, LABEL,                  0, "Z", xco+80+2*(width-115)/3, yco-45, (width-115)/3, 19, NULL, 0.0, 0.0, 0, 0, "");
@@ -2133,7 +2133,9 @@ static short draw_actuatorbuttons(Object *ob, bActuator *act, uiBlock *block, sh
                        uiDefButF(block, NUM, 0, "",            xco+80+(width-115)/3, yco-65, (width-115)/3, 19, &coa->maxrot[1], -2000.0, 2000.0, 10, 0, "Y component of reference direction");
                        uiDefButF(block, NUM, 0, "",            xco+80+2*(width-115)/3, yco-65, (width-115)/3, 19, &coa->maxrot[2], -2000.0, 2000.0, 10, 0, "Z component of reference direction");
 
-                       uiDefButS(block, NUM, 0, "time", xco+10, yco-84, 70+(width-115)/3, 19, &(coa->time), 0.0, 1000.0, 0, 0, "Maximum activation time in frame, 0 for unlimited");
+                       uiDefButS(block, NUM, 0, "time", xco+10, yco-84, 70, 19, &(coa->time), 0.0, 1000.0, 0, 0, "Maximum activation time in frame, 0 for unlimited");
+                       uiDefButF(block, NUM, 0, "min", xco+80, yco-84, (width-115)/2, 19, &(coa->minloc[0]), 0.0, 180.0, 10, 1, "Minimum angle (in degree) to maintain with target direction. No correction is done if angle with target direction is between min and max");
+                       uiDefButF(block, NUM, 0, "max", xco+80+(width-115)/2, yco-84, (width-115)/2, 19, &(coa->maxloc[0]), 0.0, 180.0, 10, 1, "Maximum angle (in degree) allowed with target direction. No correction is done if angle with target direction is between min and max");
                }
                str= "Constraint Type %t|Location %x0|Distance %x1|Orientation %x2";
                but = uiDefButS(block, MENU, B_REDR, str,               xco+40, yco-23, (width-80), 19, &coa->type, 0.0, 0.0, 0, 0, "");
index 8739fb109fd6e57a8fe4a6386dcaa2075595f685..f2ec01db839cb5b2d71f09617eaf1051fa5184d2 100644 (file)
@@ -628,6 +628,8 @@ void BL_ConvertActuators(char* maggiename,
                                /* convert settings... degrees in the ui become radians  */ 
                                /* internally                                            */ 
                                if (conact->type == ACT_CONST_TYPE_ORI) {
+                                       min = (MT_2_PI * conact->minloc[0])/360.0;
+                                       max = (MT_2_PI * conact->maxloc[0])/360.0;
                                        switch (conact->mode) {
                                        case ACT_CONST_DIRPX:
                                                locrot = KX_ConstraintActuator::KX_ACT_CONSTRAINT_ORIX;
index e0b2efb3a255364b09b44645de3381cf9653694c..800416b5f0ab5b786a5d5a04df5db5aa6cbe602b 100644 (file)
@@ -88,11 +88,17 @@ KX_ConstraintActuator::KX_ConstraintActuator(SCA_IObject *gameobj,
                        } else {
                                m_refDirection /= len;
                        }
+                       m_minimumBound = cos(minBound);
+                       m_maximumBound = cos(maxBound);
+                       m_minimumSine = sin(minBound);
+                       m_maximumSine = sin(maxBound);
                }
                break;
        default:
                m_minimumBound = minBound;
                m_maximumBound = maxBound;
+               m_minimumSine = 0.f;
+               m_maximumSine = 0.f;
                break;
        }
 
@@ -153,9 +159,9 @@ bool KX_ConstraintActuator::Update(double curtime, bool frame)
                KX_GameObject  *obj = (KX_GameObject*) GetParent();
                MT_Point3    position = obj->NodeGetWorldPosition();
                MT_Point3    newposition;
-               MT_Vector3   direction;
+               MT_Vector3   direction, refDirection;
                MT_Matrix3x3 rotation = obj->NodeGetWorldOrientation();
-               MT_Scalar    filter, newdistance;
+               MT_Scalar    filter, newdistance, cosangle;
                int axis, sign;
 
                if (m_posDampTime) {
@@ -185,11 +191,45 @@ bool KX_ConstraintActuator::Update(double curtime, bool frame)
                                axis = 2;
                                break;
                        }
+                       if ((m_maximumBound < (1.0f-FLT_EPSILON)) || (m_minimumBound < (1.0f-FLT_EPSILON))) {
+                               // reference direction needs to be evaluated
+                               // 1. get the cosine between current direction and target
+                               cosangle = direction.dot(m_refDirection);
+                               if (cosangle >= (m_maximumBound-FLT_EPSILON) && cosangle <= (m_minimumBound+FLT_EPSILON)) {
+                                       // no change to do
+                                       result = true;
+                                       goto CHECK_TIME;
+                               }
+                               // 2. define a new reference direction
+                               //    compute local axis with reference direction as X and
+                               //    Y in direction X refDirection plane
+                               MT_Vector3 zaxis = m_refDirection.cross(direction);
+                               if (MT_fuzzyZero2(zaxis.length2())) {
+                                       // direction and refDirection are identical,
+                                       // choose any other direction to define plane
+                                       if (direction[0] < 0.9999)
+                                               zaxis = m_refDirection.cross(MT_Vector3(1.0,0.0,0.0));
+                                       else
+                                               zaxis = m_refDirection.cross(MT_Vector3(0.0,1.0,0.0));
+                               }
+                               MT_Vector3 yaxis = zaxis.cross(m_refDirection);
+                               yaxis.normalize();
+                               if (cosangle > m_minimumBound) {
+                                       // angle is too close to reference direction,
+                                       // choose a new reference that is exactly at minimum angle
+                                       refDirection = m_minimumBound * m_refDirection + m_minimumSine * yaxis;
+                               } else {
+                                       // angle is too large, choose new reference direction at maximum angle
+                                       refDirection = m_maximumBound * m_refDirection + m_maximumSine * yaxis;
+                               }
+                       } else {
+                               refDirection = m_refDirection;
+                       }
                        if (m_posDampTime) {
                                // apply damping on the direction
-                               direction = filter*direction + (1.0-filter)*m_refDirection;
+                               direction = filter*direction + (1.0-filter)*refDirection;
                        } else {
-                               direction = m_refDirection;
+                               direction = refDirection;
                        }
                        obj->AlignAxisToVect(direction, axis);
                        result = true;
index 5a1d4d23217551b4db9c36f1a2e794aa14694e0f..3ef515950988216306a4f5dea3a7f79ee3a5a230 100644 (file)
@@ -48,6 +48,10 @@ protected:
        float m_minimumBound;
        // max (float)
        float m_maximumBound;
+       // sinus of minimum angle
+       float m_minimumSine;
+       // sinus of maximum angle
+       float m_maximumSine;
        // reference direction
        MT_Vector3 m_refDirection;
        // locrotxyz choice (pick one): only one choice allowed at a time!