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