diff --git a/source/gameengine/Converter/BL_Action.cpp b/source/gameengine/Converter/BL_Action.cpp new file mode 100644 index 00000000000..dcd0c1402e1 --- /dev/null +++ b/source/gameengine/Converter/BL_Action.cpp @@ -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_blendframeGetMRDPose(&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 index 00000000000..3d977f3984a --- /dev/null +++ b/source/gameengine/Converter/BL_Action.h @@ -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 index 00000000000..359a7389b51 --- /dev/null +++ b/source/gameengine/Converter/BL_ActionManager.cpp @@ -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; iIsDone()) + 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 index 00000000000..409d6c24e30 --- /dev/null +++ b/source/gameengine/Converter/BL_ActionManager.h @@ -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 + diff --git a/source/gameengine/Converter/CMakeLists.txt b/source/gameengine/Converter/CMakeLists.txt index bdd0769e0a3..8f0e8eb5113 100644 --- a/source/gameengine/Converter/CMakeLists.txt +++ b/source/gameengine/Converter/CMakeLists.txt @@ -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 diff --git a/source/gameengine/Expressions/PyObjectPlus.h b/source/gameengine/Expressions/PyObjectPlus.h index 157124ebc81..88a9f3afd7d 100644 --- a/source/gameengine/Expressions/PyObjectPlus.h +++ b/source/gameengine/Expressions/PyObjectPlus.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; \ diff --git a/source/gameengine/Ketsji/KX_GameObject.cpp b/source/gameengine/Ketsji/KX_GameObject.cpp index 47d83c16659..868dc4591e4 100644 --- a/source/gameengine/Ketsji/KX_GameObject.cpp +++ b/source/gameengine/Ketsji/KX_GameObject.cpp @@ -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(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 */ diff --git a/source/gameengine/Ketsji/KX_GameObject.h b/source/gameengine/Ketsji/KX_GameObject.h index 50fbebe1341..a5ca5f872d5 100644 --- a/source/gameengine/Ketsji/KX_GameObject.h +++ b/source/gameengine/Ketsji/KX_GameObject.h @@ -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); diff --git a/source/gameengine/Ketsji/KX_Scene.cpp b/source/gameengine/Ketsji/KX_Scene.cpp index 28dc660037c..27bf5d19b14 100644 --- a/source/gameengine/Ketsji/KX_Scene.cpp +++ b/source/gameengine/Ketsji/KX_Scene.cpp @@ -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; iGetCount(); ++i) + ((KX_GameObject*)GetObjectList()->GetValue(i))->UpdateActionManager(curtime); m_logicmgr->UpdateFrame(curtime, frame); }