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