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