Blender V2.61 - r43446

KX_ConstraintActuator.cpp

Go to the documentation of this file.
00001 /*
00002  * Apply a constraint to a position or rotation value
00003  *
00004  *
00005  * ***** BEGIN GPL LICENSE BLOCK *****
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software Foundation,
00019  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
00022  * All rights reserved.
00023  *
00024  * The Original Code is: all of this file.
00025  *
00026  * Contributor(s): none yet.
00027  *
00028  * ***** END GPL LICENSE BLOCK *****
00029  */
00030 
00036 #include "SCA_IActuator.h"
00037 #include "KX_ConstraintActuator.h"
00038 #include "SCA_IObject.h"
00039 #include "MT_Point3.h"
00040 #include "MT_Matrix3x3.h"
00041 #include "KX_GameObject.h"
00042 #include "KX_RayCast.h"
00043 #include "KX_PythonInit.h" // KX_GetActiveScene
00044 
00045 #include <stdio.h>
00046 
00047 /* ------------------------------------------------------------------------- */
00048 /* Native functions                                                          */
00049 /* ------------------------------------------------------------------------- */
00050 
00051 KX_ConstraintActuator::KX_ConstraintActuator(SCA_IObject *gameobj, 
00052                                              int posDampTime,
00053                                              int rotDampTime,
00054                                              float minBound,
00055                                              float maxBound,
00056                                              float refDir[3],
00057                                              int locrotxyz,
00058                                              int time,
00059                                              int option,
00060                                              char *property) :
00061     SCA_IActuator(gameobj, KX_ACT_CONSTRAINT),
00062     m_refDirVector(refDir),
00063     m_currentTime(0)
00064 {
00065     m_refDirection[0] = refDir[0];
00066     m_refDirection[1] = refDir[1];
00067     m_refDirection[2] = refDir[2];
00068     m_posDampTime = posDampTime;
00069     m_rotDampTime = rotDampTime;
00070     m_locrot   = locrotxyz;
00071     m_option = option;
00072     m_activeTime = time;
00073     if (property) {
00074         m_property = property;
00075     } else {
00076         m_property = "";
00077     }
00078     /* The units of bounds are determined by the type of constraint. To      */
00079     /* make the constraint application easier and more transparent later on, */
00080     /* I think converting the bounds to the applicable domain makes more     */
00081     /* sense.                                                                */
00082     switch (m_locrot) {
00083     case KX_ACT_CONSTRAINT_ORIX:
00084     case KX_ACT_CONSTRAINT_ORIY:
00085     case KX_ACT_CONSTRAINT_ORIZ:
00086         {
00087             MT_Scalar len = m_refDirVector.length();
00088             if (MT_fuzzyZero(len)) {
00089                 // missing a valid direction
00090                 std::cout << "WARNING: Constraint actuator " << GetName() << ":  There is no valid reference direction!" << std::endl;
00091                 m_locrot = KX_ACT_CONSTRAINT_NODEF;
00092             } else {
00093                 m_refDirection[0] /= len;
00094                 m_refDirection[1] /= len;
00095                 m_refDirection[2] /= len;
00096                 m_refDirVector /= len;
00097             }
00098             m_minimumBound = cos(minBound);
00099             m_maximumBound = cos(maxBound);
00100             m_minimumSine = sin(minBound);
00101             m_maximumSine = sin(maxBound);
00102         }
00103         break;
00104     default:
00105         m_minimumBound = minBound;
00106         m_maximumBound = maxBound;
00107         m_minimumSine = 0.f;
00108         m_maximumSine = 0.f;
00109         break;
00110     }
00111 
00112 } /* End of constructor */
00113 
00114 KX_ConstraintActuator::~KX_ConstraintActuator()
00115 { 
00116     // there's nothing to be done here, really....
00117 } /* end of destructor */
00118 
00119 bool KX_ConstraintActuator::RayHit(KX_ClientObjectInfo* client, KX_RayCast* result, void * const data)
00120 {
00121 
00122     m_hitObject = client->m_gameobject;
00123     
00124     bool bFound = false;
00125 
00126     if (m_property.IsEmpty())
00127     {
00128         bFound = true;
00129     }
00130     else
00131     {
00132         if (m_option & KX_ACT_CONSTRAINT_MATERIAL)
00133         {
00134             if (client->m_auxilary_info)
00135             {
00136                 bFound = !strcmp(m_property.Ptr(), ((char*)client->m_auxilary_info));
00137             }
00138         }
00139         else
00140         {
00141             bFound = m_hitObject->GetProperty(m_property) != NULL;
00142         }
00143     }
00144     // update the hit status
00145     result->m_hitFound = bFound;
00146     // stop looking
00147     return true;
00148 }
00149 
00150 /* this function is used to pre-filter the object before casting the ray on them.
00151    This is useful for "X-Ray" option when we want to see "through" unwanted object.
00152  */
00153 bool KX_ConstraintActuator::NeedRayCast(KX_ClientObjectInfo* client)
00154 {
00155     if (client->m_type > KX_ClientObjectInfo::ACTOR)
00156     {
00157         // Unknown type of object, skip it.
00158         // Should not occur as the sensor objects are filtered in RayTest()
00159         printf("Invalid client type %d found in ray casting\n", client->m_type);
00160         return false;
00161     }
00162     // no X-Ray function yet
00163     return true;
00164 }
00165 
00166 bool KX_ConstraintActuator::Update(double curtime, bool frame)
00167 {
00168 
00169     bool result = false;    
00170     bool bNegativeEvent = IsNegativeEvent();
00171     RemoveAllEvents();
00172 
00173     if (!bNegativeEvent) {
00174         /* Constraint clamps the values to the specified range, with a sort of    */
00175         /* low-pass filtered time response, if the damp time is unequal to 0.     */
00176 
00177         /* Having to retrieve location/rotation and setting it afterwards may not */
00178         /* be efficient enough... Somthing to look at later.                      */
00179         KX_GameObject  *obj = (KX_GameObject*) GetParent();
00180         MT_Point3    position = obj->NodeGetWorldPosition();
00181         MT_Point3    newposition;
00182         MT_Vector3   normal, direction, refDirection;
00183         MT_Matrix3x3 rotation = obj->NodeGetWorldOrientation();
00184         MT_Scalar    filter, newdistance, cosangle;
00185         int axis, sign;
00186 
00187         if (m_posDampTime) {
00188             filter = m_posDampTime/(1.0+m_posDampTime);
00189         } else {
00190             filter = 0.0;
00191         }
00192         switch (m_locrot) {
00193         case KX_ACT_CONSTRAINT_ORIX:
00194         case KX_ACT_CONSTRAINT_ORIY:
00195         case KX_ACT_CONSTRAINT_ORIZ:
00196             switch (m_locrot) {
00197             case KX_ACT_CONSTRAINT_ORIX:
00198                 direction[0] = rotation[0][0];
00199                 direction[1] = rotation[1][0];
00200                 direction[2] = rotation[2][0];
00201                 axis = 0;
00202                 break;
00203             case KX_ACT_CONSTRAINT_ORIY:
00204                 direction[0] = rotation[0][1];
00205                 direction[1] = rotation[1][1];
00206                 direction[2] = rotation[2][1];
00207                 axis = 1;
00208                 break;
00209             default:
00210                 direction[0] = rotation[0][2];
00211                 direction[1] = rotation[1][2];
00212                 direction[2] = rotation[2][2];
00213                 axis = 2;
00214                 break;
00215             }
00216             if ((m_maximumBound < (1.0f-FLT_EPSILON)) || (m_minimumBound < (1.0f-FLT_EPSILON))) {
00217                 // reference direction needs to be evaluated
00218                 // 1. get the cosine between current direction and target
00219                 cosangle = direction.dot(m_refDirVector);
00220                 if (cosangle >= (m_maximumBound-FLT_EPSILON) && cosangle <= (m_minimumBound+FLT_EPSILON)) {
00221                     // no change to do
00222                     result = true;
00223                     goto CHECK_TIME;
00224                 }
00225                 // 2. define a new reference direction
00226                 //    compute local axis with reference direction as X and
00227                 //    Y in direction X refDirection plane
00228                 MT_Vector3 zaxis = m_refDirVector.cross(direction);
00229                 if (MT_fuzzyZero2(zaxis.length2())) {
00230                     // direction and refDirection are identical,
00231                     // choose any other direction to define plane
00232                     if (direction[0] < 0.9999)
00233                         zaxis = m_refDirVector.cross(MT_Vector3(1.0,0.0,0.0));
00234                     else
00235                         zaxis = m_refDirVector.cross(MT_Vector3(0.0,1.0,0.0));
00236                 }
00237                 MT_Vector3 yaxis = zaxis.cross(m_refDirVector);
00238                 yaxis.normalize();
00239                 if (cosangle > m_minimumBound) {
00240                     // angle is too close to reference direction,
00241                     // choose a new reference that is exactly at minimum angle
00242                     refDirection = m_minimumBound * m_refDirVector + m_minimumSine * yaxis;
00243                 } else {
00244                     // angle is too large, choose new reference direction at maximum angle
00245                     refDirection = m_maximumBound * m_refDirVector + m_maximumSine * yaxis;
00246                 }
00247             } else {
00248                 refDirection = m_refDirVector;
00249             }
00250             // apply damping on the direction
00251             direction = filter*direction + (1.0-filter)*refDirection;
00252             obj->AlignAxisToVect(direction, axis);
00253             result = true;
00254             goto CHECK_TIME;
00255         case KX_ACT_CONSTRAINT_DIRPX:
00256         case KX_ACT_CONSTRAINT_DIRPY:
00257         case KX_ACT_CONSTRAINT_DIRPZ:
00258         case KX_ACT_CONSTRAINT_DIRNX:
00259         case KX_ACT_CONSTRAINT_DIRNY:
00260         case KX_ACT_CONSTRAINT_DIRNZ:
00261             switch (m_locrot) {
00262             case KX_ACT_CONSTRAINT_DIRPX:
00263                 normal[0] = rotation[0][0];
00264                 normal[1] = rotation[1][0];
00265                 normal[2] = rotation[2][0];
00266                 axis = 0;       // axis according to KX_GameObject::AlignAxisToVect()
00267                 sign = 0;       // X axis will be parrallel to direction of ray
00268                 break;
00269             case KX_ACT_CONSTRAINT_DIRPY:
00270                 normal[0] = rotation[0][1];
00271                 normal[1] = rotation[1][1];
00272                 normal[2] = rotation[2][1];
00273                 axis = 1;
00274                 sign = 0;
00275                 break;
00276             case KX_ACT_CONSTRAINT_DIRPZ:
00277                 normal[0] = rotation[0][2];
00278                 normal[1] = rotation[1][2];
00279                 normal[2] = rotation[2][2];
00280                 axis = 2;
00281                 sign = 0;
00282                 break;
00283             case KX_ACT_CONSTRAINT_DIRNX:
00284                 normal[0] = -rotation[0][0];
00285                 normal[1] = -rotation[1][0];
00286                 normal[2] = -rotation[2][0];
00287                 axis = 0;
00288                 sign = 1;
00289                 break;
00290             case KX_ACT_CONSTRAINT_DIRNY:
00291                 normal[0] = -rotation[0][1];
00292                 normal[1] = -rotation[1][1];
00293                 normal[2] = -rotation[2][1];
00294                 axis = 1;
00295                 sign = 1;
00296                 break;
00297             case KX_ACT_CONSTRAINT_DIRNZ:
00298                 normal[0] = -rotation[0][2];
00299                 normal[1] = -rotation[1][2];
00300                 normal[2] = -rotation[2][2];
00301                 axis = 2;
00302                 sign = 1;
00303                 break;
00304             }
00305             normal.normalize();
00306             if (m_option & KX_ACT_CONSTRAINT_LOCAL) {
00307                 // direction of the ray is along the local axis
00308                 direction = normal;
00309             } else {
00310                 switch (m_locrot) {
00311                 case KX_ACT_CONSTRAINT_DIRPX:
00312                     direction = MT_Vector3(1.0,0.0,0.0);
00313                     break;
00314                 case KX_ACT_CONSTRAINT_DIRPY:
00315                     direction = MT_Vector3(0.0,1.0,0.0);
00316                     break;
00317                 case KX_ACT_CONSTRAINT_DIRPZ:
00318                     direction = MT_Vector3(0.0,0.0,1.0);
00319                     break;
00320                 case KX_ACT_CONSTRAINT_DIRNX:
00321                     direction = MT_Vector3(-1.0,0.0,0.0);
00322                     break;
00323                 case KX_ACT_CONSTRAINT_DIRNY:
00324                     direction = MT_Vector3(0.0,-1.0,0.0);
00325                     break;
00326                 case KX_ACT_CONSTRAINT_DIRNZ:
00327                     direction = MT_Vector3(0.0,0.0,-1.0);
00328                     break;
00329                 }
00330             }
00331             {
00332                 MT_Point3 topoint = position + (m_maximumBound) * direction;
00333                 PHY_IPhysicsEnvironment* pe = KX_GetActiveScene()->GetPhysicsEnvironment();
00334                 KX_IPhysicsController *spc = obj->GetPhysicsController();
00335 
00336                 if (!pe) {
00337                     std::cout << "WARNING: Constraint actuator " << GetName() << ":  There is no physics environment!" << std::endl;
00338                     goto CHECK_TIME;
00339                 }    
00340                 if (!spc) {
00341                     // the object is not physical, we probably want to avoid hitting its own parent
00342                     KX_GameObject *parent = obj->GetParent();
00343                     if (parent) {
00344                         spc = parent->GetPhysicsController();
00345                         parent->Release();
00346                     }
00347                 }
00348                 KX_RayCast::Callback<KX_ConstraintActuator> callback(this,spc);
00349                 result = KX_RayCast::RayTest(pe, position, topoint, callback);
00350                 if (result) {
00351                     MT_Vector3 newnormal = callback.m_hitNormal;
00352                     // compute new position & orientation
00353                     if ((m_option & (KX_ACT_CONSTRAINT_NORMAL|KX_ACT_CONSTRAINT_DISTANCE)) == 0) {
00354                         // if none option is set, the actuator does nothing but detect ray 
00355                         // (works like a sensor)
00356                         goto CHECK_TIME;
00357                     }
00358                     if (m_option & KX_ACT_CONSTRAINT_NORMAL) {
00359                         MT_Scalar rotFilter;
00360                         // apply damping on the direction
00361                         if (m_rotDampTime) {
00362                             rotFilter = m_rotDampTime/(1.0+m_rotDampTime);
00363                         } else {
00364                             rotFilter = filter;
00365                         }
00366                         newnormal = rotFilter*normal - (1.0-rotFilter)*newnormal;
00367                         obj->AlignAxisToVect((sign)?-newnormal:newnormal, axis);
00368                         if (m_option & KX_ACT_CONSTRAINT_LOCAL) {
00369                             direction = newnormal;
00370                             direction.normalize();
00371                         }
00372                     }
00373                     if (m_option & KX_ACT_CONSTRAINT_DISTANCE) {
00374                         if (m_posDampTime) {
00375                             newdistance = filter*(position-callback.m_hitPoint).length()+(1.0-filter)*m_minimumBound;
00376                         } else {
00377                             newdistance = m_minimumBound;
00378                         }
00379                         // logically we should cancel the speed along the ray direction as we set the
00380                         // position along that axis
00381                         spc = obj->GetPhysicsController();
00382                         if (spc && spc->IsDyna()) {
00383                             MT_Vector3 linV = spc->GetLinearVelocity();
00384                             // cancel the projection along the ray direction
00385                             MT_Scalar fallspeed = linV.dot(direction);
00386                             if (!MT_fuzzyZero(fallspeed))
00387                                 spc->SetLinearVelocity(linV-fallspeed*direction,false);
00388                         }
00389                     } else {
00390                         newdistance = (position-callback.m_hitPoint).length();
00391                     }
00392                     newposition = callback.m_hitPoint-newdistance*direction;
00393                 } else if (m_option & KX_ACT_CONSTRAINT_PERMANENT) {
00394                     // no contact but still keep running
00395                     result = true;
00396                     goto CHECK_TIME;
00397                 }
00398             }
00399             break; 
00400         case KX_ACT_CONSTRAINT_FHPX:
00401         case KX_ACT_CONSTRAINT_FHPY:
00402         case KX_ACT_CONSTRAINT_FHPZ:
00403         case KX_ACT_CONSTRAINT_FHNX:
00404         case KX_ACT_CONSTRAINT_FHNY:
00405         case KX_ACT_CONSTRAINT_FHNZ:
00406             switch (m_locrot) {
00407             case KX_ACT_CONSTRAINT_FHPX:
00408                 normal[0] = -rotation[0][0];
00409                 normal[1] = -rotation[1][0];
00410                 normal[2] = -rotation[2][0];
00411                 direction = MT_Vector3(1.0,0.0,0.0);
00412                 break;
00413             case KX_ACT_CONSTRAINT_FHPY:
00414                 normal[0] = -rotation[0][1];
00415                 normal[1] = -rotation[1][1];
00416                 normal[2] = -rotation[2][1];
00417                 direction = MT_Vector3(0.0,1.0,0.0);
00418                 break;
00419             case KX_ACT_CONSTRAINT_FHPZ:
00420                 normal[0] = -rotation[0][2];
00421                 normal[1] = -rotation[1][2];
00422                 normal[2] = -rotation[2][2];
00423                 direction = MT_Vector3(0.0,0.0,1.0);
00424                 break;
00425             case KX_ACT_CONSTRAINT_FHNX:
00426                 normal[0] = rotation[0][0];
00427                 normal[1] = rotation[1][0];
00428                 normal[2] = rotation[2][0];
00429                 direction = MT_Vector3(-1.0,0.0,0.0);
00430                 break;
00431             case KX_ACT_CONSTRAINT_FHNY:
00432                 normal[0] = rotation[0][1];
00433                 normal[1] = rotation[1][1];
00434                 normal[2] = rotation[2][1];
00435                 direction = MT_Vector3(0.0,-1.0,0.0);
00436                 break;
00437             case KX_ACT_CONSTRAINT_FHNZ:
00438                 normal[0] = rotation[0][2];
00439                 normal[1] = rotation[1][2];
00440                 normal[2] = rotation[2][2];
00441                 direction = MT_Vector3(0.0,0.0,-1.0);
00442                 break;
00443             }
00444             normal.normalize();
00445             {
00446                 PHY_IPhysicsEnvironment* pe = KX_GetActiveScene()->GetPhysicsEnvironment();
00447                 KX_IPhysicsController *spc = obj->GetPhysicsController();
00448 
00449                 if (!pe) {
00450                     std::cout << "WARNING: Constraint actuator " << GetName() << ":  There is no physics environment!" << std::endl;
00451                     goto CHECK_TIME;
00452                 }    
00453                 if (!spc || !spc->IsDyna()) {
00454                     // the object is not dynamic, it won't support setting speed
00455                     goto CHECK_TIME;
00456                 }
00457                 m_hitObject = NULL;
00458                 // distance of Fh area is stored in m_minimum
00459                 MT_Point3 topoint = position + (m_minimumBound+spc->GetRadius()) * direction;
00460                 KX_RayCast::Callback<KX_ConstraintActuator> callback(this,spc);
00461                 result = KX_RayCast::RayTest(pe, position, topoint, callback);
00462                 // we expect a hit object
00463                 if (!m_hitObject)
00464                     result = false;
00465                 if (result) 
00466                 {
00467                     MT_Vector3 newnormal = callback.m_hitNormal;
00468                     // compute new position & orientation
00469                     MT_Scalar distance = (callback.m_hitPoint-position).length()-spc->GetRadius(); 
00470                     // estimate the velocity of the hit point
00471                     MT_Point3 relativeHitPoint;
00472                     relativeHitPoint = (callback.m_hitPoint-m_hitObject->NodeGetWorldPosition());
00473                     MT_Vector3 velocityHitPoint = m_hitObject->GetVelocity(relativeHitPoint);
00474                     MT_Vector3 relativeVelocity = spc->GetLinearVelocity() - velocityHitPoint;
00475                     MT_Scalar relativeVelocityRay = direction.dot(relativeVelocity);
00476                     MT_Scalar springExtent = 1.0 - distance/m_minimumBound;
00477                     // Fh force is stored in m_maximum
00478                     MT_Scalar springForce = springExtent * m_maximumBound;
00479                     // damping is stored in m_refDirection [0] = damping, [1] = rot damping
00480                     MT_Scalar springDamp = relativeVelocityRay * m_refDirVector[0];
00481                     MT_Vector3 newVelocity = spc->GetLinearVelocity()-(springForce+springDamp)*direction;
00482                     if (m_option & KX_ACT_CONSTRAINT_NORMAL)
00483                     {
00484                         newVelocity+=(springForce+springDamp)*(newnormal-newnormal.dot(direction)*direction);
00485                     }
00486                     spc->SetLinearVelocity(newVelocity, false);
00487                     if (m_option & KX_ACT_CONSTRAINT_DOROTFH)
00488                     {
00489                         MT_Vector3 angSpring = (normal.cross(newnormal))*m_maximumBound;
00490                         MT_Vector3 angVelocity = spc->GetAngularVelocity();
00491                         // remove component that is parallel to normal
00492                         angVelocity -= angVelocity.dot(newnormal)*newnormal;
00493                         MT_Vector3 angDamp = angVelocity * ((m_refDirVector[1]>MT_EPSILON)?m_refDirVector[1]:m_refDirVector[0]);
00494                         spc->SetAngularVelocity(spc->GetAngularVelocity()+(angSpring-angDamp), false);
00495                     }
00496                 } else if (m_option & KX_ACT_CONSTRAINT_PERMANENT) {
00497                     // no contact but still keep running
00498                     result = true;
00499                 }
00500                 // don't set the position with this constraint
00501                 goto CHECK_TIME;
00502             }
00503             break; 
00504         case KX_ACT_CONSTRAINT_LOCX:
00505         case KX_ACT_CONSTRAINT_LOCY:
00506         case KX_ACT_CONSTRAINT_LOCZ:
00507             newposition = position = obj->GetSGNode()->GetLocalPosition();
00508             switch (m_locrot) {
00509             case KX_ACT_CONSTRAINT_LOCX:
00510                 Clamp(newposition[0], m_minimumBound, m_maximumBound);
00511                 break;
00512             case KX_ACT_CONSTRAINT_LOCY:
00513                 Clamp(newposition[1], m_minimumBound, m_maximumBound);
00514                 break;
00515             case KX_ACT_CONSTRAINT_LOCZ:
00516                 Clamp(newposition[2], m_minimumBound, m_maximumBound);
00517                 break;
00518             }
00519             result = true;
00520             if (m_posDampTime) {
00521                 newposition = filter*position + (1.0-filter)*newposition;
00522             }
00523             obj->NodeSetLocalPosition(newposition);
00524             goto CHECK_TIME;
00525         }
00526         if (result) {
00527             // set the new position but take into account parent if any
00528             obj->NodeSetWorldPosition(newposition);
00529         }
00530     CHECK_TIME:
00531         if (result && m_activeTime > 0 ) {
00532             if (++m_currentTime >= m_activeTime)
00533                 result = false;
00534         }
00535     }
00536     if (!result) {
00537         m_currentTime = 0;
00538     }
00539     return result;
00540 } /* end of KX_ConstraintActuator::Update(double curtime,double deltatime)   */
00541 
00542 void KX_ConstraintActuator::Clamp(MT_Scalar &var, 
00543                                   float min, 
00544                                   float max) {
00545     if (var < min) {
00546         var = min;
00547     } else if (var > max) {
00548         var = max;
00549     }
00550 }
00551 
00552 
00553 bool KX_ConstraintActuator::IsValidMode(KX_ConstraintActuator::KX_CONSTRAINTTYPE m) 
00554 {
00555     bool res = false;
00556 
00557     if ( (m > KX_ACT_CONSTRAINT_NODEF) && (m < KX_ACT_CONSTRAINT_MAX)) {
00558         res = true;
00559     }
00560 
00561     return res;
00562 }
00563 
00564 #ifdef WITH_PYTHON
00565 
00566 /* ------------------------------------------------------------------------- */
00567 /* Python functions                                                          */
00568 /* ------------------------------------------------------------------------- */
00569 
00570 /* Integration hooks ------------------------------------------------------- */
00571 PyTypeObject KX_ConstraintActuator::Type = {
00572     PyVarObject_HEAD_INIT(NULL, 0)
00573     "KX_ConstraintActuator",
00574     sizeof(PyObjectPlus_Proxy),
00575     0,
00576     py_base_dealloc,
00577     0,
00578     0,
00579     0,
00580     0,
00581     py_base_repr,
00582     0,0,0,0,0,0,0,0,0,
00583     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
00584     0,0,0,0,0,0,0,
00585     Methods,
00586     0,
00587     0,
00588     &SCA_IActuator::Type,
00589     0,0,0,0,0,0,
00590     py_base_new
00591 };
00592 
00593 PyMethodDef KX_ConstraintActuator::Methods[] = {
00594     {NULL,NULL} //Sentinel
00595 };
00596 
00597 PyAttributeDef KX_ConstraintActuator::Attributes[] = {
00598     KX_PYATTRIBUTE_INT_RW("damp",0,100,true,KX_ConstraintActuator,m_posDampTime),
00599     KX_PYATTRIBUTE_INT_RW("rotDamp",0,100,true,KX_ConstraintActuator,m_rotDampTime),
00600     KX_PYATTRIBUTE_FLOAT_ARRAY_RW_CHECK("direction",-FLT_MAX,FLT_MAX,KX_ConstraintActuator,m_refDirection,3,pyattr_check_direction),
00601     KX_PYATTRIBUTE_INT_RW("option",0,0xFFFF,false,KX_ConstraintActuator,m_option),
00602     KX_PYATTRIBUTE_INT_RW("time",0,1000,true,KX_ConstraintActuator,m_activeTime),
00603     KX_PYATTRIBUTE_STRING_RW("propName",0,MAX_PROP_NAME,true,KX_ConstraintActuator,m_property),
00604     KX_PYATTRIBUTE_FLOAT_RW("min",-FLT_MAX,FLT_MAX,KX_ConstraintActuator,m_minimumBound),
00605     KX_PYATTRIBUTE_FLOAT_RW("distance",-FLT_MAX,FLT_MAX,KX_ConstraintActuator,m_minimumBound),
00606     KX_PYATTRIBUTE_FLOAT_RW("max",-FLT_MAX,FLT_MAX,KX_ConstraintActuator,m_maximumBound),
00607     KX_PYATTRIBUTE_FLOAT_RW("rayLength",0,2000.f,KX_ConstraintActuator,m_maximumBound),
00608     KX_PYATTRIBUTE_INT_RW("limit",KX_ConstraintActuator::KX_ACT_CONSTRAINT_NODEF+1,KX_ConstraintActuator::KX_ACT_CONSTRAINT_MAX-1,false,KX_ConstraintActuator,m_locrot),
00609     { NULL }    //Sentinel
00610 };
00611 
00612 int KX_ConstraintActuator::pyattr_check_direction(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
00613 {
00614     KX_ConstraintActuator* act = static_cast<KX_ConstraintActuator*>(self);
00615     MT_Vector3 dir(act->m_refDirection);
00616     MT_Scalar len = dir.length();
00617     if (MT_fuzzyZero(len)) {
00618         PyErr_SetString(PyExc_ValueError, "actuator.direction = vec: KX_ConstraintActuator, invalid direction");
00619         return 1;
00620     }
00621     act->m_refDirVector = dir/len;
00622     return 0;   
00623 }
00624 
00625 #endif
00626 
00627 /* eof */