doxygen: prevent GPL license block from being parsed as doxygen comment.
[blender.git] / source / gameengine / Ketsji / KX_ConstraintActuator.cpp
1 /*
2  * Apply a constraint to a position or rotation value
3  *
4  * $Id$
5  *
6  * ***** BEGIN GPL LICENSE BLOCK *****
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL LICENSE BLOCK *****
30  */
31
32 #include "SCA_IActuator.h"
33 #include "KX_ConstraintActuator.h"
34 #include "SCA_IObject.h"
35 #include "MT_Point3.h"
36 #include "MT_Matrix3x3.h"
37 #include "KX_GameObject.h"
38 #include "KX_RayCast.h"
39 #include "KX_PythonInit.h" // KX_GetActiveScene
40
41 #include <stdio.h>
42
43 /* ------------------------------------------------------------------------- */
44 /* Native functions                                                          */
45 /* ------------------------------------------------------------------------- */
46
47 KX_ConstraintActuator::KX_ConstraintActuator(SCA_IObject *gameobj, 
48                                                                                          int posDampTime,
49                                                                                          int rotDampTime,
50                                                                                          float minBound,
51                                                                                          float maxBound,
52                                                                                          float refDir[3],
53                                                                                          int locrotxyz,
54                                                                                          int time,
55                                                                                          int option,
56                                                                                          char *property) :
57         SCA_IActuator(gameobj, KX_ACT_CONSTRAINT),
58         m_refDirVector(refDir),
59         m_currentTime(0)
60 {
61         m_refDirection[0] = refDir[0];
62         m_refDirection[1] = refDir[1];
63         m_refDirection[2] = refDir[2];
64         m_posDampTime = posDampTime;
65         m_rotDampTime = rotDampTime;
66         m_locrot   = locrotxyz;
67         m_option = option;
68         m_activeTime = time;
69         if (property) {
70                 m_property = property;
71         } else {
72                 m_property = "";
73         }
74         /* The units of bounds are determined by the type of constraint. To      */
75         /* make the constraint application easier and more transparent later on, */
76         /* I think converting the bounds to the applicable domain makes more     */
77         /* sense.                                                                */
78         switch (m_locrot) {
79         case KX_ACT_CONSTRAINT_ORIX:
80         case KX_ACT_CONSTRAINT_ORIY:
81         case KX_ACT_CONSTRAINT_ORIZ:
82                 {
83                         MT_Scalar len = m_refDirVector.length();
84                         if (MT_fuzzyZero(len)) {
85                                 // missing a valid direction
86                                 std::cout << "WARNING: Constraint actuator " << GetName() << ":  There is no valid reference direction!" << std::endl;
87                                 m_locrot = KX_ACT_CONSTRAINT_NODEF;
88                         } else {
89                                 m_refDirection[0] /= len;
90                                 m_refDirection[1] /= len;
91                                 m_refDirection[2] /= len;
92                                 m_refDirVector /= len;
93                         }
94                         m_minimumBound = cos(minBound);
95                         m_maximumBound = cos(maxBound);
96                         m_minimumSine = sin(minBound);
97                         m_maximumSine = sin(maxBound);
98                 }
99                 break;
100         default:
101                 m_minimumBound = minBound;
102                 m_maximumBound = maxBound;
103                 m_minimumSine = 0.f;
104                 m_maximumSine = 0.f;
105                 break;
106         }
107
108 } /* End of constructor */
109
110 KX_ConstraintActuator::~KX_ConstraintActuator()
111
112         // there's nothing to be done here, really....
113 } /* end of destructor */
114
115 bool KX_ConstraintActuator::RayHit(KX_ClientObjectInfo* client, KX_RayCast* result, void * const data)
116 {
117
118         m_hitObject = client->m_gameobject;
119         
120         bool bFound = false;
121
122         if (m_property.IsEmpty())
123         {
124                 bFound = true;
125         }
126         else
127         {
128                 if (m_option & KX_ACT_CONSTRAINT_MATERIAL)
129                 {
130                         if (client->m_auxilary_info)
131                         {
132                                 bFound = !strcmp(m_property.Ptr(), ((char*)client->m_auxilary_info));
133                         }
134                 }
135                 else
136                 {
137                         bFound = m_hitObject->GetProperty(m_property) != NULL;
138                 }
139         }
140         // update the hit status
141         result->m_hitFound = bFound;
142         // stop looking
143         return true;
144 }
145
146 /* this function is used to pre-filter the object before casting the ray on them.
147    This is useful for "X-Ray" option when we want to see "through" unwanted object.
148  */
149 bool KX_ConstraintActuator::NeedRayCast(KX_ClientObjectInfo* client)
150 {
151         if (client->m_type > KX_ClientObjectInfo::ACTOR)
152         {
153                 // Unknown type of object, skip it.
154                 // Should not occur as the sensor objects are filtered in RayTest()
155                 printf("Invalid client type %d found in ray casting\n", client->m_type);
156                 return false;
157         }
158         // no X-Ray function yet
159         return true;
160 }
161
162 bool KX_ConstraintActuator::Update(double curtime, bool frame)
163 {
164
165         bool result = false;    
166         bool bNegativeEvent = IsNegativeEvent();
167         RemoveAllEvents();
168
169         if (!bNegativeEvent) {
170                 /* Constraint clamps the values to the specified range, with a sort of    */
171                 /* low-pass filtered time response, if the damp time is unequal to 0.     */
172
173                 /* Having to retrieve location/rotation and setting it afterwards may not */
174                 /* be efficient enough... Somthing to look at later.                      */
175                 KX_GameObject  *obj = (KX_GameObject*) GetParent();
176                 MT_Point3    position = obj->NodeGetWorldPosition();
177                 MT_Point3    newposition;
178                 MT_Vector3   normal, direction, refDirection;
179                 MT_Matrix3x3 rotation = obj->NodeGetWorldOrientation();
180                 MT_Scalar    filter, newdistance, cosangle;
181                 int axis, sign;
182
183                 if (m_posDampTime) {
184                         filter = m_posDampTime/(1.0+m_posDampTime);
185                 } else {
186                         filter = 0.0;
187                 }
188                 switch (m_locrot) {
189                 case KX_ACT_CONSTRAINT_ORIX:
190                 case KX_ACT_CONSTRAINT_ORIY:
191                 case KX_ACT_CONSTRAINT_ORIZ:
192                         switch (m_locrot) {
193                         case KX_ACT_CONSTRAINT_ORIX:
194                                 direction[0] = rotation[0][0];
195                                 direction[1] = rotation[1][0];
196                                 direction[2] = rotation[2][0];
197                                 axis = 0;
198                                 break;
199                         case KX_ACT_CONSTRAINT_ORIY:
200                                 direction[0] = rotation[0][1];
201                                 direction[1] = rotation[1][1];
202                                 direction[2] = rotation[2][1];
203                                 axis = 1;
204                                 break;
205                         default:
206                                 direction[0] = rotation[0][2];
207                                 direction[1] = rotation[1][2];
208                                 direction[2] = rotation[2][2];
209                                 axis = 2;
210                                 break;
211                         }
212                         if ((m_maximumBound < (1.0f-FLT_EPSILON)) || (m_minimumBound < (1.0f-FLT_EPSILON))) {
213                                 // reference direction needs to be evaluated
214                                 // 1. get the cosine between current direction and target
215                                 cosangle = direction.dot(m_refDirVector);
216                                 if (cosangle >= (m_maximumBound-FLT_EPSILON) && cosangle <= (m_minimumBound+FLT_EPSILON)) {
217                                         // no change to do
218                                         result = true;
219                                         goto CHECK_TIME;
220                                 }
221                                 // 2. define a new reference direction
222                                 //    compute local axis with reference direction as X and
223                                 //    Y in direction X refDirection plane
224                                 MT_Vector3 zaxis = m_refDirVector.cross(direction);
225                                 if (MT_fuzzyZero2(zaxis.length2())) {
226                                         // direction and refDirection are identical,
227                                         // choose any other direction to define plane
228                                         if (direction[0] < 0.9999)
229                                                 zaxis = m_refDirVector.cross(MT_Vector3(1.0,0.0,0.0));
230                                         else
231                                                 zaxis = m_refDirVector.cross(MT_Vector3(0.0,1.0,0.0));
232                                 }
233                                 MT_Vector3 yaxis = zaxis.cross(m_refDirVector);
234                                 yaxis.normalize();
235                                 if (cosangle > m_minimumBound) {
236                                         // angle is too close to reference direction,
237                                         // choose a new reference that is exactly at minimum angle
238                                         refDirection = m_minimumBound * m_refDirVector + m_minimumSine * yaxis;
239                                 } else {
240                                         // angle is too large, choose new reference direction at maximum angle
241                                         refDirection = m_maximumBound * m_refDirVector + m_maximumSine * yaxis;
242                                 }
243                         } else {
244                                 refDirection = m_refDirVector;
245                         }
246                         // apply damping on the direction
247                         direction = filter*direction + (1.0-filter)*refDirection;
248                         obj->AlignAxisToVect(direction, axis);
249                         result = true;
250                         goto CHECK_TIME;
251                 case KX_ACT_CONSTRAINT_DIRPX:
252                 case KX_ACT_CONSTRAINT_DIRPY:
253                 case KX_ACT_CONSTRAINT_DIRPZ:
254                 case KX_ACT_CONSTRAINT_DIRNX:
255                 case KX_ACT_CONSTRAINT_DIRNY:
256                 case KX_ACT_CONSTRAINT_DIRNZ:
257                         switch (m_locrot) {
258                         case KX_ACT_CONSTRAINT_DIRPX:
259                                 normal[0] = rotation[0][0];
260                                 normal[1] = rotation[1][0];
261                                 normal[2] = rotation[2][0];
262                                 axis = 0;               // axis according to KX_GameObject::AlignAxisToVect()
263                                 sign = 0;               // X axis will be parrallel to direction of ray
264                                 break;
265                         case KX_ACT_CONSTRAINT_DIRPY:
266                                 normal[0] = rotation[0][1];
267                                 normal[1] = rotation[1][1];
268                                 normal[2] = rotation[2][1];
269                                 axis = 1;
270                                 sign = 0;
271                                 break;
272                         case KX_ACT_CONSTRAINT_DIRPZ:
273                                 normal[0] = rotation[0][2];
274                                 normal[1] = rotation[1][2];
275                                 normal[2] = rotation[2][2];
276                                 axis = 2;
277                                 sign = 0;
278                                 break;
279                         case KX_ACT_CONSTRAINT_DIRNX:
280                                 normal[0] = -rotation[0][0];
281                                 normal[1] = -rotation[1][0];
282                                 normal[2] = -rotation[2][0];
283                                 axis = 0;
284                                 sign = 1;
285                                 break;
286                         case KX_ACT_CONSTRAINT_DIRNY:
287                                 normal[0] = -rotation[0][1];
288                                 normal[1] = -rotation[1][1];
289                                 normal[2] = -rotation[2][1];
290                                 axis = 1;
291                                 sign = 1;
292                                 break;
293                         case KX_ACT_CONSTRAINT_DIRNZ:
294                                 normal[0] = -rotation[0][2];
295                                 normal[1] = -rotation[1][2];
296                                 normal[2] = -rotation[2][2];
297                                 axis = 2;
298                                 sign = 1;
299                                 break;
300                         }
301                         normal.normalize();
302                         if (m_option & KX_ACT_CONSTRAINT_LOCAL) {
303                                 // direction of the ray is along the local axis
304                                 direction = normal;
305                         } else {
306                                 switch (m_locrot) {
307                                 case KX_ACT_CONSTRAINT_DIRPX:
308                                         direction = MT_Vector3(1.0,0.0,0.0);
309                                         break;
310                                 case KX_ACT_CONSTRAINT_DIRPY:
311                                         direction = MT_Vector3(0.0,1.0,0.0);
312                                         break;
313                                 case KX_ACT_CONSTRAINT_DIRPZ:
314                                         direction = MT_Vector3(0.0,0.0,1.0);
315                                         break;
316                                 case KX_ACT_CONSTRAINT_DIRNX:
317                                         direction = MT_Vector3(-1.0,0.0,0.0);
318                                         break;
319                                 case KX_ACT_CONSTRAINT_DIRNY:
320                                         direction = MT_Vector3(0.0,-1.0,0.0);
321                                         break;
322                                 case KX_ACT_CONSTRAINT_DIRNZ:
323                                         direction = MT_Vector3(0.0,0.0,-1.0);
324                                         break;
325                                 }
326                         }
327                         {
328                                 MT_Point3 topoint = position + (m_maximumBound) * direction;
329                                 PHY_IPhysicsEnvironment* pe = KX_GetActiveScene()->GetPhysicsEnvironment();
330                                 KX_IPhysicsController *spc = obj->GetPhysicsController();
331
332                                 if (!pe) {
333                                         std::cout << "WARNING: Constraint actuator " << GetName() << ":  There is no physics environment!" << std::endl;
334                                         goto CHECK_TIME;
335                                 }        
336                                 if (!spc) {
337                                         // the object is not physical, we probably want to avoid hitting its own parent
338                                         KX_GameObject *parent = obj->GetParent();
339                                         if (parent) {
340                                                 spc = parent->GetPhysicsController();
341                                                 parent->Release();
342                                         }
343                                 }
344                                 KX_RayCast::Callback<KX_ConstraintActuator> callback(this,spc);
345                                 result = KX_RayCast::RayTest(pe, position, topoint, callback);
346                                 if (result)     {
347                                         MT_Vector3 newnormal = callback.m_hitNormal;
348                                         // compute new position & orientation
349                                         if ((m_option & (KX_ACT_CONSTRAINT_NORMAL|KX_ACT_CONSTRAINT_DISTANCE)) == 0) {
350                                                 // if none option is set, the actuator does nothing but detect ray 
351                                                 // (works like a sensor)
352                                                 goto CHECK_TIME;
353                                         }
354                                         if (m_option & KX_ACT_CONSTRAINT_NORMAL) {
355                                                 MT_Scalar rotFilter;
356                                                 // apply damping on the direction
357                                                 if (m_rotDampTime) {
358                                                         rotFilter = m_rotDampTime/(1.0+m_rotDampTime);
359                                                 } else {
360                                                         rotFilter = filter;
361                                                 }
362                                                 newnormal = rotFilter*normal - (1.0-rotFilter)*newnormal;
363                                                 obj->AlignAxisToVect((sign)?-newnormal:newnormal, axis);
364                                                 if (m_option & KX_ACT_CONSTRAINT_LOCAL) {
365                                                         direction = newnormal;
366                                                         direction.normalize();
367                                                 }
368                                         }
369                                         if (m_option & KX_ACT_CONSTRAINT_DISTANCE) {
370                                                 if (m_posDampTime) {
371                                                         newdistance = filter*(position-callback.m_hitPoint).length()+(1.0-filter)*m_minimumBound;
372                                                 } else {
373                                                         newdistance = m_minimumBound;
374                                                 }
375                                                 // logically we should cancel the speed along the ray direction as we set the
376                                                 // position along that axis
377                                                 spc = obj->GetPhysicsController();
378                                                 if (spc && spc->IsDyna()) {
379                                                         MT_Vector3 linV = spc->GetLinearVelocity();
380                                                         // cancel the projection along the ray direction
381                                                         MT_Scalar fallspeed = linV.dot(direction);
382                                                         if (!MT_fuzzyZero(fallspeed))
383                                                                 spc->SetLinearVelocity(linV-fallspeed*direction,false);
384                                                 }
385                                         } else {
386                                                 newdistance = (position-callback.m_hitPoint).length();
387                                         }
388                                         newposition = callback.m_hitPoint-newdistance*direction;
389                                 } else if (m_option & KX_ACT_CONSTRAINT_PERMANENT) {
390                                         // no contact but still keep running
391                                         result = true;
392                                         goto CHECK_TIME;
393                                 }
394                         }
395                         break; 
396                 case KX_ACT_CONSTRAINT_FHPX:
397                 case KX_ACT_CONSTRAINT_FHPY:
398                 case KX_ACT_CONSTRAINT_FHPZ:
399                 case KX_ACT_CONSTRAINT_FHNX:
400                 case KX_ACT_CONSTRAINT_FHNY:
401                 case KX_ACT_CONSTRAINT_FHNZ:
402                         switch (m_locrot) {
403                         case KX_ACT_CONSTRAINT_FHPX:
404                                 normal[0] = -rotation[0][0];
405                                 normal[1] = -rotation[1][0];
406                                 normal[2] = -rotation[2][0];
407                                 direction = MT_Vector3(1.0,0.0,0.0);
408                                 break;
409                         case KX_ACT_CONSTRAINT_FHPY:
410                                 normal[0] = -rotation[0][1];
411                                 normal[1] = -rotation[1][1];
412                                 normal[2] = -rotation[2][1];
413                                 direction = MT_Vector3(0.0,1.0,0.0);
414                                 break;
415                         case KX_ACT_CONSTRAINT_FHPZ:
416                                 normal[0] = -rotation[0][2];
417                                 normal[1] = -rotation[1][2];
418                                 normal[2] = -rotation[2][2];
419                                 direction = MT_Vector3(0.0,0.0,1.0);
420                                 break;
421                         case KX_ACT_CONSTRAINT_FHNX:
422                                 normal[0] = rotation[0][0];
423                                 normal[1] = rotation[1][0];
424                                 normal[2] = rotation[2][0];
425                                 direction = MT_Vector3(-1.0,0.0,0.0);
426                                 break;
427                         case KX_ACT_CONSTRAINT_FHNY:
428                                 normal[0] = rotation[0][1];
429                                 normal[1] = rotation[1][1];
430                                 normal[2] = rotation[2][1];
431                                 direction = MT_Vector3(0.0,-1.0,0.0);
432                                 break;
433                         case KX_ACT_CONSTRAINT_FHNZ:
434                                 normal[0] = rotation[0][2];
435                                 normal[1] = rotation[1][2];
436                                 normal[2] = rotation[2][2];
437                                 direction = MT_Vector3(0.0,0.0,-1.0);
438                                 break;
439                         }
440                         normal.normalize();
441                         {
442                                 PHY_IPhysicsEnvironment* pe = KX_GetActiveScene()->GetPhysicsEnvironment();
443                                 KX_IPhysicsController *spc = obj->GetPhysicsController();
444
445                                 if (!pe) {
446                                         std::cout << "WARNING: Constraint actuator " << GetName() << ":  There is no physics environment!" << std::endl;
447                                         goto CHECK_TIME;
448                                 }        
449                                 if (!spc || !spc->IsDyna()) {
450                                         // the object is not dynamic, it won't support setting speed
451                                         goto CHECK_TIME;
452                                 }
453                                 m_hitObject = NULL;
454                                 // distance of Fh area is stored in m_minimum
455                                 MT_Point3 topoint = position + (m_minimumBound+spc->GetRadius()) * direction;
456                                 KX_RayCast::Callback<KX_ConstraintActuator> callback(this,spc);
457                                 result = KX_RayCast::RayTest(pe, position, topoint, callback);
458                                 // we expect a hit object
459                                 if (!m_hitObject)
460                                         result = false;
461                                 if (result)     
462                                 {
463                                         MT_Vector3 newnormal = callback.m_hitNormal;
464                                         // compute new position & orientation
465                                         MT_Scalar distance = (callback.m_hitPoint-position).length()-spc->GetRadius(); 
466                                         // estimate the velocity of the hit point
467                                         MT_Point3 relativeHitPoint;
468                                         relativeHitPoint = (callback.m_hitPoint-m_hitObject->NodeGetWorldPosition());
469                                         MT_Vector3 velocityHitPoint = m_hitObject->GetVelocity(relativeHitPoint);
470                                         MT_Vector3 relativeVelocity = spc->GetLinearVelocity() - velocityHitPoint;
471                                         MT_Scalar relativeVelocityRay = direction.dot(relativeVelocity);
472                                         MT_Scalar springExtent = 1.0 - distance/m_minimumBound;
473                                         // Fh force is stored in m_maximum
474                                         MT_Scalar springForce = springExtent * m_maximumBound;
475                                         // damping is stored in m_refDirection [0] = damping, [1] = rot damping
476                                         MT_Scalar springDamp = relativeVelocityRay * m_refDirVector[0];
477                                         MT_Vector3 newVelocity = spc->GetLinearVelocity()-(springForce+springDamp)*direction;
478                                         if (m_option & KX_ACT_CONSTRAINT_NORMAL)
479                                         {
480                                                 newVelocity+=(springForce+springDamp)*(newnormal-newnormal.dot(direction)*direction);
481                                         }
482                                         spc->SetLinearVelocity(newVelocity, false);
483                                         if (m_option & KX_ACT_CONSTRAINT_DOROTFH)
484                                         {
485                                                 MT_Vector3 angSpring = (normal.cross(newnormal))*m_maximumBound;
486                                                 MT_Vector3 angVelocity = spc->GetAngularVelocity();
487                                                 // remove component that is parallel to normal
488                                                 angVelocity -= angVelocity.dot(newnormal)*newnormal;
489                                                 MT_Vector3 angDamp = angVelocity * ((m_refDirVector[1]>MT_EPSILON)?m_refDirVector[1]:m_refDirVector[0]);
490                                                 spc->SetAngularVelocity(spc->GetAngularVelocity()+(angSpring-angDamp), false);
491                                         }
492                                 } else if (m_option & KX_ACT_CONSTRAINT_PERMANENT) {
493                                         // no contact but still keep running
494                                         result = true;
495                                 }
496                                 // don't set the position with this constraint
497                                 goto CHECK_TIME;
498                         }
499                         break; 
500                 case KX_ACT_CONSTRAINT_LOCX:
501                 case KX_ACT_CONSTRAINT_LOCY:
502                 case KX_ACT_CONSTRAINT_LOCZ:
503                         newposition = position = obj->GetSGNode()->GetLocalPosition();
504                         switch (m_locrot) {
505                         case KX_ACT_CONSTRAINT_LOCX:
506                                 Clamp(newposition[0], m_minimumBound, m_maximumBound);
507                                 break;
508                         case KX_ACT_CONSTRAINT_LOCY:
509                                 Clamp(newposition[1], m_minimumBound, m_maximumBound);
510                                 break;
511                         case KX_ACT_CONSTRAINT_LOCZ:
512                                 Clamp(newposition[2], m_minimumBound, m_maximumBound);
513                                 break;
514                         }
515                         result = true;
516                         if (m_posDampTime) {
517                                 newposition = filter*position + (1.0-filter)*newposition;
518                         }
519                         obj->NodeSetLocalPosition(newposition);
520                         goto CHECK_TIME;
521                 }
522                 if (result) {
523                         // set the new position but take into account parent if any
524                         obj->NodeSetWorldPosition(newposition);
525                 }
526         CHECK_TIME:
527                 if (result && m_activeTime > 0 ) {
528                         if (++m_currentTime >= m_activeTime)
529                                 result = false;
530                 }
531         }
532         if (!result) {
533                 m_currentTime = 0;
534         }
535         return result;
536 } /* end of KX_ConstraintActuator::Update(double curtime,double deltatime)   */
537
538 void KX_ConstraintActuator::Clamp(MT_Scalar &var, 
539                                                                   float min, 
540                                                                   float max) {
541         if (var < min) {
542                 var = min;
543         } else if (var > max) {
544                 var = max;
545         }
546 }
547
548
549 bool KX_ConstraintActuator::IsValidMode(KX_ConstraintActuator::KX_CONSTRAINTTYPE m) 
550 {
551         bool res = false;
552
553         if ( (m > KX_ACT_CONSTRAINT_NODEF) && (m < KX_ACT_CONSTRAINT_MAX)) {
554                 res = true;
555         }
556
557         return res;
558 }
559
560 #ifdef WITH_PYTHON
561
562 /* ------------------------------------------------------------------------- */
563 /* Python functions                                                          */
564 /* ------------------------------------------------------------------------- */
565
566 /* Integration hooks ------------------------------------------------------- */
567 PyTypeObject KX_ConstraintActuator::Type = {
568         PyVarObject_HEAD_INIT(NULL, 0)
569         "KX_ConstraintActuator",
570         sizeof(PyObjectPlus_Proxy),
571         0,
572         py_base_dealloc,
573         0,
574         0,
575         0,
576         0,
577         py_base_repr,
578         0,0,0,0,0,0,0,0,0,
579         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
580         0,0,0,0,0,0,0,
581         Methods,
582         0,
583         0,
584         &SCA_IActuator::Type,
585         0,0,0,0,0,0,
586         py_base_new
587 };
588
589 PyMethodDef KX_ConstraintActuator::Methods[] = {
590         {NULL,NULL} //Sentinel
591 };
592
593 PyAttributeDef KX_ConstraintActuator::Attributes[] = {
594         KX_PYATTRIBUTE_INT_RW("damp",0,100,true,KX_ConstraintActuator,m_posDampTime),
595         KX_PYATTRIBUTE_INT_RW("rotDamp",0,100,true,KX_ConstraintActuator,m_rotDampTime),
596         KX_PYATTRIBUTE_FLOAT_ARRAY_RW_CHECK("direction",-FLT_MAX,FLT_MAX,KX_ConstraintActuator,m_refDirection,3,pyattr_check_direction),
597         KX_PYATTRIBUTE_INT_RW("option",0,0xFFFF,false,KX_ConstraintActuator,m_option),
598         KX_PYATTRIBUTE_INT_RW("time",0,1000,true,KX_ConstraintActuator,m_activeTime),
599         KX_PYATTRIBUTE_STRING_RW("propName",0,32,true,KX_ConstraintActuator,m_property),
600         KX_PYATTRIBUTE_FLOAT_RW("min",-FLT_MAX,FLT_MAX,KX_ConstraintActuator,m_minimumBound),
601         KX_PYATTRIBUTE_FLOAT_RW("distance",-FLT_MAX,FLT_MAX,KX_ConstraintActuator,m_minimumBound),
602         KX_PYATTRIBUTE_FLOAT_RW("max",-FLT_MAX,FLT_MAX,KX_ConstraintActuator,m_maximumBound),
603         KX_PYATTRIBUTE_FLOAT_RW("rayLength",0,2000.f,KX_ConstraintActuator,m_maximumBound),
604         KX_PYATTRIBUTE_INT_RW("limit",KX_ConstraintActuator::KX_ACT_CONSTRAINT_NODEF+1,KX_ConstraintActuator::KX_ACT_CONSTRAINT_MAX-1,false,KX_ConstraintActuator,m_locrot),
605         { NULL }        //Sentinel
606 };
607
608 int KX_ConstraintActuator::pyattr_check_direction(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
609 {
610         KX_ConstraintActuator* act = static_cast<KX_ConstraintActuator*>(self);
611         MT_Vector3 dir(act->m_refDirection);
612         MT_Scalar len = dir.length();
613         if (MT_fuzzyZero(len)) {
614                 PyErr_SetString(PyExc_ValueError, "actuator.direction = vec: KX_ConstraintActuator, invalid direction");
615                 return 1;
616         }
617         act->m_refDirVector = dir/len;
618         return 0;       
619 }
620
621 #endif
622
623 /* eof */