Blender V2.61 - r43446

KX_IpoActuator.cpp

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