BGE Animations:
authorMitchell Stokes <mogurijin@gmail.com>
Tue, 24 May 2011 07:52:29 +0000 (07:52 +0000)
committerMitchell Stokes <mogurijin@gmail.com>
Tue, 24 May 2011 07:52:29 +0000 (07:52 +0000)
  * Adding a BL_Action and a BL_ActionManager
  * Each KX_GameObject has a BL_ActionManager, which can control up to for BL_Action objects at a given time
  * Currently, the only interface to BL_ActionManager is through KX_GameObject via Python
  * Only armature animations are currently supported

source/gameengine/Converter/BL_Action.cpp [new file with mode: 0644]
source/gameengine/Converter/BL_Action.h [new file with mode: 0644]
source/gameengine/Converter/BL_ActionManager.cpp [new file with mode: 0644]
source/gameengine/Converter/BL_ActionManager.h [new file with mode: 0644]
source/gameengine/Converter/CMakeLists.txt
source/gameengine/Expressions/PyObjectPlus.h
source/gameengine/Ketsji/KX_GameObject.cpp
source/gameengine/Ketsji/KX_GameObject.h
source/gameengine/Ketsji/KX_Scene.cpp

diff --git a/source/gameengine/Converter/BL_Action.cpp b/source/gameengine/Converter/BL_Action.cpp
new file mode 100644 (file)
index 0000000..dcd0c14
--- /dev/null
@@ -0,0 +1,198 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "BL_Action.h"
+#include "BL_ArmatureObject.h"
+#include "KX_GameObject.h"
+
+// These three are for getting the action from the logic manager
+#include "KX_Scene.h"
+#include "KX_PythonInit.h"
+#include "SCA_LogicManager.h"
+
+extern "C" {
+#include "BKE_animsys.h"
+#include "BKE_action.h"
+#include "RNA_access.h"
+#include "RNA_define.h"
+}
+
+BL_Action::BL_Action(class KX_GameObject* gameobj,
+                                       const char* name,
+                                       float start,
+                                       float end,
+                                       float blendin,
+                                       short play_mode,
+                                       short blend_mode)
+:
+       m_obj(gameobj),
+       m_startframe(start),
+       m_endframe(end),
+       m_blendin(blendin),
+       m_playmode(play_mode),
+       m_endtime(0.f),
+       m_localtime(start),
+       m_blendframe(0.f),
+       m_blendstart(0.f),
+       m_pose(NULL),
+       m_blendpose(NULL),
+       m_done(false)
+{
+       m_starttime = KX_GetActiveEngine()->GetFrameTime();
+       m_action = (bAction*)KX_GetActiveScene()->GetLogicManager()->GetActionByName(name);
+
+       if (!m_action) printf("Failed to load action: %s\n", name);
+}
+
+BL_Action::~BL_Action()
+{
+       if (m_pose)
+               game_free_pose(m_pose);
+       if (m_blendpose)
+               game_free_pose(m_blendpose);
+}
+
+void BL_Action::SetLocalTime(float curtime)
+{
+       float dt = (curtime-m_starttime)*KX_KetsjiEngine::GetAnimFrameRate();
+
+       if (m_endframe < m_startframe)
+               dt = -dt;
+
+       m_localtime = m_startframe + dt;
+}
+
+void BL_Action::Update(float curtime)
+{
+       curtime -= KX_KetsjiEngine::GetSuspendedDelta();
+
+       SetLocalTime(curtime);
+
+       // Handle wrap around
+       if (m_localtime < m_startframe || m_localtime > m_endframe)
+       {
+               switch(m_playmode)
+               {
+               case ACT_MODE_PLAY:
+                       // Clamp
+                       m_localtime = m_endframe;
+                       m_done = true;
+                       break;
+               case ACT_MODE_LOOP:
+                       // Put the time back to the beginning
+                       m_localtime = m_startframe;
+                       m_starttime = curtime;
+                       break;
+               case ACT_MODE_PING_PONG:
+                       // Swap the start and end frames
+                       float temp = m_startframe;
+                       m_startframe = m_endframe;
+                       m_endframe = temp;
+
+                       m_starttime = curtime;
+
+                       break;
+               }
+       }
+
+       if (m_obj->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE)
+       {
+               bPose* prev_pose = NULL;
+               BL_ArmatureObject *obj = (BL_ArmatureObject*)m_obj;
+               obj->GetPose(&m_pose);
+
+               // Save the old pose if we need to do some layer blending
+               if (m_blendmode != ACT_BLEND_NONE)
+                       obj->GetMRDPose(&prev_pose);
+
+               // Extract the pose from the action
+               {
+                       struct PointerRNA id_ptr;
+                       Object *arm = obj->GetArmatureObject();
+                       bPose *temp = arm->pose;
+
+                       arm->pose = m_pose;
+                       RNA_id_pointer_create((ID*)arm, &id_ptr);
+                       animsys_evaluate_action(&id_ptr, m_action, NULL, m_localtime);
+
+                       arm->pose = temp;
+               }
+
+               // Handle blending between layers
+               switch(m_blendmode)
+               {
+               case ACT_BLEND_MIX:
+                       game_blend_poses(m_pose, prev_pose, 0.5f);
+                       break;
+               case ACT_BLEND_NONE:
+               default:
+                       break;
+               }
+
+               // Handle blending between actions
+               if (m_blendin && m_blendframe<m_blendin)
+               {
+                       if (!m_blendpose)
+                       {
+                               obj->GetMRDPose(&m_blendpose);
+                               m_blendstart = curtime;
+                       }
+
+                       // Calculate weight
+                       float weight = 1.f - (m_blendframe/m_blendin);
+                       game_blend_poses(m_pose, m_blendpose, weight);
+
+                       // Bump the blend frame
+                       m_blendframe = (curtime - m_blendstart)*KX_KetsjiEngine::GetAnimFrameRate();
+
+                       // Clamp
+                       if (m_blendframe>m_blendin)
+                               m_blendframe = m_blendin;
+               }
+               else
+               {
+                       if (m_blendpose)
+                       {
+                               game_free_pose(m_blendpose);
+                               m_blendpose = NULL;
+                       }
+               }
+
+               obj->SetPose(m_pose);
+
+               obj->SetActiveAction(NULL, 0, curtime);
+
+               if (prev_pose)
+                       game_free_pose(prev_pose);
+       }
+       else
+       {
+               printf("Only armature actions are currently supported\n");
+       }
+}
diff --git a/source/gameengine/Converter/BL_Action.h b/source/gameengine/Converter/BL_Action.h
new file mode 100644 (file)
index 0000000..3d977f3
--- /dev/null
@@ -0,0 +1,98 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+#ifndef __BL_ACTION
+#define __BL_ACTION
+
+#ifdef WITH_CXX_GUARDEDALLOC
+#include "MEM_guardedalloc.h"
+#endif
+
+
+class BL_Action
+{
+private:
+       struct bAction* m_action;
+       struct bPose* m_pose;
+       struct bPose* m_blendpose;
+       struct PointerRNA *m_ptrrna;
+       class KX_GameObject* m_obj;
+
+       float m_startframe;
+       float m_endframe;
+       float m_starttime;
+       float m_endtime;
+       float m_localtime;
+
+       float m_blendin;
+       float m_blendframe;
+       float m_blendstart;
+
+       short m_playmode;
+       short m_blendmode;
+
+       bool m_done;
+
+       void SetLocalTime(float curtime);
+public:
+       BL_Action(class KX_GameObject* gameobj,
+                       const char* name,
+                       float start,
+                       float end,
+                       float blendin,
+                       short play_mode,
+                       short blend_mode);
+       ~BL_Action();
+
+       bool IsDone() {return m_done;}
+       void Update(float curtime);
+
+       enum 
+       {
+               ACT_MODE_PLAY = 0,
+               ACT_MODE_LOOP,
+               ACT_MODE_PING_PONG,
+               ACT_MODE_MAX,
+       };
+
+       enum
+       {
+               ACT_BLEND_NONE = 0,
+               ACT_BLEND_MIX,
+               ACT_BLEND_MAX,
+       };
+
+#ifdef WITH_CXX_GUARDEDALLOC
+public:
+       void *operator new(size_t num_bytes) { return MEM_mallocN(num_bytes, "GE:BL_Action"); }
+       void operator delete( void *mem ) { MEM_freeN(mem); }
+#endif
+};
+
+#endif //BL_ACTION
+
diff --git a/source/gameengine/Converter/BL_ActionManager.cpp b/source/gameengine/Converter/BL_ActionManager.cpp
new file mode 100644 (file)
index 0000000..359a738
--- /dev/null
@@ -0,0 +1,80 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "BL_ActionManager.h"
+
+BL_ActionManager::BL_ActionManager()
+{
+       for (int i=0; i<MAX_ACTION_LAYERS; ++i)
+               m_layers[i] = 0;
+}
+
+BL_ActionManager::~BL_ActionManager()
+{
+       for (int i=0; i<MAX_ACTION_LAYERS; ++i)
+               if (m_layers[i])
+                       StopAction(i);
+}
+
+void BL_ActionManager::PlayAction(class KX_GameObject* gameobj,
+                                                               const char* name,
+                                                               float start,
+                                                               float end,
+                                                               short layer,
+                                                               float blendin,
+                                                               short play_mode,
+                                                               short blend_mode)
+{
+       // Remove a currently running action on this layer if there is one
+       if (m_layers[layer])
+               StopAction(layer);
+
+       // Create a new action
+       m_layers[layer] = new BL_Action(gameobj, name, start, end, blendin, play_mode, blend_mode);
+}
+
+void BL_ActionManager::StopAction(short layer)
+{
+       delete m_layers[layer];
+       m_layers[layer] = 0;
+}
+
+void BL_ActionManager::Update(float curtime)
+{
+       for (int i=0; i<MAX_ACTION_LAYERS; ++i)
+       {
+               if (m_layers[i])
+               {
+                       if (m_layers[i]->IsDone())
+                               StopAction(i);
+                       else
+                               m_layers[i]->Update(curtime);
+               }
+       }
+}
diff --git a/source/gameengine/Converter/BL_ActionManager.h b/source/gameengine/Converter/BL_ActionManager.h
new file mode 100644 (file)
index 0000000..409d6c2
--- /dev/null
@@ -0,0 +1,64 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+#ifndef __BL_ACTIONMANAGER
+#define __BL_ACTIONMANAGER
+
+#include "BL_Action.h"
+
+#define MAX_ACTION_LAYERS 4
+
+class BL_ActionManager
+{
+private:
+       BL_Action* m_layers[MAX_ACTION_LAYERS];
+
+public:
+       BL_ActionManager();
+       ~BL_ActionManager();
+
+       void PlayAction(class KX_GameObject* gameobj,
+                                       const char* name,
+                                       float start,
+                                       float end,
+                                       short layer=0,
+                                       float blendin=0.f,
+                                       short play_mode=0,
+                                       short blend_mode=0);
+
+       void StopAction(short layer);
+       void Update(float);
+#ifdef WITH_CXX_GUARDEDALLOC
+public:
+       void *operator new(size_t num_bytes) { return MEM_mallocN(num_bytes, "GE:BL_ActionManager"); }
+       void operator delete( void *mem ) { MEM_freeN(mem); }
+#endif
+};
+
+#endif //BL_ACTIONMANAGER
+
index bdd0769e0a38c3ff44693fd6b9055d9f8fc2d37a..8f0e8eb511395b2aa9caf4f12abcb08e508d337f 100644 (file)
@@ -60,7 +60,9 @@ set(INC
 )
 
 set(SRC
+       BL_Action.cpp
        BL_ActionActuator.cpp
+       BL_ActionManager.cpp
        BL_ArmatureActuator.cpp
        BL_ArmatureChannel.cpp
        BL_ArmatureConstraint.cpp
@@ -82,7 +84,9 @@ set(SRC
        KX_IpoConvert.cpp
        KX_SoftBodyDeformer.cpp
 
+       BL_Action.h
        BL_ActionActuator.h
+       BL_ActionManager.h
        BL_ArmatureActuator.h
        BL_ArmatureChannel.h
        BL_ArmatureConstraint.h
index 157124ebc810692fa80a9bfab2fcfb2e5072b865..88a9f3afd7d8fff500a9edb37524ecd3934aa28c 100644 (file)
@@ -254,12 +254,15 @@ typedef struct PyObjectPlus_Proxy {
 #define KX_PYMETHODTABLE_NOARGS(class_name, method_name) \
        {#method_name , (PyCFunction) class_name::sPy##method_name, METH_NOARGS, (const char *)class_name::method_name##_doc}
 
+#define KX_PYMETHODTABLE_KEYWORDS(class_name, method_name) \
+       {#method_name , (PyCFunction) class_name::sPy##method_name, METH_VARARGS|METH_KEYWORDS, (const char *)class_name::method_name##_doc}
+
 /**
  * Function implementation macro
  */
 #define KX_PYMETHODDEF_DOC(class_name, method_name, doc_string) \
 const char class_name::method_name##_doc[] = doc_string; \
-PyObject* class_name::Py##method_name(PyObject* args, PyObject*)
+PyObject* class_name::Py##method_name(PyObject* args, PyObject* kwds)
 
 #define KX_PYMETHODDEF_DOC_VARARGS(class_name, method_name, doc_string) \
 const char class_name::method_name##_doc[] = doc_string; \
index 47d83c16659b255bf291c9f116366b201ddff83b..868dc4591e42598450f60d01d0fb05033466f7ba 100644 (file)
@@ -74,6 +74,8 @@ typedef unsigned long uint_ptr;
 #include "SCA_IController.h"
 #include "NG_NetworkScene.h" //Needed for sendMessage()
 
+#include "BL_ActionManager.h"
+
 #include "PyObjectPlus.h" /* python stuff */
 
 // This file defines relationships between parents and children
@@ -121,6 +123,8 @@ KX_GameObject::KX_GameObject(
        KX_NormalParentRelation * parent_relation = 
                KX_NormalParentRelation::New();
        m_pSGNode->SetParentRelation(parent_relation);
+
+       m_actionManager = new BL_ActionManager();
 };
 
 
@@ -154,6 +158,10 @@ KX_GameObject::~KX_GameObject()
        {
                delete m_pGraphicController;
        }
+       if (m_actionManager)
+       {
+               delete m_actionManager;
+       }
 #ifdef WITH_PYTHON
        if (m_attr_dict) {
                PyDict_Clear(m_attr_dict); /* incase of circular refs or other weird cases */
@@ -344,6 +352,11 @@ void KX_GameObject::RemoveParent(KX_Scene *scene)
        }
 }
 
+void KX_GameObject::UpdateActionManager(float curtime)
+{
+       m_actionManager->Update(curtime);
+}
+
 void KX_GameObject::ProcessReplica()
 {
        SCA_IObject::ProcessReplica();
@@ -353,6 +366,7 @@ void KX_GameObject::ProcessReplica()
        m_pSGNode = NULL;
        m_pClient_info = new KX_ClientObjectInfo(*m_pClient_info);
        m_pClient_info->m_gameobject = this;
+       m_actionManager = new BL_ActionManager();
        m_state = 0;
 
 #ifdef WITH_PYTHON
@@ -1497,6 +1511,8 @@ PyMethodDef KX_GameObject::Methods[] = {
        KX_PYMETHODTABLE_O(KX_GameObject, getDistanceTo),
        KX_PYMETHODTABLE_O(KX_GameObject, getVectTo),
        KX_PYMETHODTABLE(KX_GameObject, sendMessage),
+
+       KX_PYMETHODTABLE_KEYWORDS(KX_GameObject, playAction),
        
        // dict style access for props
        {"get",(PyCFunction) KX_GameObject::sPyget, METH_VARARGS},
@@ -2975,6 +2991,43 @@ KX_PYMETHODDEF_DOC_VARARGS(KX_GameObject, sendMessage,
        Py_RETURN_NONE;
 }
 
+KX_PYMETHODDEF_DOC(KX_GameObject, playAction,
+       "playAction(name, start_frame, end_frame, layer=0, blendin=0, play_mode=ACT_MODE_PLAY, blend_mode=ACT_BLEND_NONE)\n"
+       "plays an action\n")
+{
+       const char* name;
+       float start, end, blendin=0.f;
+       short layer=0;
+       short play_mode=0, blend_mode=0;
+
+       static const char *kwlist[] = {"name", "start_frame", "end_frame", "layer", "blendin", "play_mode", "blend_mode", NULL};
+
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "sff|hfhh", const_cast<char**>(kwlist),
+                                                                       &name, &start, &end, &layer, &blendin, &play_mode, &blend_mode))
+               return NULL;
+
+       if (layer < 0 || layer > MAX_ACTION_LAYERS)
+       {
+               printf("KX_GameObject.playAction(): given layer (%d) is out of range (0 - %d), setting to 0", layer, MAX_ACTION_LAYERS-1);
+               layer = 0;
+       }
+
+       if (play_mode < 0 || play_mode > BL_Action::ACT_MODE_MAX)
+       {
+               printf("KX_GameObject.playAction(): given play_mode (%d) is out of range (0 - %d), setting to ACT_MODE_PLAY", play_mode, BL_Action::ACT_MODE_MAX-1);
+               play_mode = BL_Action::ACT_MODE_MAX;
+       }
+
+       if (blend_mode < 0 || blend_mode > BL_Action::ACT_BLEND_MAX)
+       {
+               printf("KX_GameObject.playAction(): given blend_mode (%d) is out of range (0 - %d), setting to ACT_BLEND_NONE", blend_mode, BL_Action::ACT_BLEND_MAX-1);
+               blend_mode = BL_Action::ACT_BLEND_NONE;
+       }
+
+       m_actionManager->PlayAction(this, name, start, end, layer, blendin, play_mode, blend_mode);
+
+       Py_RETURN_NONE;
+}
 /* dict style access */
 
 
index 50fbebe1341d86b66d215ae330bbebe98b0b9c5b..a5ca5f872d5caf388c4c34c6e874dda8f12633e6 100644 (file)
@@ -63,6 +63,7 @@ class RAS_MeshObject;
 class KX_IPhysicsController;
 class PHY_IGraphicController;
 class PHY_IPhysicsEnvironment;
+class BL_ActionManager;
 struct Object;
 
 #ifdef WITH_PYTHON
@@ -112,6 +113,9 @@ protected:
        SG_Node*                                                        m_pSGNode;
 
        MT_CmMatrix4x4                                          m_OpenGL_4x4Matrix;
+
+       // The action manager is used to play/stop/update actions
+       BL_ActionManager*                               m_actionManager;
        
 public:
        bool                                                            m_isDeformable;
@@ -198,6 +202,11 @@ public:
         */                     
        void RemoveParent(KX_Scene *scene);
 
+       /**
+        * Kick the object's action manager
+        */
+       void UpdateActionManager(float curtime);
+
        /**
         * Construct a game object. This class also inherits the 
         * default constructors - use those with care!
@@ -853,6 +862,8 @@ public:
        KX_PYMETHOD_DOC_O(KX_GameObject,getVectTo);
        KX_PYMETHOD_DOC_VARARGS(KX_GameObject, sendMessage);
        KX_PYMETHOD_VARARGS(KX_GameObject, ReinstancePhysicsMesh);
+
+       KX_PYMETHOD_DOC(KX_GameObject, playAction);
        
        /* Dict access */
        KX_PYMETHOD_VARARGS(KX_GameObject,get);
index 28dc660037c5046cc04ba5a5399d18d56393035f..27bf5d19b14b66700e6818aacf898448281cdfac 100644 (file)
@@ -1506,6 +1506,9 @@ void KX_Scene::LogicBeginFrame(double curtime)
 
 void KX_Scene::LogicUpdateFrame(double curtime, bool frame)
 {
+       // Update any animations
+       for (int i=0; i<GetObjectList()->GetCount(); ++i)
+               ((KX_GameObject*)GetObjectList()->GetValue(i))->UpdateActionManager(curtime);
        m_logicmgr->UpdateFrame(curtime, frame);
 }