Blender  V2.59
BL_ActionActuator.cpp
Go to the documentation of this file.
00001 /*
00002 * $Id: BL_ActionActuator.cpp 37512 2011-06-15 14:06:25Z campbellbarton $
00003 *
00004  * ***** BEGIN GPL LICENSE BLOCK *****
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * as published by the Free Software Foundation; either version 2
00009  * of the License, or (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software Foundation,
00018  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  *
00020  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
00021  * All rights reserved.
00022  *
00023  * The Original Code is: all of this file.
00024  *
00025  * Contributor(s): none yet.
00026  *
00027  * ***** END GPL LICENSE BLOCK *****
00028 */
00029 
00035 #include "SCA_LogicManager.h"
00036 #include "BL_ActionActuator.h"
00037 #include "BL_ArmatureObject.h"
00038 #include "BL_SkinDeformer.h"
00039 #include "KX_GameObject.h"
00040 #include "STR_HashedString.h"
00041 #include "MEM_guardedalloc.h"
00042 #include "DNA_nla_types.h"
00043 #include "DNA_action_types.h"
00044 #include "DNA_armature_types.h"
00045 #include "DNA_scene_types.h"
00046 #include "BLI_blenlib.h"
00047 #include "BLI_math.h"
00048 #include "BLI_utildefines.h"
00049 #include "MT_Matrix4x4.h"
00050 
00051 #include "BKE_action.h"
00052 #include "FloatValue.h"
00053 #include "PyObjectPlus.h"
00054 #include "KX_PyMath.h"
00055 
00056 extern "C" {
00057 #include "BKE_animsys.h"
00058 #include "BKE_action.h"
00059 #include "RNA_access.h"
00060 #include "RNA_define.h"
00061 }
00062 
00063 BL_ActionActuator::~BL_ActionActuator()
00064 {
00065         if (m_pose)
00066                 game_free_pose(m_pose);
00067         if (m_userpose)
00068                 game_free_pose(m_userpose);
00069         if (m_blendpose)
00070                 game_free_pose(m_blendpose);
00071 }
00072 
00073 void BL_ActionActuator::ProcessReplica()
00074 {
00075         SCA_IActuator::ProcessReplica();
00076         
00077         m_pose = NULL;
00078         m_blendpose = NULL;
00079         m_localtime=m_startframe;
00080         m_lastUpdate=-1;
00081         
00082 }
00083 
00084 void BL_ActionActuator::SetBlendTime (float newtime){
00085         m_blendframe = newtime;
00086 }
00087 
00088 CValue* BL_ActionActuator::GetReplica() {
00089         BL_ActionActuator* replica = new BL_ActionActuator(*this);//m_float,GetName());
00090         replica->ProcessReplica();
00091         return replica;
00092 }
00093 
00094 bool BL_ActionActuator::ClampLocalTime()
00095 {
00096         if (m_startframe < m_endframe)
00097         {
00098                 if (m_localtime < m_startframe)
00099                 {
00100                         m_localtime = m_startframe;
00101                         return true;
00102                 } 
00103                 else if (m_localtime > m_endframe)
00104                 {
00105                         m_localtime = m_endframe;
00106                         return true;
00107                 }
00108         } else {
00109                 if (m_localtime > m_startframe)
00110                 {
00111                         m_localtime = m_startframe;
00112                         return true;
00113                 }
00114                 else if (m_localtime < m_endframe)
00115                 {
00116                         m_localtime = m_endframe;
00117                         return true;
00118                 }
00119         }
00120         return false;
00121 }
00122 
00123 void BL_ActionActuator::SetStartTime(float curtime)
00124 {
00125         float direction = m_startframe < m_endframe ? 1.0 : -1.0;
00126         
00127         if (!(m_flag & ACT_FLAG_REVERSE))
00128                 m_starttime = curtime - direction*(m_localtime - m_startframe)/KX_KetsjiEngine::GetAnimFrameRate();
00129         else
00130                 m_starttime = curtime - direction*(m_endframe - m_localtime)/KX_KetsjiEngine::GetAnimFrameRate();
00131 }
00132 
00133 void BL_ActionActuator::SetLocalTime(float curtime)
00134 {
00135         float delta_time = (curtime - m_starttime)*KX_KetsjiEngine::GetAnimFrameRate();
00136         
00137         if (m_endframe < m_startframe)
00138                 delta_time = -delta_time;
00139 
00140         if (!(m_flag & ACT_FLAG_REVERSE))
00141                 m_localtime = m_startframe + delta_time;
00142         else
00143                 m_localtime = m_endframe - delta_time;
00144 }
00145 
00146 
00147 bool BL_ActionActuator::Update(double curtime, bool frame)
00148 {
00149         bool bNegativeEvent = false;
00150         bool bPositiveEvent = false;
00151         bool keepgoing = true;
00152         bool wrap = false;
00153         bool apply=true;
00154         int     priority;
00155         float newweight;
00156 
00157         curtime -= KX_KetsjiEngine::GetSuspendedDelta();
00158         
00159         // result = true if animation has to be continued, false if animation stops
00160         // maybe there are events for us in the queue !
00161         if (frame)
00162         {
00163                 bNegativeEvent = m_negevent;
00164                 bPositiveEvent = m_posevent;
00165                 RemoveAllEvents();
00166                 
00167                 if (bPositiveEvent)
00168                         m_flag |= ACT_FLAG_ACTIVE;
00169                 
00170                 if (bNegativeEvent)
00171                 {
00172                         // dont continue where we left off when restarting
00173                         if (m_end_reset) {
00174                                 m_flag &= ~ACT_FLAG_LOCKINPUT;
00175                         }
00176                         
00177                         if (!(m_flag & ACT_FLAG_ACTIVE))
00178                                 return false;
00179                         m_flag &= ~ACT_FLAG_ACTIVE;
00180                 }
00181         }
00182         
00183         /*      We know that action actuators have been discarded from all non armature objects:
00184         if we're being called, we're attached to a BL_ArmatureObject */
00185         BL_ArmatureObject *obj = (BL_ArmatureObject*)GetParent();
00186         float length = m_endframe - m_startframe;
00187         
00188         priority = m_priority;
00189         
00190         /* Determine pre-incrementation behaviour and set appropriate flags */
00191         switch (m_playtype){
00192         case ACT_ACTION_MOTION:
00193                 if (bNegativeEvent){
00194                         keepgoing=false;
00195                         apply=false;
00196                 };
00197                 break;
00198         case ACT_ACTION_FROM_PROP:
00199                 if (bNegativeEvent){
00200                         apply=false;
00201                         keepgoing=false;
00202                 }
00203                 break;
00204         case ACT_ACTION_LOOP_END:
00205                 if (bPositiveEvent){
00206                         if (!(m_flag & ACT_FLAG_LOCKINPUT)){
00207                                 m_flag &= ~ACT_FLAG_KEYUP;
00208                                 m_flag &= ~ACT_FLAG_REVERSE;
00209                                 m_flag |= ACT_FLAG_LOCKINPUT;
00210                                 m_localtime = m_startframe;
00211                                 m_starttime = curtime;
00212                         }
00213                 }
00214                 if (bNegativeEvent){
00215                         m_flag |= ACT_FLAG_KEYUP;
00216                 }
00217                 break;
00218         case ACT_ACTION_LOOP_STOP:
00219                 if (bPositiveEvent){
00220                         if (!(m_flag & ACT_FLAG_LOCKINPUT)){
00221                                 m_flag &= ~ACT_FLAG_REVERSE;
00222                                 m_flag &= ~ACT_FLAG_KEYUP;
00223                                 m_flag |= ACT_FLAG_LOCKINPUT;
00224                                 SetStartTime(curtime);
00225                         }
00226                 }
00227                 if (bNegativeEvent){
00228                         m_flag |= ACT_FLAG_KEYUP;
00229                         m_flag &= ~ACT_FLAG_LOCKINPUT;
00230                         keepgoing=false;
00231                         apply=false;
00232                 }
00233                 break;
00234         case ACT_ACTION_PINGPONG:
00235                 if (bPositiveEvent){
00236                         if (!(m_flag & ACT_FLAG_LOCKINPUT)){
00237                                 m_flag &= ~ACT_FLAG_KEYUP;
00238                                 m_localtime = m_starttime;
00239                                 m_starttime = curtime;
00240                                 m_flag |= ACT_FLAG_LOCKINPUT;
00241                         }
00242                 }
00243                 break;
00244         case ACT_ACTION_FLIPPER:
00245                 if (bPositiveEvent){
00246                         if (!(m_flag & ACT_FLAG_LOCKINPUT)){
00247                                 m_flag &= ~ACT_FLAG_REVERSE;
00248                                 m_flag |= ACT_FLAG_LOCKINPUT;
00249                                 SetStartTime(curtime);
00250                         }
00251                 }
00252                 else if (bNegativeEvent){
00253                         m_flag |= ACT_FLAG_REVERSE;
00254                         m_flag &= ~ACT_FLAG_LOCKINPUT;
00255                         SetStartTime(curtime);
00256                 }
00257                 break;
00258         case ACT_ACTION_PLAY:
00259                 if (bPositiveEvent){
00260                         if (!(m_flag & ACT_FLAG_LOCKINPUT)){
00261                                 m_flag &= ~ACT_FLAG_REVERSE;
00262                                 m_localtime = m_starttime;
00263                                 m_starttime = curtime;
00264                                 m_flag |= ACT_FLAG_LOCKINPUT;
00265                         }
00266                 }
00267                 break;
00268         default:
00269                 break;
00270         }
00271         
00272         /* Perform increment */
00273         if (keepgoing){
00274                 if (m_playtype == ACT_ACTION_MOTION){
00275                         MT_Point3       newpos;
00276                         MT_Point3       deltapos;
00277                         
00278                         newpos = obj->NodeGetWorldPosition();
00279                         
00280                         /* Find displacement */
00281                         deltapos = newpos-m_lastpos;
00282                         m_localtime += (length/m_stridelength) * deltapos.length();
00283                         m_lastpos = newpos;
00284                 }
00285                 else{
00286                         SetLocalTime(curtime);
00287                 }
00288         }
00289         
00290         /* Check if a wrapping response is needed */
00291         if (length){
00292                 if (m_localtime < m_startframe || m_localtime > m_endframe)
00293                 {
00294                         m_localtime = m_startframe + fmod(m_localtime, length);
00295                         wrap = true;
00296                 }
00297         }
00298         else
00299                 m_localtime = m_startframe;
00300         
00301         /* Perform post-increment tasks */
00302         switch (m_playtype){
00303         case ACT_ACTION_FROM_PROP:
00304                 {
00305                         CValue* propval = GetParent()->GetProperty(m_propname);
00306                         if (propval)
00307                                 m_localtime = propval->GetNumber();
00308                         
00309                         if (bNegativeEvent){
00310                                 keepgoing=false;
00311                         }
00312                 }
00313                 break;
00314         case ACT_ACTION_MOTION:
00315                 break;
00316         case ACT_ACTION_LOOP_STOP:
00317                 break;
00318         case ACT_ACTION_PINGPONG:
00319                 if (wrap){
00320                         if (!(m_flag & ACT_FLAG_REVERSE))
00321                                 m_localtime = m_endframe;
00322                         else 
00323                                 m_localtime = m_startframe;
00324 
00325                         m_flag &= ~ACT_FLAG_LOCKINPUT;
00326                         m_flag ^= ACT_FLAG_REVERSE; //flip direction
00327                         keepgoing = false;
00328                 }
00329                 break;
00330         case ACT_ACTION_FLIPPER:
00331                 if (wrap){
00332                         if (!(m_flag & ACT_FLAG_REVERSE)){
00333                                 m_localtime=m_endframe;
00334                                 //keepgoing = false;
00335                         }
00336                         else {
00337                                 m_localtime=m_startframe;
00338                                 keepgoing = false;
00339                         }
00340                 }
00341                 break;
00342         case ACT_ACTION_LOOP_END:
00343                 if (wrap){
00344                         if (m_flag & ACT_FLAG_KEYUP){
00345                                 keepgoing = false;
00346                                 m_localtime = m_endframe;
00347                                 m_flag &= ~ACT_FLAG_LOCKINPUT;
00348                         }
00349                         SetStartTime(curtime);
00350                 }
00351                 break;
00352         case ACT_ACTION_PLAY:
00353                 if (wrap){
00354                         m_localtime = m_endframe;
00355                         keepgoing = false;
00356                         m_flag &= ~ACT_FLAG_LOCKINPUT;
00357                 }
00358                 break;
00359         default:
00360                 keepgoing = false;
00361                 break;
00362         }
00363         
00364         /* Set the property if its defined */
00365         if (m_framepropname[0] != '\0') {
00366                 CValue* propowner = GetParent();
00367                 CValue* oldprop = propowner->GetProperty(m_framepropname);
00368                 CValue* newval = new CFloatValue(m_localtime);
00369                 if (oldprop) {
00370                         oldprop->SetValue(newval);
00371                 } else {
00372                         propowner->SetProperty(m_framepropname, newval);
00373                 }
00374                 newval->Release();
00375         }
00376         
00377         if (bNegativeEvent)
00378                 m_blendframe=0.0;
00379         
00380         /* Apply the pose if necessary*/
00381         if (apply){
00382 
00383                 /* Priority test */
00384                 if (obj->SetActiveAction(this, priority, curtime)){
00385                         
00386                         /* Get the underlying pose from the armature */
00387                         obj->GetPose(&m_pose);
00388 
00389 // 2.4x function, 
00390                         /* Override the necessary channels with ones from the action */
00391                         // XXX extract_pose_from_action(m_pose, m_action, m_localtime);
00392                         
00393                         
00394 // 2.5x - replacement for extract_pose_from_action(...) above.
00395                         {
00396                                 struct PointerRNA id_ptr;
00397                                 Object *arm= obj->GetArmatureObject();
00398                                 bPose *pose_back= arm->pose;
00399                                 
00400                                 arm->pose= m_pose;
00401                                 RNA_id_pointer_create((ID *)arm, &id_ptr);
00402                                 animsys_evaluate_action(&id_ptr, m_action, NULL, m_localtime);
00403                                 
00404                                 arm->pose= pose_back;
00405                         
00406 // 2.5x - could also do this but looks too high level, constraints use this, it works ok.
00407 //                              Object workob; /* evaluate using workob */
00408 //                              what_does_obaction(obj->GetArmatureObject(), &workob, m_pose, m_action, NULL, m_localtime);
00409                         }
00410 
00411                         // done getting the pose from the action
00412                         
00413                         /* Perform the user override (if any) */
00414                         if (m_userpose){
00415                                 extract_pose_from_pose(m_pose, m_userpose);
00416                                 game_free_pose(m_userpose); //cant use MEM_freeN(m_userpose) because the channels need freeing too.
00417                                 m_userpose = NULL;
00418                         }
00419 #if 1
00420                         /* Handle blending */
00421                         if (m_blendin && (m_blendframe<m_blendin)){
00422                                 /* If this is the start of a blending sequence... */
00423                                 if ((m_blendframe==0.0) || (!m_blendpose)){
00424                                         obj->GetMRDPose(&m_blendpose);
00425                                         m_blendstart = curtime;
00426                                 }
00427                                 
00428                                 /* Find percentages */
00429                                 newweight = (m_blendframe/(float)m_blendin);
00430                                 game_blend_poses(m_pose, m_blendpose, 1.0 - newweight);
00431 
00432                                 /* Increment current blending percentage */
00433                                 m_blendframe = (curtime - m_blendstart)*KX_KetsjiEngine::GetAnimFrameRate();
00434                                 if (m_blendframe>m_blendin)
00435                                         m_blendframe = m_blendin;
00436                                 
00437                         }
00438 #endif
00439                         m_lastUpdate = m_localtime;
00440                         obj->SetPose (m_pose);
00441                 }
00442                 else{
00443                         m_blendframe = 0.0;
00444                 }
00445         }
00446         
00447         if (!keepgoing){
00448                 m_blendframe = 0.0;
00449         }
00450         return keepgoing;
00451 };
00452 
00453 #ifdef WITH_PYTHON
00454 
00455 /* ------------------------------------------------------------------------- */
00456 /* Python functions                                                          */
00457 /* ------------------------------------------------------------------------- */
00458 
00459 PyObject* BL_ActionActuator::PyGetChannel(PyObject* value) {
00460         char *string= _PyUnicode_AsString(value);
00461         
00462         if (!string) {
00463                 PyErr_SetString(PyExc_TypeError, "expected a single string");
00464                 return NULL;
00465         }
00466         
00467         bPoseChannel *pchan;
00468         
00469         if(m_userpose==NULL && m_pose==NULL) {
00470                 BL_ArmatureObject *obj = (BL_ArmatureObject*)GetParent();
00471                 obj->GetPose(&m_pose); /* Get the underlying pose from the armature */
00472         }
00473         
00474         // get_pose_channel accounts for NULL pose, run on both incase one exists but
00475         // the channel doesnt
00476         if(             !(pchan=get_pose_channel(m_userpose, string)) &&
00477                         !(pchan=get_pose_channel(m_pose, string))  )
00478         {
00479                 PyErr_SetString(PyExc_ValueError, "channel doesnt exist");
00480                 return NULL;
00481         }
00482 
00483         PyObject *ret = PyTuple_New(3);
00484         
00485         PyObject *list = PyList_New(3); 
00486         PyList_SET_ITEM(list, 0, PyFloat_FromDouble(pchan->loc[0]));
00487         PyList_SET_ITEM(list, 1, PyFloat_FromDouble(pchan->loc[1]));
00488         PyList_SET_ITEM(list, 2, PyFloat_FromDouble(pchan->loc[2]));
00489         PyTuple_SET_ITEM(ret, 0, list);
00490         
00491         list = PyList_New(3);
00492         PyList_SET_ITEM(list, 0, PyFloat_FromDouble(pchan->size[0]));
00493         PyList_SET_ITEM(list, 1, PyFloat_FromDouble(pchan->size[1]));
00494         PyList_SET_ITEM(list, 2, PyFloat_FromDouble(pchan->size[2]));
00495         PyTuple_SET_ITEM(ret, 1, list);
00496         
00497         list = PyList_New(4);
00498         PyList_SET_ITEM(list, 0, PyFloat_FromDouble(pchan->quat[0]));
00499         PyList_SET_ITEM(list, 1, PyFloat_FromDouble(pchan->quat[1]));
00500         PyList_SET_ITEM(list, 2, PyFloat_FromDouble(pchan->quat[2]));
00501         PyList_SET_ITEM(list, 3, PyFloat_FromDouble(pchan->quat[3]));
00502         PyTuple_SET_ITEM(ret, 2, list);
00503 
00504         return ret;
00505 /*
00506         return Py_BuildValue("([fff][fff][ffff])",
00507                 pchan->loc[0], pchan->loc[1], pchan->loc[2],
00508                 pchan->size[0], pchan->size[1], pchan->size[2],
00509                 pchan->quat[0], pchan->quat[1], pchan->quat[2], pchan->quat[3] );
00510 */
00511 }
00512 
00513 /*     setChannel                                                         */
00514 KX_PYMETHODDEF_DOC(BL_ActionActuator, setChannel,
00515 "setChannel(channel, matrix)\n"
00516 "\t - channel   : A string specifying the name of the bone channel.\n"
00517 "\t - matrix    : A 4x4 matrix specifying the overriding transformation\n"
00518 "\t               as an offset from the bone's rest position.\n")
00519 {
00520         BL_ArmatureObject *obj = (BL_ArmatureObject*)GetParent();
00521         char *string;
00522         PyObject *pymat= NULL;
00523         PyObject *pyloc= NULL, *pysize= NULL, *pyquat= NULL;
00524         bPoseChannel *pchan;
00525         
00526         if(PyTuple_Size(args)==2) {
00527                 if (!PyArg_ParseTuple(args,"sO:setChannel", &string, &pymat)) // matrix
00528                         return NULL;
00529         }
00530         else if(PyTuple_Size(args)==4) {
00531                 if (!PyArg_ParseTuple(args,"sOOO:setChannel", &string, &pyloc, &pysize, &pyquat)) // loc/size/quat
00532                         return NULL;
00533         }
00534         else {
00535                 PyErr_SetString(PyExc_ValueError, "Expected a string and a 4x4 matrix (2 args) or a string and loc/size/quat sequences (4 args)");
00536                 return NULL;
00537         }
00538         
00539         if(pymat) {
00540                 float matrix[4][4];
00541                 MT_Matrix4x4 mat;
00542                 
00543                 if(!PyMatTo(pymat, mat))
00544                         return NULL;
00545                 
00546                 mat.getValue((float*)matrix);
00547                 
00548                 BL_ArmatureObject *obj = (BL_ArmatureObject*)GetParent();
00549                 
00550                 if (!m_userpose) {
00551                         if(!m_pose)
00552                                 obj->GetPose(&m_pose); /* Get the underlying pose from the armature */
00553                         game_copy_pose(&m_userpose, m_pose, 0);
00554                 }
00555                 // pchan= verify_pose_channel(m_userpose, string); // adds the channel if its not there.
00556                 pchan= get_pose_channel(m_userpose, string); // adds the channel if its not there.
00557                 
00558                 if(pchan) {
00559                         VECCOPY (pchan->loc, matrix[3]);
00560                         mat4_to_size( pchan->size,matrix);
00561                         mat4_to_quat( pchan->quat,matrix);
00562                 }
00563         }
00564         else {
00565                 MT_Vector3 loc;
00566                 MT_Vector3 size;
00567                 MT_Quaternion quat;
00568                 
00569                 if (!PyVecTo(pyloc, loc) || !PyVecTo(pysize, size) || !PyQuatTo(pyquat, quat))
00570                         return NULL;
00571                 
00572                 // same as above
00573                 if (!m_userpose) {
00574                         if(!m_pose)
00575                                 obj->GetPose(&m_pose); /* Get the underlying pose from the armature */
00576                         game_copy_pose(&m_userpose, m_pose, 0);
00577                 }
00578                 // pchan= verify_pose_channel(m_userpose, string);
00579                 pchan= get_pose_channel(m_userpose, string); // adds the channel if its not there.
00580                 
00581                 // for some reason loc.setValue(pchan->loc) fails
00582                 if(pchan) {
00583                         pchan->loc[0]= loc[0]; pchan->loc[1]= loc[1]; pchan->loc[2]= loc[2];
00584                         pchan->size[0]= size[0]; pchan->size[1]= size[1]; pchan->size[2]= size[2];
00585                         pchan->quat[0]= quat[3]; pchan->quat[1]= quat[0]; pchan->quat[2]= quat[1]; pchan->quat[3]= quat[2]; /* notice xyzw -> wxyz is intentional */
00586                 }
00587         }
00588         
00589         if(pchan==NULL) {
00590                 PyErr_SetString(PyExc_ValueError, "Channel could not be found, use the 'channelNames' attribute to get a list of valid channels");
00591                 return NULL;
00592         }
00593         
00594         Py_RETURN_NONE;
00595 }
00596 
00597 /* ------------------------------------------------------------------------- */
00598 /* Python Integration Hooks                                                                      */
00599 /* ------------------------------------------------------------------------- */
00600 
00601 PyTypeObject BL_ActionActuator::Type = {
00602         PyVarObject_HEAD_INIT(NULL, 0)
00603         "BL_ActionActuator",
00604         sizeof(PyObjectPlus_Proxy),
00605         0,
00606         py_base_dealloc,
00607         0,
00608         0,
00609         0,
00610         0,
00611         py_base_repr,
00612         0,0,0,0,0,0,0,0,0,
00613         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
00614         0,0,0,0,0,0,0,
00615         Methods,
00616         0,
00617         0,
00618         &SCA_IActuator::Type,
00619         0,0,0,0,0,0,
00620         py_base_new
00621 };
00622 
00623 PyMethodDef BL_ActionActuator::Methods[] = {
00624         {"getChannel", (PyCFunction) BL_ActionActuator::sPyGetChannel, METH_O},
00625         KX_PYMETHODTABLE(BL_ActionActuator, setChannel),
00626         {NULL,NULL} //Sentinel
00627 };
00628 
00629 PyAttributeDef BL_ActionActuator::Attributes[] = {
00630         KX_PYATTRIBUTE_FLOAT_RW("frameStart", 0, MAXFRAMEF, BL_ActionActuator, m_startframe),
00631         KX_PYATTRIBUTE_FLOAT_RW("frameEnd", 0, MAXFRAMEF, BL_ActionActuator, m_endframe),
00632         KX_PYATTRIBUTE_FLOAT_RW("blendIn", 0, MAXFRAMEF, BL_ActionActuator, m_blendin),
00633         KX_PYATTRIBUTE_RW_FUNCTION("action", BL_ActionActuator, pyattr_get_action, pyattr_set_action),
00634         KX_PYATTRIBUTE_RO_FUNCTION("channelNames", BL_ActionActuator, pyattr_get_channel_names),
00635         KX_PYATTRIBUTE_SHORT_RW("priority", 0, 100, false, BL_ActionActuator, m_priority),
00636         KX_PYATTRIBUTE_FLOAT_RW_CHECK("frame", 0, MAXFRAMEF, BL_ActionActuator, m_localtime, CheckFrame),
00637         KX_PYATTRIBUTE_STRING_RW("propName", 0, 31, false, BL_ActionActuator, m_propname),
00638         KX_PYATTRIBUTE_STRING_RW("framePropName", 0, 31, false, BL_ActionActuator, m_framepropname),
00639         KX_PYATTRIBUTE_BOOL_RW("useContinue", BL_ActionActuator, m_end_reset),
00640         KX_PYATTRIBUTE_FLOAT_RW_CHECK("blendTime", 0, MAXFRAMEF, BL_ActionActuator, m_blendframe, CheckBlendTime),
00641         KX_PYATTRIBUTE_SHORT_RW_CHECK("mode",0,100,false,BL_ActionActuator,m_playtype,CheckType),
00642         { NULL }        //Sentinel
00643 };
00644 
00645 PyObject* BL_ActionActuator::pyattr_get_action(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
00646 {
00647         BL_ActionActuator* self= static_cast<BL_ActionActuator*>(self_v);
00648         return PyUnicode_FromString(self->GetAction() ? self->GetAction()->id.name+2 : "");
00649 }
00650 
00651 int BL_ActionActuator::pyattr_set_action(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
00652 {
00653         BL_ActionActuator* self= static_cast<BL_ActionActuator*>(self_v);
00654         
00655         if (!PyUnicode_Check(value))
00656         {
00657                 PyErr_SetString(PyExc_ValueError, "actuator.action = val: Action Actuator, expected the string name of the action");
00658                 return PY_SET_ATTR_FAIL;
00659         }
00660 
00661         bAction *action= NULL;
00662         STR_String val = _PyUnicode_AsString(value);
00663         
00664         if (val != "")
00665         {
00666                 action= (bAction*)SCA_ILogicBrick::m_sCurrentLogicManager->GetActionByName(val);
00667                 if (!action)
00668                 {
00669                         PyErr_SetString(PyExc_ValueError, "actuator.action = val: Action Actuator, action not found!");
00670                         return PY_SET_ATTR_FAIL;
00671                 }
00672         }
00673         
00674         self->SetAction(action);
00675         return PY_SET_ATTR_SUCCESS;
00676 
00677 }
00678 
00679 PyObject* BL_ActionActuator::pyattr_get_channel_names(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
00680 {
00681         BL_ActionActuator* self= static_cast<BL_ActionActuator*>(self_v);
00682         PyObject *ret= PyList_New(0);
00683         PyObject *item;
00684         
00685         bPose *pose= ((BL_ArmatureObject*)self->GetParent())->GetOrigPose();
00686         
00687         if(pose) {
00688                 bPoseChannel *pchan;
00689                 for(pchan= (bPoseChannel *)pose->chanbase.first; pchan; pchan= (bPoseChannel *)pchan->next) {
00690                         item= PyUnicode_FromString(pchan->name);
00691                         PyList_Append(ret, item);
00692                         Py_DECREF(item);
00693                 }
00694         }
00695         
00696         return ret;
00697 }
00698 
00699 #endif // WITH_PYTHON