Blender V2.61 - r43446
|
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 */