BGE Animations:

* 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
This commit is contained in:
Mitchell Stokes 2011-05-24 07:52:29 +00:00
parent c6881d08e5
commit 017fa2c19f
9 changed files with 515 additions and 1 deletions

@ -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");
}
}

@ -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

@ -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);
}
}
}

@ -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

@ -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

@ -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; \

@ -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
@ -1498,6 +1512,8 @@ PyMethodDef KX_GameObject::Methods[] = {
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 */

@ -63,6 +63,7 @@ class RAS_MeshObject;
class KX_IPhysicsController;
class PHY_IGraphicController;
class PHY_IPhysicsEnvironment;
class BL_ActionManager;
struct Object;
#ifdef WITH_PYTHON
@ -113,6 +114,9 @@ protected:
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!
@ -854,6 +863,8 @@ public:
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);

@ -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);
}