aed6c666404011d97c462c594e30531f7596b411
[blender.git] / source / gameengine / Ketsji / KX_IpoActuator.cpp
1 /*
2  * Do Ipo stuff
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 /** \file gameengine/Ketsji/KX_IpoActuator.cpp
33  *  \ingroup ketsji
34  */
35
36
37 #if defined (__sgi)
38 #include <math.h>
39 #else
40 #include <cmath>
41 #endif
42  
43 #include "KX_IpoActuator.h"
44 #include "KX_GameObject.h"
45 #include "FloatValue.h"
46
47 #include "KX_KetsjiEngine.h"
48
49 /* ------------------------------------------------------------------------- */
50 /* Type strings                                                              */
51 /* ------------------------------------------------------------------------- */
52
53 const char *KX_IpoActuator::S_KX_ACT_IPO_PLAY_STRING      = "Play";
54 const char *KX_IpoActuator::S_KX_ACT_IPO_PINGPONG_STRING  = "PingPong";
55 const char *KX_IpoActuator::S_KX_ACT_IPO_FLIPPER_STRING   = "Flipper";
56 const char *KX_IpoActuator::S_KX_ACT_IPO_LOOPSTOP_STRING  = "LoopStop";
57 const char *KX_IpoActuator::S_KX_ACT_IPO_LOOPEND_STRING   = "LoopEnd";
58 const char *KX_IpoActuator::S_KX_ACT_IPO_KEY2KEY_STRING   = "Key2key";
59 const char *KX_IpoActuator::S_KX_ACT_IPO_FROM_PROP_STRING = "FromProp";
60
61 /* ------------------------------------------------------------------------- */
62 /* Native functions                                                          */
63 /* ------------------------------------------------------------------------- */
64
65 KX_IpoActuator::KX_IpoActuator(SCA_IObject* gameobj,
66                                                            const STR_String& propname,
67                                                            const STR_String& framePropname,
68                                                            float starttime,
69                                                            float endtime,
70                                                            bool recurse,
71                                                            int acttype,
72                                                            bool ipo_as_force,
73                                                            bool ipo_add,
74                                                            bool ipo_local)
75         : SCA_IActuator(gameobj, KX_ACT_IPO),
76         m_bNegativeEvent(false),
77         m_startframe (starttime),
78         m_endframe(endtime),
79         m_recurse(recurse),
80         m_localtime(starttime),
81         m_direction(1),
82         m_propname(propname),
83         m_framepropname(framePropname),
84         m_ipo_as_force(ipo_as_force),
85         m_ipo_add(ipo_add),
86         m_ipo_local(ipo_local),
87         m_type(acttype)
88 {
89         this->ResetStartTime();
90         m_bIpoPlaying = false;
91 }
92
93 void KX_IpoActuator::SetStart(float starttime) 
94
95         m_startframe=starttime;
96 }
97
98 void KX_IpoActuator::SetEnd(float endtime) 
99
100         m_endframe=endtime;
101 }
102
103 bool KX_IpoActuator::ClampLocalTime()
104 {
105         if (m_startframe < m_endframe)
106         {
107                 if (m_localtime < m_startframe)
108                 {
109                         m_localtime = m_startframe;
110                         return true;
111                 } 
112                 else if (m_localtime > m_endframe)
113                 {
114                         m_localtime = m_endframe;
115                         return true;
116                 }
117         } else {
118                 if (m_localtime > m_startframe)
119                 {
120                         m_localtime = m_startframe;
121                         return true;
122                 }
123                 else if (m_localtime < m_endframe)
124                 {
125                         m_localtime = m_endframe;
126                         return true;
127                 }
128         }
129         return false;
130 }
131
132 void KX_IpoActuator::SetStartTime(float curtime)
133 {
134         float direction = m_startframe < m_endframe ? 1.0f : -1.0f;
135
136         if (m_direction > 0)
137                 m_starttime = curtime - direction*(m_localtime - m_startframe)/KX_KetsjiEngine::GetAnimFrameRate();
138         else
139                 m_starttime = curtime - direction*(m_endframe - m_localtime)/KX_KetsjiEngine::GetAnimFrameRate();
140 }
141
142 void KX_IpoActuator::SetLocalTime(float curtime)
143 {
144         float delta_time = (curtime - m_starttime)*KX_KetsjiEngine::GetAnimFrameRate();
145         
146         // negative delta_time is caused by floating point inaccuracy
147         // perhaps the inaccuracy could be reduced a bit
148         if ((m_localtime==m_startframe || m_localtime==m_endframe) && delta_time<0.0)
149         {
150                 delta_time = 0.0;
151         }
152         
153         if (m_endframe < m_startframe)
154                 delta_time = -delta_time;
155
156         if (m_direction > 0)
157                 m_localtime = m_startframe + delta_time;
158         else
159                 m_localtime = m_endframe - delta_time;
160 }
161
162 bool KX_IpoActuator::Update(double curtime, bool frame)
163 {
164         // result = true if animation has to be continued, false if animation stops
165         // maybe there are events for us in the queue !
166         bool bNegativeEvent = false;
167         bool numevents = false;
168         bool bIpoStart = false;
169
170         curtime -= KX_KetsjiEngine::GetSuspendedDelta();
171
172         if (frame)
173         {
174                 numevents = m_posevent || m_negevent;
175                 bNegativeEvent = IsNegativeEvent();
176                 RemoveAllEvents();
177         }
178         
179         float  start_smaller_then_end = ( m_startframe < m_endframe ? 1.0f : -1.0f);
180
181         bool result=true;
182         if (!bNegativeEvent)
183         {
184                 if (m_starttime < -2.0f*fabs(m_endframe - m_startframe))
185                 {
186                         // start for all Ipo, initial start for LOOP_STOP
187                         m_starttime = curtime;
188                         m_bIpoPlaying = true;
189                         bIpoStart = true;
190                 }
191         }       
192
193         switch ((IpoActType)m_type)
194         {
195                 
196         case KX_ACT_IPO_PLAY:
197         {
198                 // Check if playing forwards.  result = ! finished
199                 
200                 if (start_smaller_then_end > 0.f)
201                         result = (m_localtime < m_endframe && m_bIpoPlaying);
202                 else
203                         result = (m_localtime > m_endframe && m_bIpoPlaying);
204                 
205                 if (result)
206                 {
207                         SetLocalTime(curtime);
208                 
209                         /* Perform clamping */
210                         ClampLocalTime();
211         
212                         if (bIpoStart)
213                                 ((KX_GameObject*)GetParent())->InitIPO(m_ipo_as_force, m_ipo_add, m_ipo_local);
214                         ((KX_GameObject*)GetParent())->UpdateIPO(m_localtime,m_recurse);
215                 } else
216                 {
217                         m_localtime=m_startframe;
218                         m_direction=1;
219                 }
220                 break;
221         }
222         case KX_ACT_IPO_PINGPONG:
223         {
224                 result = true;
225                 if (bNegativeEvent && !m_bIpoPlaying)
226                         result = false;
227                 else
228                         SetLocalTime(curtime);
229                         
230                 if (ClampLocalTime())
231                 {
232                         result = false;
233                         m_direction = -m_direction;
234                 }
235                 
236                 if (bIpoStart && m_direction > 0)
237                         ((KX_GameObject*)GetParent())->InitIPO(m_ipo_as_force, m_ipo_add, m_ipo_local);
238                 ((KX_GameObject*)GetParent())->UpdateIPO(m_localtime,m_recurse);
239                 break;
240         }
241         case KX_ACT_IPO_FLIPPER:
242         {
243                 if (bNegativeEvent && !m_bIpoPlaying)
244                         result = false;
245                 if (numevents)
246                 {
247                         float oldDirection = m_direction;
248                         if (bNegativeEvent)
249                                 m_direction = -1;
250                         else
251                                 m_direction = 1;
252                         if (m_direction != oldDirection)
253                                 // changing direction, reset start time
254                                 SetStartTime(curtime);
255                 }
256                 
257                 SetLocalTime(curtime);
258                 
259                 if (ClampLocalTime() && m_localtime == m_startframe)
260                         result = false;
261
262                 if (bIpoStart)
263                         ((KX_GameObject*)GetParent())->InitIPO(m_ipo_as_force, m_ipo_add, m_ipo_local);                 
264                 ((KX_GameObject*)GetParent())->UpdateIPO(m_localtime,m_recurse);
265                 break;
266         }
267
268         case KX_ACT_IPO_LOOPSTOP:
269         {
270                 if (numevents)
271                 {
272                         if (bNegativeEvent)
273                         {
274                                 result = false;
275                                 m_bNegativeEvent = false;
276                                 numevents = false;
277                         }
278                         if (!m_bIpoPlaying)
279                         {
280                                 // Ipo was stopped, make sure we will restart from where it stopped
281                                 SetStartTime(curtime);
282                                 if (!bNegativeEvent)
283                                         // positive signal will restart the Ipo
284                                         m_bIpoPlaying = true;
285                         }
286
287                 } // fall through to loopend, and quit the ipo animation immediatly 
288         }
289         case KX_ACT_IPO_LOOPEND:
290         {
291                 if (numevents){
292                         if (bNegativeEvent && m_bIpoPlaying){
293                                 m_bNegativeEvent = true;
294                         }
295                 }
296                 
297                 if (bNegativeEvent && !m_bIpoPlaying){
298                         result = false;
299                 } 
300                 else
301                 {
302                         if (m_localtime*start_smaller_then_end < m_endframe*start_smaller_then_end)
303                         {
304                                 SetLocalTime(curtime);
305                         }
306                         else{
307                                 if (!m_bNegativeEvent){
308                                         /* Perform wraparound */
309                                         SetLocalTime(curtime);
310                                         if (start_smaller_then_end > 0.f)
311                                                 m_localtime = m_startframe + fmod(m_localtime - m_startframe, m_endframe - m_startframe);
312                                         else
313                                                 m_localtime = m_startframe - fmod(m_startframe - m_localtime, m_startframe - m_endframe);
314                                         SetStartTime(curtime);
315                                         bIpoStart = true;
316                                 }
317                                 else
318                                 {       
319                                         /* Perform clamping */
320                                         m_localtime=m_endframe;
321                                         result = false;
322                                         m_bNegativeEvent = false;
323                                 }
324                         }
325                 }
326                 
327                 if (m_bIpoPlaying && bIpoStart)
328                         ((KX_GameObject*)GetParent())->InitIPO(m_ipo_as_force, m_ipo_add, m_ipo_local);
329                 ((KX_GameObject*)GetParent())->UpdateIPO(m_localtime,m_recurse);
330                 break;
331         }
332         
333         case KX_ACT_IPO_KEY2KEY:
334         {
335                 // not implemented yet
336                 result = false;
337                 break;
338         }
339         
340         case KX_ACT_IPO_FROM_PROP:
341         {
342                 result = !bNegativeEvent;
343
344                 CValue* propval = GetParent()->GetProperty(m_propname);
345                 if (propval)
346                 {
347                         m_localtime = propval->GetNumber(); 
348         
349                         if (bIpoStart)
350                                 ((KX_GameObject*)GetParent())->InitIPO(m_ipo_as_force, m_ipo_add, m_ipo_local);
351                         ((KX_GameObject*)GetParent())->UpdateIPO(m_localtime,m_recurse);
352                 } else
353                 {
354                         result = false;
355                 }
356                 break;
357         }
358                 
359         default:
360                 result = false;
361         }
362
363         /* Set the property if its defined */
364         if (m_framepropname[0] != '\0') {
365                 CValue* propowner = GetParent();
366                 CValue* oldprop = propowner->GetProperty(m_framepropname);
367                 CValue* newval = new CFloatValue(m_localtime);
368                 if (oldprop) {
369                         oldprop->SetValue(newval);
370                 } else {
371                         propowner->SetProperty(m_framepropname, newval);
372                 }
373                 newval->Release();
374         }
375
376         if (!result)
377         {
378                 if (m_type != KX_ACT_IPO_LOOPSTOP)
379                         this->ResetStartTime();
380                 m_bIpoPlaying = false;
381         }
382
383         return result;
384 }
385
386 void KX_IpoActuator::ResetStartTime()
387 {
388         this->m_starttime = -2.0*fabs(this->m_endframe - this->m_startframe) - 1.0;
389 }
390
391 int KX_IpoActuator::string2mode(char* modename) {
392         IpoActType res = KX_ACT_IPO_NODEF;
393
394         if (strcmp(modename, S_KX_ACT_IPO_PLAY_STRING)==0) { 
395                 res = KX_ACT_IPO_PLAY;
396         } else if (strcmp(modename, S_KX_ACT_IPO_PINGPONG_STRING)==0) {
397                 res = KX_ACT_IPO_PINGPONG;
398         } else if (strcmp(modename, S_KX_ACT_IPO_FLIPPER_STRING)==0) {
399                 res = KX_ACT_IPO_FLIPPER;
400         } else if (strcmp(modename, S_KX_ACT_IPO_LOOPSTOP_STRING)==0) {
401                 res = KX_ACT_IPO_LOOPSTOP;
402         } else if (strcmp(modename, S_KX_ACT_IPO_LOOPEND_STRING)==0) {
403                 res = KX_ACT_IPO_LOOPEND;
404         } else if (strcmp(modename, S_KX_ACT_IPO_KEY2KEY_STRING)==0) {
405                 res = KX_ACT_IPO_KEY2KEY;
406         } else if (strcmp(modename, S_KX_ACT_IPO_FROM_PROP_STRING)==0) {
407                 res = KX_ACT_IPO_FROM_PROP;
408         }
409
410         return res;
411 }
412
413 #ifdef WITH_PYTHON
414
415 /* ------------------------------------------------------------------------- */
416 /* Python functions                                                          */
417 /* ------------------------------------------------------------------------- */
418
419
420 /* Integration hooks ------------------------------------------------------- */
421 PyTypeObject KX_IpoActuator::Type = {
422         PyVarObject_HEAD_INIT(NULL, 0)
423         "KX_IpoActuator",
424         sizeof(PyObjectPlus_Proxy),
425         0,
426         py_base_dealloc,
427         0,
428         0,
429         0,
430         0,
431         py_base_repr,
432         0,0,0,0,0,0,0,0,0,
433         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
434         0,0,0,0,0,0,0,
435         Methods,
436         0,
437         0,
438         &SCA_IActuator::Type,
439         0,0,0,0,0,0,
440         py_base_new
441 };
442
443 PyMethodDef KX_IpoActuator::Methods[] = {
444         {NULL,NULL} //Sentinel
445 };
446
447 PyAttributeDef KX_IpoActuator::Attributes[] = {
448         KX_PYATTRIBUTE_RW_FUNCTION("frameStart", KX_IpoActuator, pyattr_get_frame_start, pyattr_set_frame_start),
449         KX_PYATTRIBUTE_RW_FUNCTION("frameEnd", KX_IpoActuator, pyattr_get_frame_end, pyattr_set_frame_end),
450         KX_PYATTRIBUTE_STRING_RW("propName", 0, 64, false, KX_IpoActuator, m_propname),
451         KX_PYATTRIBUTE_STRING_RW("framePropName", 0, 64, false, KX_IpoActuator, m_framepropname),
452         KX_PYATTRIBUTE_INT_RW("mode", KX_ACT_IPO_NODEF+1, KX_ACT_IPO_MAX-1, true, KX_IpoActuator, m_type),
453         KX_PYATTRIBUTE_BOOL_RW("useIpoAsForce", KX_IpoActuator, m_ipo_as_force),
454         KX_PYATTRIBUTE_BOOL_RW("useIpoAdd", KX_IpoActuator, m_ipo_add),
455         KX_PYATTRIBUTE_BOOL_RW("useIpoLocal", KX_IpoActuator, m_ipo_local),
456         KX_PYATTRIBUTE_BOOL_RW("useChildren", KX_IpoActuator, m_recurse),
457         
458         { NULL }        //Sentinel
459 };
460
461 PyObject* KX_IpoActuator::pyattr_get_frame_start(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
462 {
463         KX_IpoActuator* self= static_cast<KX_IpoActuator*>(self_v);
464         return PyFloat_FromDouble(self->m_startframe);
465 }
466
467 int KX_IpoActuator::pyattr_set_frame_start(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
468 {
469         KX_IpoActuator* self= static_cast<KX_IpoActuator*>(self_v);
470         float param = PyFloat_AsDouble(value);
471
472         if (PyErr_Occurred()) {
473                 PyErr_SetString(PyExc_AttributeError, "frameStart = float: KX_IpoActuator, expected a float value");
474                 return PY_SET_ATTR_FAIL;
475         }
476
477         self->m_startframe = param;
478         self->ResetStartTime();
479         return PY_SET_ATTR_SUCCESS;
480 }
481
482 PyObject* KX_IpoActuator::pyattr_get_frame_end(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
483 {
484         KX_IpoActuator* self= static_cast<KX_IpoActuator*>(self_v);
485         return PyFloat_FromDouble(self->m_endframe);
486 }
487
488 int KX_IpoActuator::pyattr_set_frame_end(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
489 {
490         KX_IpoActuator* self= static_cast<KX_IpoActuator*>(self_v);
491         float param = PyFloat_AsDouble(value);
492
493         if (PyErr_Occurred()) {
494                 PyErr_SetString(PyExc_AttributeError, "frameEnd = float: KX_IpoActuator, expected a float value");
495                 return PY_SET_ATTR_FAIL;
496         }
497
498         self->m_endframe = param;
499         self->ResetStartTime();
500         return PY_SET_ATTR_SUCCESS;
501 }
502
503 #endif // WITH_PYTHON
504
505 /* eof */