Blender V2.61 - r43446

KX_SteeringActuator.cpp

Go to the documentation of this file.
00001 /*
00002  * Add steering behaviors
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 
00031 #include "BLI_math.h"
00032 #include "KX_SteeringActuator.h"
00033 #include "KX_GameObject.h"
00034 #include "KX_NavMeshObject.h"
00035 #include "KX_ObstacleSimulation.h"
00036 #include "KX_PythonInit.h"
00037 #include "KX_PyMath.h"
00038 #include "Recast.h"
00039 
00040 /* ------------------------------------------------------------------------- */
00041 /* Native functions                                                          */
00042 /* ------------------------------------------------------------------------- */
00043 
00044 KX_SteeringActuator::KX_SteeringActuator(SCA_IObject *gameobj, 
00045                                          int mode,
00046                                          KX_GameObject *target,
00047                                          KX_GameObject *navmesh,
00048                                          float distance,
00049                                          float velocity,
00050                                          float acceleration,
00051                                          float turnspeed,
00052                                          bool  isSelfTerminated,
00053                                          int pathUpdatePeriod,
00054                                          KX_ObstacleSimulation* simulation,
00055                                          short facingmode,
00056                                          bool normalup,
00057                                          bool enableVisualization)
00058     : SCA_IActuator(gameobj, KX_ACT_STEERING),
00059       m_target(target),
00060       m_mode(mode),
00061       m_distance(distance),
00062       m_velocity(velocity),
00063       m_acceleration(acceleration),
00064       m_turnspeed(turnspeed),
00065       m_simulation(simulation),
00066       m_updateTime(0),
00067       m_obstacle(NULL),
00068       m_isActive(false),
00069       m_isSelfTerminated(isSelfTerminated),
00070       m_enableVisualization(enableVisualization),
00071       m_facingMode(facingmode),
00072       m_normalUp(normalup),
00073       m_pathLen(0),
00074       m_pathUpdatePeriod(pathUpdatePeriod),
00075       m_wayPointIdx(-1),
00076       m_steerVec(MT_Vector3(0, 0, 0))
00077 {
00078     m_navmesh = static_cast<KX_NavMeshObject*>(navmesh);
00079     if (m_navmesh)
00080         m_navmesh->RegisterActuator(this);
00081     if (m_target)
00082         m_target->RegisterActuator(this);
00083     
00084     if (m_simulation)
00085         m_obstacle = m_simulation->GetObstacle((KX_GameObject*)gameobj);
00086     KX_GameObject* parent = ((KX_GameObject*)gameobj)->GetParent();
00087     if (m_facingMode>0 && parent)
00088     {
00089         m_parentlocalmat = parent->GetSGNode()->GetLocalOrientation();
00090     }
00091     else
00092         m_parentlocalmat.setIdentity();
00093 } 
00094 
00095 KX_SteeringActuator::~KX_SteeringActuator()
00096 {
00097     if (m_navmesh)
00098         m_navmesh->UnregisterActuator(this);
00099     if (m_target)
00100         m_target->UnregisterActuator(this);
00101 } 
00102 
00103 CValue* KX_SteeringActuator::GetReplica()
00104 {
00105     KX_SteeringActuator* replica = new KX_SteeringActuator(*this);
00106     // replication just copy the m_base pointer => common random generator
00107     replica->ProcessReplica();
00108     return replica;
00109 }
00110 
00111 void KX_SteeringActuator::ProcessReplica()
00112 {
00113     if (m_target)
00114         m_target->RegisterActuator(this);
00115     if (m_navmesh)
00116         m_navmesh->RegisterActuator(this);
00117     SCA_IActuator::ProcessReplica();
00118 }
00119 
00120 
00121 bool KX_SteeringActuator::UnlinkObject(SCA_IObject* clientobj)
00122 {
00123     if (clientobj == m_target)
00124     {
00125         m_target = NULL;
00126         return true;
00127     }
00128     else if (clientobj == m_navmesh)
00129     {
00130         m_navmesh = NULL;
00131         return true;
00132     }
00133     return false;
00134 }
00135 
00136 void KX_SteeringActuator::Relink(CTR_Map<CTR_HashedPtr, void*> *obj_map)
00137 {
00138     void **h_obj = (*obj_map)[m_target];
00139     if (h_obj) {
00140         if (m_target)
00141             m_target->UnregisterActuator(this);
00142         m_target = (KX_GameObject*)(*h_obj);
00143         m_target->RegisterActuator(this);
00144     }
00145 
00146     h_obj = (*obj_map)[m_navmesh];
00147     if (h_obj) {
00148         if (m_navmesh)
00149             m_navmesh->UnregisterActuator(this);
00150         m_navmesh = (KX_NavMeshObject*)(*h_obj);
00151         m_navmesh->RegisterActuator(this);
00152     }
00153 }
00154 
00155 bool KX_SteeringActuator::Update(double curtime, bool frame)
00156 {
00157     if (frame)
00158     {
00159         double delta =  curtime - m_updateTime;
00160         m_updateTime = curtime;
00161         
00162         if (m_posevent && !m_isActive)
00163         {
00164             delta = 0;
00165             m_pathUpdateTime = -1;
00166             m_updateTime = curtime;
00167             m_isActive = true;
00168         }
00169         bool bNegativeEvent = IsNegativeEvent();
00170         if (bNegativeEvent)
00171             m_isActive = false;
00172 
00173         RemoveAllEvents();
00174 
00175         if (!delta)
00176             return true;
00177 
00178         if (bNegativeEvent || !m_target)
00179             return false; // do nothing on negative events
00180 
00181         KX_GameObject *obj = (KX_GameObject*) GetParent();
00182         const MT_Point3& mypos = obj->NodeGetWorldPosition();
00183         const MT_Point3& targpos = m_target->NodeGetWorldPosition();
00184         MT_Vector3 vectotarg = targpos - mypos;
00185         MT_Vector3 vectotarg2d = vectotarg;
00186         vectotarg2d.z() = 0;
00187         m_steerVec = MT_Vector3(0, 0, 0);
00188         bool apply_steerforce = false;
00189         bool terminate = true;
00190 
00191         switch (m_mode) {
00192             case KX_STEERING_SEEK:
00193                 if (vectotarg2d.length2()>m_distance*m_distance)
00194                 {
00195                     terminate = false;
00196                     m_steerVec = vectotarg;
00197                     m_steerVec.normalize();
00198                     apply_steerforce = true;
00199                 }
00200                 break;
00201             case KX_STEERING_FLEE:
00202                 if (vectotarg2d.length2()<m_distance*m_distance)
00203                 {
00204                     terminate = false;
00205                     m_steerVec = -vectotarg;
00206                     m_steerVec.normalize();
00207                     apply_steerforce = true;
00208                 }
00209                 break;
00210             case KX_STEERING_PATHFOLLOWING:
00211                 if (m_navmesh && vectotarg.length2()>m_distance*m_distance)
00212                 {
00213                     terminate = false;
00214 
00215                     static const MT_Scalar WAYPOINT_RADIUS(0.25);
00216 
00217                     if (m_pathUpdateTime<0 || (m_pathUpdatePeriod>=0 && 
00218                                                 curtime - m_pathUpdateTime>((double)m_pathUpdatePeriod/1000)))
00219                     {
00220                         m_pathUpdateTime = curtime;
00221                         m_pathLen = m_navmesh->FindPath(mypos, targpos, m_path, MAX_PATH_LENGTH);
00222                         m_wayPointIdx = m_pathLen > 1 ? 1 : -1;
00223                     }
00224 
00225                     if (m_wayPointIdx>0)
00226                     {
00227                         MT_Vector3 waypoint(&m_path[3*m_wayPointIdx]);
00228                         if ((waypoint-mypos).length2()<WAYPOINT_RADIUS*WAYPOINT_RADIUS)
00229                         {
00230                             m_wayPointIdx++;
00231                             if (m_wayPointIdx>=m_pathLen)
00232                             {
00233                                 m_wayPointIdx = -1;
00234                                 terminate = true;
00235                             }
00236                             else
00237                                 waypoint.setValue(&m_path[3*m_wayPointIdx]);
00238                         }
00239 
00240                         m_steerVec = waypoint - mypos;
00241                         apply_steerforce = true;
00242 
00243                         
00244                         if (m_enableVisualization)
00245                         {
00246                             //debug draw
00247                             static const MT_Vector3 PATH_COLOR(1,0,0);
00248                             m_navmesh->DrawPath(m_path, m_pathLen, PATH_COLOR);
00249                         }
00250                     }   
00251                     
00252                 }
00253                 break;
00254         }
00255 
00256         if (apply_steerforce)
00257         {
00258             bool isdyna = obj->IsDynamic();
00259             if (isdyna)
00260                 m_steerVec.z() = 0;
00261             if (!m_steerVec.fuzzyZero())
00262                 m_steerVec.normalize();
00263             MT_Vector3 newvel = m_velocity*m_steerVec;
00264 
00265             //adjust velocity to avoid obstacles
00266             if (m_simulation && m_obstacle /*&& !newvel.fuzzyZero()*/)
00267             {
00268                 if (m_enableVisualization)
00269                     KX_RasterizerDrawDebugLine(mypos, mypos + newvel, MT_Vector3(1.,0.,0.));
00270                 m_simulation->AdjustObstacleVelocity(m_obstacle, m_mode!=KX_STEERING_PATHFOLLOWING ? m_navmesh : NULL, 
00271                                 newvel, m_acceleration*delta, m_turnspeed/180.0f*M_PI*delta);
00272                 if (m_enableVisualization)
00273                     KX_RasterizerDrawDebugLine(mypos, mypos + newvel, MT_Vector3(0.,1.,0.));
00274             }
00275 
00276             HandleActorFace(newvel);
00277             if (isdyna)
00278             {
00279                 //temporary solution: set 2D steering velocity directly to obj
00280                 //correct way is to apply physical force
00281                 MT_Vector3 curvel = obj->GetLinearVelocity();
00282                 newvel.z() = curvel.z();            
00283                 obj->setLinearVelocity(newvel, false);
00284             }
00285             else
00286             {
00287                 MT_Vector3 movement = delta*newvel;
00288                 obj->ApplyMovement(movement, false);
00289             }
00290         }
00291         else
00292         {
00293             if (m_simulation && m_obstacle)
00294             {
00295                 m_obstacle->dvel[0] = 0.f;
00296                 m_obstacle->dvel[1] = 0.f;
00297             }
00298             
00299         }
00300 
00301         if (terminate && m_isSelfTerminated)
00302             return false;
00303     }
00304 
00305     return true;
00306 }
00307 
00308 const MT_Vector3& KX_SteeringActuator::GetSteeringVec()
00309 {
00310     static MT_Vector3 ZERO_VECTOR(0, 0, 0);
00311     if (m_isActive)
00312         return m_steerVec;
00313     else
00314         return ZERO_VECTOR;
00315 }
00316 
00317 inline float vdot2(const float* a, const float* b)
00318 {
00319     return a[0]*b[0] + a[2]*b[2];
00320 }
00321 static bool barDistSqPointToTri(const float* p, const float* a, const float* b, const float* c)
00322 {
00323     float v0[3], v1[3], v2[3];
00324     rcVsub(v0, c,a);
00325     rcVsub(v1, b,a);
00326     rcVsub(v2, p,a);
00327 
00328     const float dot00 = vdot2(v0, v0);
00329     const float dot01 = vdot2(v0, v1);
00330     const float dot02 = vdot2(v0, v2);
00331     const float dot11 = vdot2(v1, v1);
00332     const float dot12 = vdot2(v1, v2);
00333 
00334     // Compute barycentric coordinates
00335     float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
00336     float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
00337     float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
00338 
00339     float ud = u<0.f ? -u : (u>1.f ? u-1.f : 0.f);
00340     float vd = v<0.f ? -v : (v>1.f ? v-1.f : 0.f);
00341     return ud*ud+vd*vd ;
00342 }
00343 
00344 inline void flipAxes(float* vec)
00345 {
00346     std::swap(vec[1],vec[2]);
00347 }
00348 
00349 static bool getNavmeshNormal(dtStatNavMesh* navmesh, const MT_Vector3& pos, MT_Vector3& normal)
00350 {
00351     static const float polyPickExt[3] = {2, 4, 2};
00352     float spos[3];
00353     pos.getValue(spos); 
00354     flipAxes(spos); 
00355     dtStatPolyRef sPolyRef = navmesh->findNearestPoly(spos, polyPickExt);
00356     if (sPolyRef == 0)
00357         return false;
00358     const dtStatPoly* p = navmesh->getPoly(sPolyRef-1);
00359     const dtStatPolyDetail* pd = navmesh->getPolyDetail(sPolyRef-1);
00360 
00361     float distMin = FLT_MAX;
00362     int idxMin = -1;
00363     for (int i = 0; i < pd->ntris; ++i)
00364     {
00365         const unsigned char* t = navmesh->getDetailTri(pd->tbase+i);
00366         const float* v[3];
00367         for (int j = 0; j < 3; ++j)
00368         {
00369             if (t[j] < p->nv)
00370                 v[j] = navmesh->getVertex(p->v[t[j]]);
00371             else
00372                 v[j] = navmesh->getDetailVertex(pd->vbase+(t[j]-p->nv));
00373         }
00374         float dist = barDistSqPointToTri(spos, v[0], v[1], v[2]);
00375         if (dist<distMin)
00376         {
00377             distMin = dist;
00378             idxMin = i;
00379         }           
00380     }
00381 
00382     if (idxMin>=0)
00383     {
00384         const unsigned char* t = navmesh->getDetailTri(pd->tbase+idxMin);
00385         const float* v[3];
00386         for (int j = 0; j < 3; ++j)
00387         {
00388             if (t[j] < p->nv)
00389                 v[j] = navmesh->getVertex(p->v[t[j]]);
00390             else
00391                 v[j] = navmesh->getDetailVertex(pd->vbase+(t[j]-p->nv));
00392         }
00393         MT_Vector3 tri[3];
00394         for (size_t j=0; j<3; j++)
00395             tri[j].setValue(v[j][0],v[j][2],v[j][1]);
00396         MT_Vector3 a,b;
00397         a = tri[1]-tri[0];
00398         b = tri[2]-tri[0];
00399         normal = b.cross(a).safe_normalized();
00400         return true;
00401     }
00402 
00403     return false;
00404 }
00405 
00406 void KX_SteeringActuator::HandleActorFace(MT_Vector3& velocity)
00407 {
00408     if (m_facingMode==0 && (!m_navmesh || !m_normalUp))
00409         return;
00410     KX_GameObject* curobj = (KX_GameObject*) GetParent();
00411     MT_Vector3 dir = m_facingMode==0 ?  curobj->NodeGetLocalOrientation().getColumn(1) : velocity;
00412     if (dir.fuzzyZero())
00413         return; 
00414     dir.normalize();
00415     MT_Vector3 up(0,0,1);
00416     MT_Vector3 left;
00417     MT_Matrix3x3 mat;
00418     
00419     if (m_navmesh && m_normalUp)
00420     {
00421         dtStatNavMesh* navmesh =  m_navmesh->GetNavMesh();
00422         MT_Vector3 normal;
00423         MT_Vector3 trpos = m_navmesh->TransformToLocalCoords(curobj->NodeGetWorldPosition());
00424         if (getNavmeshNormal(navmesh, trpos, normal))
00425         {
00426 
00427             left = (dir.cross(up)).safe_normalized();
00428             dir = (-left.cross(normal)).safe_normalized();          
00429             up = normal;
00430         }
00431     }
00432 
00433     switch (m_facingMode)
00434     {
00435     case 1: // TRACK X
00436         {
00437             left  = dir.safe_normalized();
00438             dir = -(left.cross(up)).safe_normalized();
00439             break;
00440         };
00441     case 2: // TRACK Y
00442         {
00443             left  = (dir.cross(up)).safe_normalized();
00444             break;
00445         }
00446 
00447     case 3: // track Z
00448         {
00449             left = up.safe_normalized();
00450             up = dir.safe_normalized();
00451             dir = left;
00452             left  = (dir.cross(up)).safe_normalized();
00453             break;
00454         }
00455 
00456     case 4: // TRACK -X
00457         {
00458             left  = -dir.safe_normalized();
00459             dir = -(left.cross(up)).safe_normalized();
00460             break;
00461         };
00462     case 5: // TRACK -Y
00463         {
00464             left  = (-dir.cross(up)).safe_normalized();
00465             dir = -dir;
00466             break;
00467         }
00468     case 6: // track -Z
00469         {
00470             left = up.safe_normalized();
00471             up = -dir.safe_normalized();
00472             dir = left;
00473             left  = (dir.cross(up)).safe_normalized();
00474             break;
00475         }
00476     }
00477 
00478     mat.setValue (
00479         left[0], dir[0],up[0], 
00480         left[1], dir[1],up[1],
00481         left[2], dir[2],up[2]
00482     );
00483 
00484     
00485     
00486     KX_GameObject* parentObject = curobj->GetParent();
00487     if(parentObject)
00488     { 
00489         MT_Point3 localpos;
00490         localpos = curobj->GetSGNode()->GetLocalPosition();
00491         MT_Matrix3x3 parentmatinv;
00492         parentmatinv = parentObject->NodeGetWorldOrientation ().inverse ();             
00493         mat = parentmatinv * mat;
00494         mat = m_parentlocalmat * mat;
00495         curobj->NodeSetLocalOrientation(mat);
00496         curobj->NodeSetLocalPosition(localpos);
00497     }
00498     else
00499     {
00500         curobj->NodeSetLocalOrientation(mat);
00501     }
00502 
00503 }
00504 
00505 #ifndef DISABLE_PYTHON
00506 
00507 /* ------------------------------------------------------------------------- */
00508 /* Python functions                                                          */
00509 /* ------------------------------------------------------------------------- */
00510 
00511 /* Integration hooks ------------------------------------------------------- */
00512 PyTypeObject KX_SteeringActuator::Type = {
00513     PyVarObject_HEAD_INIT(NULL, 0)
00514     "KX_SteeringActuator",
00515     sizeof(PyObjectPlus_Proxy),
00516     0,
00517     py_base_dealloc,
00518     0,
00519     0,
00520     0,
00521     0,
00522     py_base_repr,
00523     0,0,0,0,0,0,0,0,0,
00524     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
00525     0,0,0,0,0,0,0,
00526     Methods,
00527     0,
00528     0,
00529     &SCA_IActuator::Type,
00530     0,0,0,0,0,0,
00531     py_base_new
00532 };
00533 
00534 PyMethodDef KX_SteeringActuator::Methods[] = {
00535     {NULL,NULL} //Sentinel
00536 };
00537 
00538 PyAttributeDef KX_SteeringActuator::Attributes[] = {
00539     KX_PYATTRIBUTE_INT_RW("behaviour", KX_STEERING_NODEF+1, KX_STEERING_MAX-1, true, KX_SteeringActuator, m_mode),
00540     KX_PYATTRIBUTE_RW_FUNCTION("target", KX_SteeringActuator, pyattr_get_target, pyattr_set_target),
00541     KX_PYATTRIBUTE_RW_FUNCTION("navmesh", KX_SteeringActuator, pyattr_get_navmesh, pyattr_set_navmesh),
00542     KX_PYATTRIBUTE_FLOAT_RW("distance", 0.0f, 1000.0f, KX_SteeringActuator, m_distance),
00543     KX_PYATTRIBUTE_FLOAT_RW("velocity", 0.0f, 1000.0f, KX_SteeringActuator, m_velocity),
00544     KX_PYATTRIBUTE_FLOAT_RW("acceleration", 0.0f, 1000.0f, KX_SteeringActuator, m_acceleration),
00545     KX_PYATTRIBUTE_FLOAT_RW("turnspeed", 0.0f, 720.0f, KX_SteeringActuator, m_turnspeed),
00546     KX_PYATTRIBUTE_BOOL_RW("selfterminated", KX_SteeringActuator, m_isSelfTerminated),
00547     KX_PYATTRIBUTE_BOOL_RW("enableVisualization", KX_SteeringActuator, m_enableVisualization),
00548     KX_PYATTRIBUTE_RO_FUNCTION("steeringVec", KX_SteeringActuator, pyattr_get_steeringVec), 
00549     KX_PYATTRIBUTE_SHORT_RW("facingMode", 0, 6, true, KX_SteeringActuator, m_facingMode),
00550     KX_PYATTRIBUTE_INT_RW("pathUpdatePeriod", -1, 100000, true, KX_SteeringActuator, m_pathUpdatePeriod),
00551     { NULL }    //Sentinel
00552 };
00553 
00554 PyObject* KX_SteeringActuator::pyattr_get_target(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
00555 {
00556     KX_SteeringActuator* actuator = static_cast<KX_SteeringActuator*>(self);
00557     if (!actuator->m_target)    
00558         Py_RETURN_NONE;
00559     else
00560         return actuator->m_target->GetProxy();
00561 }
00562 
00563 int KX_SteeringActuator::pyattr_set_target(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
00564 {
00565     KX_SteeringActuator* actuator = static_cast<KX_SteeringActuator*>(self);
00566     KX_GameObject *gameobj;
00567 
00568     if (!ConvertPythonToGameObject(value, &gameobj, true, "actuator.object = value: KX_SteeringActuator"))
00569         return PY_SET_ATTR_FAIL; // ConvertPythonToGameObject sets the error
00570 
00571     if (actuator->m_target != NULL)
00572         actuator->m_target->UnregisterActuator(actuator);   
00573 
00574     actuator->m_target = (KX_GameObject*) gameobj;
00575 
00576     if (actuator->m_target)
00577         actuator->m_target->RegisterActuator(actuator);
00578 
00579     return PY_SET_ATTR_SUCCESS;
00580 }
00581 
00582 PyObject* KX_SteeringActuator::pyattr_get_navmesh(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
00583 {
00584     KX_SteeringActuator* actuator = static_cast<KX_SteeringActuator*>(self);
00585     if (!actuator->m_navmesh)   
00586         Py_RETURN_NONE;
00587     else
00588         return actuator->m_navmesh->GetProxy();
00589 }
00590 
00591 int KX_SteeringActuator::pyattr_set_navmesh(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
00592 {
00593     KX_SteeringActuator* actuator = static_cast<KX_SteeringActuator*>(self);
00594     KX_GameObject *gameobj;
00595 
00596     if (!ConvertPythonToGameObject(value, &gameobj, true, "actuator.object = value: KX_SteeringActuator"))
00597         return PY_SET_ATTR_FAIL; // ConvertPythonToGameObject sets the error
00598 
00599     if (!PyObject_TypeCheck(value, &KX_NavMeshObject::Type))
00600     {
00601         PyErr_Format(PyExc_TypeError, "KX_NavMeshObject is expected");
00602         return PY_SET_ATTR_FAIL;
00603     }
00604 
00605     if (actuator->m_navmesh != NULL)
00606         actuator->m_navmesh->UnregisterActuator(actuator);  
00607 
00608     actuator->m_navmesh = static_cast<KX_NavMeshObject*>(gameobj);
00609 
00610     if (actuator->m_navmesh)
00611         actuator->m_navmesh->RegisterActuator(actuator);
00612 
00613     return PY_SET_ATTR_SUCCESS;
00614 }
00615 
00616 PyObject* KX_SteeringActuator::pyattr_get_steeringVec(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
00617 {
00618     KX_SteeringActuator* actuator = static_cast<KX_SteeringActuator*>(self);
00619     const MT_Vector3& steeringVec = actuator->GetSteeringVec();
00620     return PyObjectFrom(steeringVec);
00621 }
00622 
00623 #endif // DISABLE_PYTHON
00624 
00625 /* eof */
00626