Blender  V2.59
KX_IpoActuator.cpp
Go to the documentation of this file.
00001 /*
00002  * Do Ipo stuff
00003  *
00004  * $Id: KX_IpoActuator.cpp 35171 2011-02-25 13:35:59Z jesterking $
00005  *
00006  * ***** BEGIN GPL LICENSE BLOCK *****
00007  *
00008  * This program is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU General Public License
00010  * as published by the Free Software Foundation; either version 2
00011  * of the License, or (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software Foundation,
00020  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00021  *
00022  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
00023  * All rights reserved.
00024  *
00025  * The Original Code is: all of this file.
00026  *
00027  * Contributor(s): none yet.
00028  *
00029  * ***** END GPL LICENSE BLOCK *****
00030  */
00031 
00037 #if defined (__sgi)
00038 #include <math.h>
00039 #else
00040 #include <cmath>
00041 #endif
00042  
00043 #include "KX_IpoActuator.h"
00044 #include "KX_GameObject.h"
00045 #include "FloatValue.h"
00046 
00047 #include "KX_KetsjiEngine.h"
00048 
00049 /* ------------------------------------------------------------------------- */
00050 /* Type strings                                                              */
00051 /* ------------------------------------------------------------------------- */
00052 
00053 const char *KX_IpoActuator::S_KX_ACT_IPO_PLAY_STRING      = "Play";
00054 const char *KX_IpoActuator::S_KX_ACT_IPO_PINGPONG_STRING  = "PingPong";
00055 const char *KX_IpoActuator::S_KX_ACT_IPO_FLIPPER_STRING   = "Flipper";
00056 const char *KX_IpoActuator::S_KX_ACT_IPO_LOOPSTOP_STRING  = "LoopStop";
00057 const char *KX_IpoActuator::S_KX_ACT_IPO_LOOPEND_STRING   = "LoopEnd";
00058 const char *KX_IpoActuator::S_KX_ACT_IPO_KEY2KEY_STRING   = "Key2key";
00059 const char *KX_IpoActuator::S_KX_ACT_IPO_FROM_PROP_STRING = "FromProp";
00060 
00061 /* ------------------------------------------------------------------------- */
00062 /* Native functions                                                          */
00063 /* ------------------------------------------------------------------------- */
00064 
00065 KX_IpoActuator::KX_IpoActuator(SCA_IObject* gameobj,
00066                                                            const STR_String& propname,
00067                                                            const STR_String& framePropname,
00068                                                            float starttime,
00069                                                            float endtime,
00070                                                            bool recurse,
00071                                                            int acttype,
00072                                                            bool ipo_as_force,
00073                                                            bool ipo_add,
00074                                                            bool ipo_local)
00075         : SCA_IActuator(gameobj, KX_ACT_IPO),
00076         m_bNegativeEvent(false),
00077         m_startframe (starttime),
00078         m_endframe(endtime),
00079         m_recurse(recurse),
00080         m_localtime(starttime),
00081         m_direction(1),
00082         m_propname(propname),
00083         m_framepropname(framePropname),
00084         m_ipo_as_force(ipo_as_force),
00085         m_ipo_add(ipo_add),
00086         m_ipo_local(ipo_local),
00087         m_type(acttype)
00088 {
00089         this->ResetStartTime();
00090         m_bIpoPlaying = false;
00091 }
00092 
00093 void KX_IpoActuator::SetStart(float starttime) 
00094 { 
00095         m_startframe=starttime;
00096 }
00097 
00098 void KX_IpoActuator::SetEnd(float endtime) 
00099 { 
00100         m_endframe=endtime;
00101 }
00102 
00103 bool KX_IpoActuator::ClampLocalTime()
00104 {
00105         if (m_startframe < m_endframe)
00106         {
00107                 if (m_localtime < m_startframe)
00108                 {
00109                         m_localtime = m_startframe;
00110                         return true;
00111                 } 
00112                 else if (m_localtime > m_endframe)
00113                 {
00114                         m_localtime = m_endframe;
00115                         return true;
00116                 }
00117         } else {
00118                 if (m_localtime > m_startframe)
00119                 {
00120                         m_localtime = m_startframe;
00121                         return true;
00122                 }
00123                 else if (m_localtime < m_endframe)
00124                 {
00125                         m_localtime = m_endframe;
00126                         return true;
00127                 }
00128         }
00129         return false;
00130 }
00131 
00132 void KX_IpoActuator::SetStartTime(float curtime)
00133 {
00134         float direction = m_startframe < m_endframe ? 1.0f : -1.0f;
00135 
00136         if (m_direction > 0)
00137                 m_starttime = curtime - direction*(m_localtime - m_startframe)/KX_KetsjiEngine::GetAnimFrameRate();
00138         else
00139                 m_starttime = curtime - direction*(m_endframe - m_localtime)/KX_KetsjiEngine::GetAnimFrameRate();
00140 }
00141 
00142 void KX_IpoActuator::SetLocalTime(float curtime)
00143 {
00144         float delta_time = (curtime - m_starttime)*KX_KetsjiEngine::GetAnimFrameRate();
00145         
00146         // negative delta_time is caused by floating point inaccuracy
00147         // perhaps the inaccuracy could be reduced a bit
00148         if ((m_localtime==m_startframe || m_localtime==m_endframe) && delta_time<0.0)
00149         {
00150                 delta_time = 0.0;
00151         }
00152         
00153         if (m_endframe < m_startframe)
00154                 delta_time = -delta_time;
00155 
00156         if (m_direction > 0)
00157                 m_localtime = m_startframe + delta_time;
00158         else
00159                 m_localtime = m_endframe - delta_time;
00160 }
00161 
00162 bool KX_IpoActuator::Update(double curtime, bool frame)
00163 {
00164         // result = true if animation has to be continued, false if animation stops
00165         // maybe there are events for us in the queue !
00166         bool bNegativeEvent = false;
00167         bool numevents = false;
00168         bool bIpoStart = false;
00169 
00170         curtime -= KX_KetsjiEngine::GetSuspendedDelta();
00171 
00172         if (frame)
00173         {
00174                 numevents = m_posevent || m_negevent;
00175                 bNegativeEvent = IsNegativeEvent();
00176                 RemoveAllEvents();
00177         }
00178         
00179         float  start_smaller_then_end = ( m_startframe < m_endframe ? 1.0f : -1.0f);
00180 
00181         bool result=true;
00182         if (!bNegativeEvent)
00183         {
00184                 if (m_starttime < -2.0f*fabs(m_endframe - m_startframe))
00185                 {
00186                         // start for all Ipo, initial start for LOOP_STOP
00187                         m_starttime = curtime;
00188                         m_bIpoPlaying = true;
00189                         bIpoStart = true;
00190                 }
00191         }       
00192 
00193         switch ((IpoActType)m_type)
00194         {
00195                 
00196         case KX_ACT_IPO_PLAY:
00197         {
00198                 // Check if playing forwards.  result = ! finished
00199                 
00200                 if (start_smaller_then_end > 0.f)
00201                         result = (m_localtime < m_endframe && m_bIpoPlaying);
00202                 else
00203                         result = (m_localtime > m_endframe && m_bIpoPlaying);
00204                 
00205                 if (result)
00206                 {
00207                         SetLocalTime(curtime);
00208                 
00209                         /* Perform clamping */
00210                         ClampLocalTime();
00211         
00212                         if (bIpoStart)
00213                                 ((KX_GameObject*)GetParent())->InitIPO(m_ipo_as_force, m_ipo_add, m_ipo_local);
00214                         ((KX_GameObject*)GetParent())->UpdateIPO(m_localtime,m_recurse);
00215                 } else
00216                 {
00217                         m_localtime=m_startframe;
00218                         m_direction=1;
00219                 }
00220                 break;
00221         }
00222         case KX_ACT_IPO_PINGPONG:
00223         {
00224                 result = true;
00225                 if (bNegativeEvent && !m_bIpoPlaying)
00226                         result = false;
00227                 else
00228                         SetLocalTime(curtime);
00229                         
00230                 if (ClampLocalTime())
00231                 {
00232                         result = false;
00233                         m_direction = -m_direction;
00234                 }
00235                 
00236                 if (bIpoStart && m_direction > 0)
00237                         ((KX_GameObject*)GetParent())->InitIPO(m_ipo_as_force, m_ipo_add, m_ipo_local);
00238                 ((KX_GameObject*)GetParent())->UpdateIPO(m_localtime,m_recurse);
00239                 break;
00240         }
00241         case KX_ACT_IPO_FLIPPER:
00242         {
00243                 if (bNegativeEvent && !m_bIpoPlaying)
00244                         result = false;
00245                 if (numevents)
00246                 {
00247                         float oldDirection = m_direction;
00248                         if (bNegativeEvent)
00249                                 m_direction = -1;
00250                         else
00251                                 m_direction = 1;
00252                         if (m_direction != oldDirection)
00253                                 // changing direction, reset start time
00254                                 SetStartTime(curtime);
00255                 }
00256                 
00257                 SetLocalTime(curtime);
00258                 
00259                 if (ClampLocalTime() && m_localtime == m_startframe)
00260                         result = false;
00261 
00262                 if (bIpoStart)
00263                         ((KX_GameObject*)GetParent())->InitIPO(m_ipo_as_force, m_ipo_add, m_ipo_local);                 
00264                 ((KX_GameObject*)GetParent())->UpdateIPO(m_localtime,m_recurse);
00265                 break;
00266         }
00267 
00268         case KX_ACT_IPO_LOOPSTOP:
00269         {
00270                 if (numevents)
00271                 {
00272                         if (bNegativeEvent)
00273                         {
00274                                 result = false;
00275                                 m_bNegativeEvent = false;
00276                                 numevents = false;
00277                         }
00278                         if (!m_bIpoPlaying)
00279                         {
00280                                 // Ipo was stopped, make sure we will restart from where it stopped
00281                                 SetStartTime(curtime);
00282                                 if (!bNegativeEvent)
00283                                         // positive signal will restart the Ipo
00284                                         m_bIpoPlaying = true;
00285                         }
00286 
00287                 } // fall through to loopend, and quit the ipo animation immediatly 
00288         }
00289         case KX_ACT_IPO_LOOPEND:
00290         {
00291                 if (numevents){
00292                         if (bNegativeEvent && m_bIpoPlaying){
00293                                 m_bNegativeEvent = true;
00294                         }
00295                 }
00296                 
00297                 if (bNegativeEvent && !m_bIpoPlaying){
00298                         result = false;
00299                 } 
00300                 else
00301                 {
00302                         if (m_localtime*start_smaller_then_end < m_endframe*start_smaller_then_end)
00303                         {
00304                                 SetLocalTime(curtime);
00305                         }
00306                         else{
00307                                 if (!m_bNegativeEvent){
00308                                         /* Perform wraparound */
00309                                         SetLocalTime(curtime);
00310                                         if (start_smaller_then_end > 0.f)
00311                                                 m_localtime = m_startframe + fmod(m_localtime - m_startframe, m_endframe - m_startframe);
00312                                         else
00313                                                 m_localtime = m_startframe - fmod(m_startframe - m_localtime, m_startframe - m_endframe);
00314                                         SetStartTime(curtime);
00315                                         bIpoStart = true;
00316                                 }
00317                                 else
00318                                 {       
00319                                         /* Perform clamping */
00320                                         m_localtime=m_endframe;
00321                                         result = false;
00322                                         m_bNegativeEvent = false;
00323                                 }
00324                         }
00325                 }
00326                 
00327                 if (m_bIpoPlaying && bIpoStart)
00328                         ((KX_GameObject*)GetParent())->InitIPO(m_ipo_as_force, m_ipo_add, m_ipo_local);
00329                 ((KX_GameObject*)GetParent())->UpdateIPO(m_localtime,m_recurse);
00330                 break;
00331         }
00332         
00333         case KX_ACT_IPO_KEY2KEY:
00334         {
00335                 // not implemented yet
00336                 result = false;
00337                 break;
00338         }
00339         
00340         case KX_ACT_IPO_FROM_PROP:
00341         {
00342                 result = !bNegativeEvent;
00343 
00344                 CValue* propval = GetParent()->GetProperty(m_propname);
00345                 if (propval)
00346                 {
00347                         m_localtime = propval->GetNumber(); 
00348         
00349                         if (bIpoStart)
00350                                 ((KX_GameObject*)GetParent())->InitIPO(m_ipo_as_force, m_ipo_add, m_ipo_local);
00351                         ((KX_GameObject*)GetParent())->UpdateIPO(m_localtime,m_recurse);
00352                 } else
00353                 {
00354                         result = false;
00355                 }
00356                 break;
00357         }
00358                 
00359         default:
00360                 result = false;
00361         }
00362 
00363         /* Set the property if its defined */
00364         if (m_framepropname[0] != '\0') {
00365                 CValue* propowner = GetParent();
00366                 CValue* oldprop = propowner->GetProperty(m_framepropname);
00367                 CValue* newval = new CFloatValue(m_localtime);
00368                 if (oldprop) {
00369                         oldprop->SetValue(newval);
00370                 } else {
00371                         propowner->SetProperty(m_framepropname, newval);
00372                 }
00373                 newval->Release();
00374         }
00375 
00376         if (!result)
00377         {
00378                 if (m_type != KX_ACT_IPO_LOOPSTOP)
00379                         this->ResetStartTime();
00380                 m_bIpoPlaying = false;
00381         }
00382 
00383         return result;
00384 }
00385 
00386 void KX_IpoActuator::ResetStartTime()
00387 {
00388         this->m_starttime = -2.0*fabs(this->m_endframe - this->m_startframe) - 1.0;
00389 }
00390 
00391 int KX_IpoActuator::string2mode(char* modename) {
00392         IpoActType res = KX_ACT_IPO_NODEF;
00393 
00394         if (strcmp(modename, S_KX_ACT_IPO_PLAY_STRING)==0) { 
00395                 res = KX_ACT_IPO_PLAY;
00396         } else if (strcmp(modename, S_KX_ACT_IPO_PINGPONG_STRING)==0) {
00397                 res = KX_ACT_IPO_PINGPONG;
00398         } else if (strcmp(modename, S_KX_ACT_IPO_FLIPPER_STRING)==0) {
00399                 res = KX_ACT_IPO_FLIPPER;
00400         } else if (strcmp(modename, S_KX_ACT_IPO_LOOPSTOP_STRING)==0) {
00401                 res = KX_ACT_IPO_LOOPSTOP;
00402         } else if (strcmp(modename, S_KX_ACT_IPO_LOOPEND_STRING)==0) {
00403                 res = KX_ACT_IPO_LOOPEND;
00404         } else if (strcmp(modename, S_KX_ACT_IPO_KEY2KEY_STRING)==0) {
00405                 res = KX_ACT_IPO_KEY2KEY;
00406         } else if (strcmp(modename, S_KX_ACT_IPO_FROM_PROP_STRING)==0) {
00407                 res = KX_ACT_IPO_FROM_PROP;
00408         }
00409 
00410         return res;
00411 }
00412 
00413 #ifdef WITH_PYTHON
00414 
00415 /* ------------------------------------------------------------------------- */
00416 /* Python functions                                                          */
00417 /* ------------------------------------------------------------------------- */
00418 
00419 
00420 /* Integration hooks ------------------------------------------------------- */
00421 PyTypeObject KX_IpoActuator::Type = {
00422         PyVarObject_HEAD_INIT(NULL, 0)
00423         "KX_IpoActuator",
00424         sizeof(PyObjectPlus_Proxy),
00425         0,
00426         py_base_dealloc,
00427         0,
00428         0,
00429         0,
00430         0,
00431         py_base_repr,
00432         0,0,0,0,0,0,0,0,0,
00433         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
00434         0,0,0,0,0,0,0,
00435         Methods,
00436         0,
00437         0,
00438         &SCA_IActuator::Type,
00439         0,0,0,0,0,0,
00440         py_base_new
00441 };
00442 
00443 PyMethodDef KX_IpoActuator::Methods[] = {
00444         {NULL,NULL} //Sentinel
00445 };
00446 
00447 PyAttributeDef KX_IpoActuator::Attributes[] = {
00448         KX_PYATTRIBUTE_RW_FUNCTION("frameStart", KX_IpoActuator, pyattr_get_frame_start, pyattr_set_frame_start),
00449         KX_PYATTRIBUTE_RW_FUNCTION("frameEnd", KX_IpoActuator, pyattr_get_frame_end, pyattr_set_frame_end),
00450         KX_PYATTRIBUTE_STRING_RW("propName", 0, 64, false, KX_IpoActuator, m_propname),
00451         KX_PYATTRIBUTE_STRING_RW("framePropName", 0, 64, false, KX_IpoActuator, m_framepropname),
00452         KX_PYATTRIBUTE_INT_RW("mode", KX_ACT_IPO_NODEF+1, KX_ACT_IPO_MAX-1, true, KX_IpoActuator, m_type),
00453         KX_PYATTRIBUTE_BOOL_RW("useIpoAsForce", KX_IpoActuator, m_ipo_as_force),
00454         KX_PYATTRIBUTE_BOOL_RW("useIpoAdd", KX_IpoActuator, m_ipo_add),
00455         KX_PYATTRIBUTE_BOOL_RW("useIpoLocal", KX_IpoActuator, m_ipo_local),
00456         KX_PYATTRIBUTE_BOOL_RW("useChildren", KX_IpoActuator, m_recurse),
00457         
00458         { NULL }        //Sentinel
00459 };
00460 
00461 PyObject* KX_IpoActuator::pyattr_get_frame_start(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
00462 {
00463         KX_IpoActuator* self= static_cast<KX_IpoActuator*>(self_v);
00464         return PyFloat_FromDouble(self->m_startframe);
00465 }
00466 
00467 int KX_IpoActuator::pyattr_set_frame_start(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
00468 {
00469         KX_IpoActuator* self= static_cast<KX_IpoActuator*>(self_v);
00470         float param = PyFloat_AsDouble(value);
00471 
00472         if (PyErr_Occurred()) {
00473                 PyErr_SetString(PyExc_AttributeError, "frameStart = float: KX_IpoActuator, expected a float value");
00474                 return PY_SET_ATTR_FAIL;
00475         }
00476 
00477         self->m_startframe = param;
00478         self->ResetStartTime();
00479         return PY_SET_ATTR_SUCCESS;
00480 }
00481 
00482 PyObject* KX_IpoActuator::pyattr_get_frame_end(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
00483 {
00484         KX_IpoActuator* self= static_cast<KX_IpoActuator*>(self_v);
00485         return PyFloat_FromDouble(self->m_endframe);
00486 }
00487 
00488 int KX_IpoActuator::pyattr_set_frame_end(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
00489 {
00490         KX_IpoActuator* self= static_cast<KX_IpoActuator*>(self_v);
00491         float param = PyFloat_AsDouble(value);
00492 
00493         if (PyErr_Occurred()) {
00494                 PyErr_SetString(PyExc_AttributeError, "frameEnd = float: KX_IpoActuator, expected a float value");
00495                 return PY_SET_ATTR_FAIL;
00496         }
00497 
00498         self->m_endframe = param;
00499         self->ResetStartTime();
00500         return PY_SET_ATTR_SUCCESS;
00501 }
00502 
00503 #endif // WITH_PYTHON
00504 
00505 /* eof */