Initial revision
[blender.git] / source / gameengine / Ketsji / KX_IpoActuator.cpp
1 /**
2  * Do Ipo stuff
3  *
4  * $Id$
5  *
6  * ***** BEGIN GPL/BL DUAL 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. The Blender
12  * Foundation also sells licenses for use in proprietary software under
13  * the Blender License.  See http://www.blender.org/BL/ for information
14  * about this.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  *
25  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
26  * All rights reserved.
27  *
28  * The Original Code is: all of this file.
29  *
30  * Contributor(s): none yet.
31  *
32  * ***** END GPL/BL DUAL LICENSE BLOCK *****
33  */
34
35 #include "KX_IpoActuator.h"
36 #include "KX_GameObject.h"
37
38
39 /* ------------------------------------------------------------------------- */
40 /* Type strings                                                              */
41 /* ------------------------------------------------------------------------- */
42
43 STR_String KX_IpoActuator::S_KX_ACT_IPO_PLAY_STRING      = "Play";
44 STR_String KX_IpoActuator::S_KX_ACT_IPO_PINGPONG_STRING  = "PingPong";
45 STR_String KX_IpoActuator::S_KX_ACT_IPO_FLIPPER_STRING   = "Flipper";
46 STR_String KX_IpoActuator::S_KX_ACT_IPO_LOOPSTOP_STRING  = "LoopStop";
47 STR_String KX_IpoActuator::S_KX_ACT_IPO_LOOPEND_STRING   = "LoopEnd";
48 STR_String KX_IpoActuator::S_KX_ACT_IPO_KEY2KEY_STRING   = "Key2key";
49 STR_String KX_IpoActuator::S_KX_ACT_IPO_FROM_PROP_STRING = "FromProp";
50
51 /* ------------------------------------------------------------------------- */
52 /* Native functions                                                          */
53 /* ------------------------------------------------------------------------- */
54 /** Another poltergeist? This seems to be a very transient class... */
55 class CIpoAction : public CAction
56 {
57         float           m_curtime;
58         bool            m_resurse;
59         KX_GameObject* m_gameobj;
60         bool        m_ipo_as_force;
61         bool        m_force_ipo_local;
62
63 public:
64         CIpoAction(KX_GameObject* gameobj,
65                 float curtime,
66                 bool recurse, 
67                 bool ipo_as_force,
68                 bool force_ipo_local) :
69           m_curtime(curtime) ,
70           m_resurse(recurse),
71           m_gameobj(gameobj),
72           m_ipo_as_force(ipo_as_force),
73           m_force_ipo_local(force_ipo_local) 
74           {
75                   /* intentionally empty */
76           };
77
78         virtual void Execute() const
79         {
80                 m_gameobj->UpdateIPO(
81                         m_curtime, 
82                         m_resurse, 
83                         m_ipo_as_force, 
84                         m_force_ipo_local);
85         };
86
87 };
88
89
90 KX_IpoActuator::KX_IpoActuator(SCA_IObject* gameobj,
91                                                            const STR_String& propname,
92                                                            float starttime,
93                                                            float endtime,
94                                                            bool recurse,
95                                                            int acttype,
96                                                            bool ipo_as_force,
97                                                            bool force_ipo_local,
98                                                            PyTypeObject* T) 
99         : SCA_IActuator(gameobj,T),
100         m_starttime (starttime),
101         m_endtime(endtime) ,
102         m_localtime(starttime),
103         m_recurse(recurse),
104         m_type((IpoActType)acttype) ,
105         m_direction(1),
106         m_bNegativeEvent(false),
107         m_propname(propname),
108         m_ipo_as_force(ipo_as_force),
109         m_force_ipo_local(force_ipo_local)
110 {
111         // intentionally empty
112 }
113
114 void KX_IpoActuator::SetStart(float starttime) 
115
116         m_starttime=starttime;
117 }
118
119 void KX_IpoActuator::SetEnd(float endtime) 
120
121         m_endtime=endtime;
122 }
123
124
125 bool KX_IpoActuator::Update(double curtime,double delta_time)
126 {
127         SCA_IActuator::Update(curtime,delta_time);
128         // result = true if animation has to be continued, false if animation stops
129         // maybe there are events for us in the queue !
130         
131
132         bool bNegativeEvent = false;
133         int numevents = m_events.size();
134
135         for (vector<CValue*>::iterator i=m_events.end(); !(i==m_events.begin());)
136         {
137                 i--;
138                 if ((*i)->GetNumber() == 0.0f)
139                 {
140                         int ka=0;
141                         bNegativeEvent = true;
142                 }
143                 (*i)->Release();
144                 m_events.pop_back();
145         }
146
147         if (bNegativeEvent)
148         {
149                 RemoveAllEvents();
150         }
151         
152
153         double  start_smaller_then_end = ( m_starttime < m_endtime ? 1.0 : -1.0);
154
155         double deltaframetime = start_smaller_then_end  * delta_time * KX_FIXED_FRAME_PER_SEC;
156
157         bool result=true;
158         
159         switch (m_type)
160         {
161                 
162         case KX_ACT_IPO_PLAY:
163                 {
164                         
165                         if (start_smaller_then_end > 0.0)
166                                 result = (m_localtime < m_endtime && !(m_localtime == m_starttime && bNegativeEvent));
167                         else
168                                 result = (m_localtime > m_endtime && !(m_localtime == m_starttime && bNegativeEvent));
169                         if (result)
170                         {
171                                 m_localtime += m_direction * deltaframetime;
172                                 
173                                 /* Perform clamping */
174                                 if ((m_localtime*start_smaller_then_end)>(m_endtime*start_smaller_then_end))
175                                         m_localtime=m_endtime;
176
177                                 CIpoAction ipoaction(
178                                         (KX_GameObject*)GetParent(), 
179                                         m_localtime, 
180                                         m_recurse, 
181                                         m_ipo_as_force,
182                                         m_force_ipo_local);
183                                 GetParent()->Execute(ipoaction);
184                         } else
185                         {
186                                 m_localtime=m_starttime;
187                                 m_direction=1;
188                         }
189                         break;
190                 }
191         case KX_ACT_IPO_PINGPONG:
192                 {
193                         result = true;
194                         if (bNegativeEvent && ((m_localtime == m_starttime )|| (m_localtime == m_endtime)))
195                         {
196                                 result = false;
197                         } else
198                         {
199                                 m_localtime += m_direction * deltaframetime;
200                         }
201                         
202                         if (m_localtime*start_smaller_then_end < m_starttime*start_smaller_then_end)
203                         {
204                                 m_localtime = m_starttime;
205                                 result = false;
206                                 m_direction = 1;
207                         }else
208                         if (m_localtime*start_smaller_then_end > m_endtime*start_smaller_then_end)
209                         {
210                                 m_localtime = m_endtime;
211                                 result = false;
212                                 m_direction = -1;
213                         } 
214                         
215                         CIpoAction ipoaction(
216                                 (KX_GameObject*) GetParent(),
217                                 m_localtime,
218                                 m_recurse, 
219                                 m_ipo_as_force,
220                                 m_force_ipo_local);
221                         GetParent()->Execute(ipoaction);
222                         break;
223                 }
224
225         case KX_ACT_IPO_FLIPPER:
226                 {
227                         result = true;
228                         if (numevents)
229                         {
230                                 if (bNegativeEvent)
231                                         m_direction = -1;
232                                 else
233                                         m_direction = 1;
234                         }
235
236                         m_localtime += m_direction * deltaframetime;
237
238                         if (m_localtime*start_smaller_then_end > m_endtime*start_smaller_then_end)
239                         {
240                                 m_localtime = m_endtime;
241                         } else
242                          if (m_localtime*start_smaller_then_end < m_starttime*start_smaller_then_end)
243                          {
244                                 m_localtime = m_starttime;
245                                 result = false;
246                          }
247                         
248                         CIpoAction ipoaction(
249                                 (KX_GameObject*) GetParent(),
250                                 m_localtime,
251                                 m_recurse,
252                                 m_ipo_as_force,
253                                 m_force_ipo_local);
254                         GetParent()->Execute(ipoaction);
255                         break;
256                 }
257
258                 case KX_ACT_IPO_LOOPSTOP:
259                 {
260                         if (numevents)
261                         {
262                                 if (bNegativeEvent)
263                                 {
264                                         result = false;
265                                         m_bNegativeEvent = false;
266                                         numevents = 0;
267                                 }
268                         } // fall through to loopend, and quit the ipo animation immediatly 
269                 }
270         
271                 case KX_ACT_IPO_LOOPEND:
272                         {
273                                 if (numevents){
274                                         if (bNegativeEvent){
275                                                 m_bNegativeEvent = true;
276                                         }
277                                 }
278                                 
279                                 if (bNegativeEvent && m_localtime == m_starttime){
280                                         result = false;
281                                 } 
282                                 else{
283                                         if (m_localtime*start_smaller_then_end < m_endtime*start_smaller_then_end){
284                                                 m_localtime += m_direction * deltaframetime;
285                                         }
286                                         else{
287                                                 if (!m_bNegativeEvent){
288                                                         /* Perform wraparound */
289                                                         float slop = m_localtime-m_endtime;
290                                                         float length = fabs(m_starttime-m_endtime);
291                                                         m_localtime = m_starttime + (slop - (int(slop/length)*(int(length))));
292                                                         
293                                                 }
294                                                 else
295                                                 {       
296                                                         /* Perform clamping */
297                                                         if ((m_localtime*start_smaller_then_end)>(m_endtime*start_smaller_then_end))
298                                                                 m_localtime=m_endtime;
299                                                         
300                                                         result = false;
301                                                         m_bNegativeEvent = false;
302                                                 }
303                                         }
304                                 }       
305                                 CIpoAction ipoaction(
306                                         (KX_GameObject*) GetParent(),
307                                         m_localtime,
308                                         m_recurse,
309                                         m_ipo_as_force,
310                                         m_force_ipo_local);
311                                 GetParent()->Execute(ipoaction);
312                                 break;
313                 }
314         case KX_ACT_IPO_KEY2KEY:
315                 {
316                         // not implemented yet
317                         result = false;
318                         break;
319                 }
320         case KX_ACT_IPO_FROM_PROP:
321                 {
322                         result = !bNegativeEvent;
323
324                          CValue* propval = GetParent()->GetProperty(m_propname);
325                          if (propval)
326                          {
327                                 m_localtime = propval->GetNumber();
328                                 CIpoAction ipoaction(
329                                         (KX_GameObject*) GetParent(),
330                                         m_localtime,
331                                         m_recurse,
332                                         m_ipo_as_force,
333                                         m_force_ipo_local);
334                                 GetParent()->Execute(ipoaction);
335
336                          } else
337                          {
338                                  result = false;
339                          }
340                         break;
341                 }
342                 
343         default:
344                 {
345                         result = false;
346                 }
347         }
348
349         return result;
350 }
351
352 KX_IpoActuator::IpoActType KX_IpoActuator::string2mode(char* modename) {
353         IpoActType res = KX_ACT_IPO_NODEF;
354
355         if (modename == S_KX_ACT_IPO_PLAY_STRING) { 
356                 res = KX_ACT_IPO_PLAY;
357         } else if (modename == S_KX_ACT_IPO_PINGPONG_STRING) {
358                 res = KX_ACT_IPO_PINGPONG;
359         } else if (modename == S_KX_ACT_IPO_FLIPPER_STRING) {
360                 res = KX_ACT_IPO_FLIPPER;
361         } else if (modename == S_KX_ACT_IPO_LOOPSTOP_STRING) {
362                 res = KX_ACT_IPO_LOOPSTOP;
363         } else if (modename == S_KX_ACT_IPO_LOOPEND_STRING) {
364                 res = KX_ACT_IPO_LOOPEND;
365         } else if (modename == S_KX_ACT_IPO_KEY2KEY_STRING) {
366                 res = KX_ACT_IPO_KEY2KEY;
367         } else if (modename == S_KX_ACT_IPO_FROM_PROP_STRING) {
368                 res = KX_ACT_IPO_FROM_PROP;
369         }
370
371         return res;
372 }
373
374 /* ------------------------------------------------------------------------- */
375 /* Python functions                                                          */
376 /* ------------------------------------------------------------------------- */
377
378
379
380 /* Integration hooks ------------------------------------------------------- */
381 PyTypeObject KX_IpoActuator::Type = {
382         PyObject_HEAD_INIT(&PyType_Type)
383         0,
384         "KX_IpoActuator",
385         sizeof(KX_IpoActuator),
386         0,
387         PyDestructor,
388         0,
389         __getattr,
390         __setattr,
391         0, //&MyPyCompare,
392         __repr,
393         0, //&cvalue_as_number,
394         0,
395         0,
396         0,
397         0
398 };
399
400 PyParentObject KX_IpoActuator::Parents[] = {
401         &KX_IpoActuator::Type,
402         &SCA_IActuator::Type,
403         &SCA_ILogicBrick::Type,
404         &CValue::Type,
405         NULL
406 };
407
408 PyMethodDef KX_IpoActuator::Methods[] = {
409         {"set", (PyCFunction) KX_IpoActuator::sPySet, 
410                 METH_VARARGS, Set_doc},
411         {"setProperty", (PyCFunction) KX_IpoActuator::sPySetProperty, 
412                 METH_VARARGS, SetProperty_doc},
413         {"setStart", (PyCFunction) KX_IpoActuator::sPySetStart, 
414                 METH_VARARGS, SetStart_doc},
415         {"getStart", (PyCFunction) KX_IpoActuator::sPyGetStart, 
416                 METH_VARARGS, GetStart_doc},
417         {"setEnd", (PyCFunction) KX_IpoActuator::sPySetEnd, 
418                 METH_VARARGS, SetEnd_doc},
419         {"getEnd", (PyCFunction) KX_IpoActuator::sPyGetEnd, 
420                 METH_VARARGS, GetEnd_doc},
421         {"setIpoAsForce", (PyCFunction) KX_IpoActuator::sPySetIpoAsForce, 
422                 METH_VARARGS, SetIpoAsForce_doc},
423         {"getIpoAsForce", (PyCFunction) KX_IpoActuator::sPyGetIpoAsForce, 
424                 METH_VARARGS, GetIpoAsForce_doc},
425         {"setType", (PyCFunction) KX_IpoActuator::sPySetType, 
426                 METH_VARARGS, SetType_doc},
427         {"getType", (PyCFunction) KX_IpoActuator::sPyGetType, 
428                 METH_VARARGS, GetType_doc},     
429         {NULL,NULL} //Sentinel
430 };
431
432 PyObject* KX_IpoActuator::_getattr(char* attr) {
433         _getattr_up(SCA_IActuator);
434 }
435
436
437
438 /* set --------------------------------------------------------------------- */
439 char KX_IpoActuator::Set_doc[] = 
440 "set(mode, startframe, endframe, force?)\n"
441 "\t - mode:       Play, PingPong, Flipper, LoopStop, LoopEnd or FromProp (string)\n"
442 "\t - startframe: first frame to use (int)\n"
443 "\t - endframe  : last frame to use (int)\n"
444 "\t - force?    : interpret this ipo as a force? (KX_TRUE, KX_FALSE)"
445 "\tSet the properties of the actuator.\n";
446 PyObject* KX_IpoActuator::PySet(PyObject* self, 
447                                                                 PyObject* args, 
448                                                                 PyObject* kwds) {
449         /* sets modes PLAY, PINGPONG, FLIPPER, LOOPSTOP, LOOPEND                 */
450         /* arg 1 = mode string, arg 2 = startframe, arg3 = stopframe,            */
451         /* arg4 = force toggle                                                   */
452         char* mode;
453         int forceToggle;
454         IpoActType modenum;
455         int startFrame, stopFrame;
456         if(!PyArg_ParseTuple(args, "siii", &mode, &startFrame, 
457                                                  &stopFrame, &forceToggle)) {
458                 return NULL;
459         }
460         modenum = string2mode(mode);
461         
462         switch (modenum) {
463         case KX_ACT_IPO_PLAY:
464         case KX_ACT_IPO_PINGPONG:
465         case KX_ACT_IPO_FLIPPER:
466         case KX_ACT_IPO_LOOPSTOP:
467         case KX_ACT_IPO_LOOPEND:
468                 m_type         = modenum;
469                 m_starttime    = startFrame;
470                 m_endtime      = stopFrame;
471                 m_ipo_as_force = PyArgToBool(forceToggle);
472                 break;
473         default:
474                 ; /* error */
475         }
476
477         Py_Return;
478 }
479
480 /* set property  ----------------------------------------------------------- */
481 char KX_IpoActuator::SetProperty_doc[] = 
482 "setProperty(propname)\n"
483 "\t - propname: name of the property (string)\n"
484 "\tSet the property to be used in FromProp mode.\n";
485 PyObject* KX_IpoActuator::PySetProperty(PyObject* self, 
486                                                                                 PyObject* args, 
487                                                                                 PyObject* kwds) {
488         /* mode is implicit here, but not supported yet... */
489         /* args: property */
490         char *propertyName;
491         if(!PyArg_ParseTuple(args, "s", &propertyName)) {
492                 return NULL;
493         }
494
495         Py_Return;
496 }
497
498 /* 4. setStart:                                                              */
499 char KX_IpoActuator::SetStart_doc[] = 
500 "setStart(frame)\n"
501 "\t - frame: first frame to use (int)\n"
502 "\tSet the frame from which the ipo starts playing.\n";
503 PyObject* KX_IpoActuator::PySetStart(PyObject* self, 
504                                                                          PyObject* args, 
505                                                                          PyObject* kwds) {
506         float startArg;
507         if(!PyArg_ParseTuple(args, "f", &startArg)) {
508                 return NULL;            
509         }
510         
511         m_starttime = startArg;
512
513         Py_Return;
514 }
515 /* 5. getStart:                                                              */
516 char KX_IpoActuator::GetStart_doc[] = 
517 "getStart()\n"
518 "\tReturns the frame from which the ipo starts playing.\n";
519 PyObject* KX_IpoActuator::PyGetStart(PyObject* self, 
520                                                                          PyObject* args, 
521                                                                          PyObject* kwds) {
522         return PyFloat_FromDouble(m_starttime);
523 }
524
525 /* 6. setEnd:                                                                */
526 char KX_IpoActuator::SetEnd_doc[] = 
527 "setEnd(frame)\n"
528 "\t - frame: last frame to use (int)\n"
529 "\tSet the frame at which the ipo stops playing.\n";
530 PyObject* KX_IpoActuator::PySetEnd(PyObject* self, 
531                                                                    PyObject* args, 
532                                                                    PyObject* kwds) {
533         float endArg;
534         if(!PyArg_ParseTuple(args, "f", &endArg)) {
535                 return NULL;            
536         }
537         
538         m_endtime = endArg;
539
540         Py_Return;
541 }
542 /* 7. getEnd:                                                                */
543 char KX_IpoActuator::GetEnd_doc[] = 
544 "getEnd()\n"
545 "\tReturns the frame at which the ipo stops playing.\n";
546 PyObject* KX_IpoActuator::PyGetEnd(PyObject* self, 
547                                                                    PyObject* args, 
548                                                                    PyObject* kwds) {
549         return PyFloat_FromDouble(m_endtime);
550 }
551
552 /* 6. setIpoAsForce:                                                           */
553 char KX_IpoActuator::SetIpoAsForce_doc[] = 
554 "setIpoAsForce(force?)\n"
555 "\t - force?    : interpret this ipo as a force? (KX_TRUE, KX_FALSE)\n"
556 "\tSet whether to interpret the ipo as a force rather than a displacement.\n";
557 PyObject* KX_IpoActuator::PySetIpoAsForce(PyObject* self, 
558                                                                                   PyObject* args, 
559                                                                                   PyObject* kwds) { 
560         int boolArg;
561         
562         if (!PyArg_ParseTuple(args, "i", &boolArg)) {
563                 return NULL;
564         }
565
566         m_ipo_as_force = PyArgToBool(boolArg);
567         
568         Py_Return;      
569 }
570 /* 7. getIpoAsForce:                                                         */
571 char KX_IpoActuator::GetIpoAsForce_doc[] = 
572 "getIpoAsForce()\n"
573 "\tReturns whether to interpret the ipo as a force rather than a displacement.\n";
574 PyObject* KX_IpoActuator::PyGetIpoAsForce(PyObject* self, 
575                                                                                   PyObject* args, 
576                                                                                   PyObject* kwds) {
577         return BoolToPyArg(m_ipo_as_force);
578 }
579
580 /* 8. setType:                                                               */
581 char KX_IpoActuator::SetType_doc[] = 
582 "setType(mode)\n"
583 "\t - mode: Play, PingPong, Flipper, LoopStop, LoopEnd or FromProp (string)\n"
584 "\tSet the operation mode of the actuator.\n";
585 PyObject* KX_IpoActuator::PySetType(PyObject* self, 
586                                                                         PyObject* args, 
587                                                                         PyObject* kwds) {
588         int typeArg;
589         
590         if (!PyArg_ParseTuple(args, "i", &typeArg)) {
591                 return NULL;
592         }
593         
594         if ( (typeArg > KX_ACT_IPO_NODEF) 
595                  && (typeArg < KX_ACT_IPO_KEY2KEY) ) {
596                 m_type = (IpoActType) typeArg;
597         }
598         
599         Py_Return;
600 }
601 /* 9. getType:                                                               */
602 char KX_IpoActuator::GetType_doc[] = 
603 "getType()\n"
604 "\tReturns the operation mode of the actuator.\n";
605 PyObject* KX_IpoActuator::PyGetType(PyObject* self, 
606                                                                         PyObject* args, 
607                                                                         PyObject* kwds) {
608         return PyInt_FromLong(m_type);
609 }
610
611 /* 10. setForceIpoActsLocal:                                                 */
612 char KX_IpoActuator::SetForceIpoActsLocal_doc[] = 
613 "setForceIpoActsLocal(local?)\n"
614 "\t - local?    : Apply the ipo-as-force in the object's local\n"
615 "\t               coordinates? (KX_TRUE, KX_FALSE)\n"
616 "\tSet whether to apply the force in the object's local\n"
617 "\tcoordinates rather than the world global coordinates.\n";
618 PyObject* KX_IpoActuator::PySetForceIpoActsLocal(PyObject* self, 
619                                                                                          PyObject* args, 
620                                                                                      PyObject* kwds) { 
621         int boolArg;
622         
623         if (!PyArg_ParseTuple(args, "i", &boolArg)) {
624                 return NULL;
625         }
626
627         m_force_ipo_local = PyArgToBool(boolArg);
628         
629         Py_Return;      
630 }
631 /* 11. getForceIpoActsLocal:                                                */
632 char KX_IpoActuator::GetForceIpoActsLocal_doc[] = 
633 "getForceIpoActsLocal()\n"
634 "\tReturn whether to apply the force in the object's local\n"
635 "\tcoordinates rather than the world global coordinates.\n";
636 PyObject* KX_IpoActuator::PyGetForceIpoActsLocal(PyObject* self, 
637                                                                                          PyObject* args, 
638                                                                                          PyObject* kwds) {
639         return BoolToPyArg(m_force_ipo_local);
640 }
641
642
643 /* eof */