From 07dca944766e702453730fe1a55605dab9380c6e Mon Sep 17 00:00:00 2001 From: Mitchell Stokes Date: Sat, 28 May 2011 07:15:27 +0000 Subject: BGE Animation: * Adding IPOs to BL_Action * Adding a "speed" option to adjust the playback speed by some factor --- .../Converter/KX_BlenderScalarInterpolator.cpp | 6 +- .../Converter/KX_BlenderScalarInterpolator.h | 2 +- source/gameengine/Converter/KX_IpoConvert.cpp | 210 +++++++++++---------- source/gameengine/Converter/KX_IpoConvert.h | 4 + source/gameengine/Ketsji/BL_Action.cpp | 48 ++++- source/gameengine/Ketsji/BL_Action.h | 7 +- source/gameengine/Ketsji/BL_ActionManager.cpp | 5 +- source/gameengine/Ketsji/BL_ActionManager.h | 3 +- source/gameengine/Ketsji/KX_GameObject.cpp | 12 +- source/gameengine/SceneGraph/SG_IObject.cpp | 12 ++ source/gameengine/SceneGraph/SG_IObject.h | 10 + 11 files changed, 197 insertions(+), 122 deletions(-) diff --git a/source/gameengine/Converter/KX_BlenderScalarInterpolator.cpp b/source/gameengine/Converter/KX_BlenderScalarInterpolator.cpp index a9a3e66f996..75c0e012226 100644 --- a/source/gameengine/Converter/KX_BlenderScalarInterpolator.cpp +++ b/source/gameengine/Converter/KX_BlenderScalarInterpolator.cpp @@ -47,11 +47,11 @@ float BL_ScalarInterpolator::GetValue(float currentTime) const { return evaluate_fcurve(m_fcu, currentTime); } -BL_InterpolatorList::BL_InterpolatorList(struct AnimData *adt) { - if(adt->action==NULL) +BL_InterpolatorList::BL_InterpolatorList(bAction *action) { + if(action==NULL) return; - for(FCurve *fcu= (FCurve *)adt->action->curves.first; fcu; fcu= (FCurve *)fcu->next) { + for(FCurve *fcu= (FCurve *)action->curves.first; fcu; fcu= (FCurve *)fcu->next) { if(fcu->rna_path) { BL_ScalarInterpolator *new_ipo = new BL_ScalarInterpolator(fcu); //assert(new_ipo); diff --git a/source/gameengine/Converter/KX_BlenderScalarInterpolator.h b/source/gameengine/Converter/KX_BlenderScalarInterpolator.h index bd786bae969..cca66b3771c 100644 --- a/source/gameengine/Converter/KX_BlenderScalarInterpolator.h +++ b/source/gameengine/Converter/KX_BlenderScalarInterpolator.h @@ -66,7 +66,7 @@ public: class BL_InterpolatorList : public std::vector { public: - BL_InterpolatorList(struct AnimData *adt); + BL_InterpolatorList(struct bAction *action); ~BL_InterpolatorList(); KX_IScalarInterpolator *GetScalarInterpolator(const char *rna_path, int array_index); diff --git a/source/gameengine/Converter/KX_IpoConvert.cpp b/source/gameengine/Converter/KX_IpoConvert.cpp index 2ad56717e26..27ae857f45c 100644 --- a/source/gameengine/Converter/KX_IpoConvert.cpp +++ b/source/gameengine/Converter/KX_IpoConvert.cpp @@ -54,6 +54,7 @@ #include "DNA_object_types.h" #include "DNA_action_types.h" +#include "DNA_anim_types.h" #include "DNA_ipo_types.h" #include "DNA_lamp_types.h" #include "DNA_world_types.h" @@ -76,133 +77,134 @@ static BL_InterpolatorList *GetAdtList(struct AnimData *for_adt, KX_BlenderScene BL_InterpolatorList *adtList= converter->FindInterpolatorList(for_adt); if (!adtList) { - adtList = new BL_InterpolatorList(for_adt); + adtList = new BL_InterpolatorList(for_adt->action); converter->RegisterInterpolatorList(adtList, for_adt); } return adtList; } -void BL_ConvertIpos(struct Object* blenderobject,KX_GameObject* gameobj,KX_BlenderSceneConverter *converter) +SG_Controller *BL_CreateIPO(struct bAction *action, KX_GameObject* gameobj, KX_BlenderSceneConverter *converter) { - if (blenderobject->adt) { - - KX_IpoSGController* ipocontr = new KX_IpoSGController(); - gameobj->GetSGNode()->AddSGController(ipocontr); - ipocontr->SetObject(gameobj->GetSGNode()); - - // For ipo_as_force, we need to know which SM object and Scene the - // object associated with this ipo is in. Is this already known here? - // I think not.... then it must be done later :( -// ipocontr->SetSumoReference(gameobj->GetSumoScene(), -// gameobj->GetSumoObject()); - - ipocontr->SetGameObject(gameobj); - - ipocontr->GetIPOTransform().SetPosition( - MT_Point3( - blenderobject->loc[0]/*+blenderobject->dloc[0]*/, - blenderobject->loc[1]/*+blenderobject->dloc[1]*/, - blenderobject->loc[2]/*+blenderobject->dloc[2]*/ - ) - ); - ipocontr->GetIPOTransform().SetEulerAngles( - MT_Vector3( - blenderobject->rot[0], - blenderobject->rot[1], - blenderobject->rot[2] - ) - ); - ipocontr->GetIPOTransform().SetScaling( - MT_Vector3( - blenderobject->size[0], - blenderobject->size[1], - blenderobject->size[2] - ) - ); - - const char *rotmode, *drotmode; - - switch(blenderobject->rotmode) - { - case ROT_MODE_AXISANGLE: - rotmode = "rotation_axis_angle"; - drotmode = "delta_rotation_axis_angle"; - case ROT_MODE_QUAT: - rotmode = "rotation_quaternion"; - drotmode = "delta_rotation_quaternion"; - default: - rotmode = "rotation_euler"; - drotmode = "delta_rotation_euler"; - } + KX_IpoSGController* ipocontr = new KX_IpoSGController(); + ipocontr->SetGameObject(gameobj); + + Object* blenderobject = gameobj->GetBlenderObject(); + + ipocontr->GetIPOTransform().SetPosition( + MT_Point3( + blenderobject->loc[0]/*+blenderobject->dloc[0]*/, + blenderobject->loc[1]/*+blenderobject->dloc[1]*/, + blenderobject->loc[2]/*+blenderobject->dloc[2]*/ + ) + ); + ipocontr->GetIPOTransform().SetEulerAngles( + MT_Vector3( + blenderobject->rot[0], + blenderobject->rot[1], + blenderobject->rot[2] + ) + ); + ipocontr->GetIPOTransform().SetScaling( + MT_Vector3( + blenderobject->size[0], + blenderobject->size[1], + blenderobject->size[2] + ) + ); + + const char *rotmode, *drotmode; + + switch(blenderobject->rotmode) + { + case ROT_MODE_AXISANGLE: + rotmode = "rotation_axis_angle"; + drotmode = "delta_rotation_axis_angle"; + case ROT_MODE_QUAT: + rotmode = "rotation_quaternion"; + drotmode = "delta_rotation_quaternion"; + default: + rotmode = "rotation_euler"; + drotmode = "delta_rotation_euler"; + } - BL_InterpolatorList *adtList= GetAdtList(blenderobject->adt, converter); + BL_InterpolatorList *adtList= GetAdtList(blenderobject->adt, converter); - // For each active channel in the adtList add an - // interpolator to the game object. + // For each active channel in the adtList add an + // interpolator to the game object. - KX_IInterpolator *interpolator; - KX_IScalarInterpolator *interp; + KX_IInterpolator *interpolator; + KX_IScalarInterpolator *interp; - for(int i=0; i<3; i++) { - if ((interp = adtList->GetScalarInterpolator("location", i))) { - interpolator= new KX_ScalarInterpolator(&(ipocontr->GetIPOTransform().GetPosition()[i]), interp); - ipocontr->AddInterpolator(interpolator); - ipocontr->SetIPOChannelActive(OB_LOC_X+i, true); - } + for(int i=0; i<3; i++) { + if ((interp = adtList->GetScalarInterpolator("location", i))) { + interpolator= new KX_ScalarInterpolator(&(ipocontr->GetIPOTransform().GetPosition()[i]), interp); + ipocontr->AddInterpolator(interpolator); + ipocontr->SetIPOChannelActive(OB_LOC_X+i, true); } - for(int i=0; i<3; i++) { - if ((interp = adtList->GetScalarInterpolator("delta_location", i))) { - interpolator= new KX_ScalarInterpolator(&(ipocontr->GetIPOTransform().GetDeltaPosition()[i]), interp); - ipocontr->AddInterpolator(interpolator); - ipocontr->SetIPOChannelActive(OB_DLOC_X+i, true); - } + } + for(int i=0; i<3; i++) { + if ((interp = adtList->GetScalarInterpolator("delta_location", i))) { + interpolator= new KX_ScalarInterpolator(&(ipocontr->GetIPOTransform().GetDeltaPosition()[i]), interp); + ipocontr->AddInterpolator(interpolator); + ipocontr->SetIPOChannelActive(OB_DLOC_X+i, true); } - for(int i=0; i<3; i++) { - if ((interp = adtList->GetScalarInterpolator(rotmode, i))) { - interpolator= new KX_ScalarInterpolator(&(ipocontr->GetIPOTransform().GetEulerAngles()[i]), interp); - ipocontr->AddInterpolator(interpolator); - ipocontr->SetIPOChannelActive(OB_ROT_X+i, true); - } + } + for(int i=0; i<3; i++) { + if ((interp = adtList->GetScalarInterpolator(rotmode, i))) { + interpolator= new KX_ScalarInterpolator(&(ipocontr->GetIPOTransform().GetEulerAngles()[i]), interp); + ipocontr->AddInterpolator(interpolator); + ipocontr->SetIPOChannelActive(OB_ROT_X+i, true); } - for(int i=0; i<3; i++) { - if ((interp = adtList->GetScalarInterpolator(drotmode, i))) { - interpolator= new KX_ScalarInterpolator(&(ipocontr->GetIPOTransform().GetDeltaEulerAngles()[i]), interp); - ipocontr->AddInterpolator(interpolator); - ipocontr->SetIPOChannelActive(OB_DROT_X+i, true); - } + } + for(int i=0; i<3; i++) { + if ((interp = adtList->GetScalarInterpolator(drotmode, i))) { + interpolator= new KX_ScalarInterpolator(&(ipocontr->GetIPOTransform().GetDeltaEulerAngles()[i]), interp); + ipocontr->AddInterpolator(interpolator); + ipocontr->SetIPOChannelActive(OB_DROT_X+i, true); } - for(int i=0; i<3; i++) { - if ((interp = adtList->GetScalarInterpolator("scale", i))) { - interpolator= new KX_ScalarInterpolator(&(ipocontr->GetIPOTransform().GetScaling()[i]), interp); - ipocontr->AddInterpolator(interpolator); - ipocontr->SetIPOChannelActive(OB_SIZE_X+i, true); - } + } + for(int i=0; i<3; i++) { + if ((interp = adtList->GetScalarInterpolator("scale", i))) { + interpolator= new KX_ScalarInterpolator(&(ipocontr->GetIPOTransform().GetScaling()[i]), interp); + ipocontr->AddInterpolator(interpolator); + ipocontr->SetIPOChannelActive(OB_SIZE_X+i, true); } - for(int i=0; i<3; i++) { - if ((interp = adtList->GetScalarInterpolator("delta_scale", i))) { - interpolator= new KX_ScalarInterpolator(&(ipocontr->GetIPOTransform().GetDeltaScaling()[i]), interp); - ipocontr->AddInterpolator(interpolator); - ipocontr->SetIPOChannelActive(OB_DSIZE_X+i, true); - } + } + for(int i=0; i<3; i++) { + if ((interp = adtList->GetScalarInterpolator("delta_scale", i))) { + interpolator= new KX_ScalarInterpolator(&(ipocontr->GetIPOTransform().GetDeltaScaling()[i]), interp); + ipocontr->AddInterpolator(interpolator); + ipocontr->SetIPOChannelActive(OB_DSIZE_X+i, true); } + } - { - KX_ObColorIpoSGController* ipocontr_obcol=NULL; + { + KX_ObColorIpoSGController* ipocontr_obcol=NULL; - for(int i=0; i<4; i++) { - if ((interp = adtList->GetScalarInterpolator("color", i))) { - if (!ipocontr_obcol) { - ipocontr_obcol = new KX_ObColorIpoSGController(); - gameobj->GetSGNode()->AddSGController(ipocontr_obcol); - ipocontr_obcol->SetObject(gameobj->GetSGNode()); - } - interpolator= new KX_ScalarInterpolator(&ipocontr_obcol->m_rgba[i], interp); - ipocontr_obcol->AddInterpolator(interpolator); + for(int i=0; i<4; i++) { + if ((interp = adtList->GetScalarInterpolator("color", i))) { + if (!ipocontr_obcol) { + ipocontr_obcol = new KX_ObColorIpoSGController(); + gameobj->GetSGNode()->AddSGController(ipocontr_obcol); + ipocontr_obcol->SetObject(gameobj->GetSGNode()); } + interpolator= new KX_ScalarInterpolator(&ipocontr_obcol->m_rgba[i], interp); + ipocontr_obcol->AddInterpolator(interpolator); } } } + + return ipocontr; +} + +void BL_ConvertIpos(struct Object* blenderobject,KX_GameObject* gameobj,KX_BlenderSceneConverter *converter) +{ + if (blenderobject->adt) { + SG_Controller *ipocontr = BL_CreateIPO(blenderobject->adt->action, gameobj, converter); + gameobj->GetSGNode()->AddSGController(ipocontr); + ipocontr->SetObject(gameobj->GetSGNode()); + } } void BL_ConvertLampIpos(struct Lamp* blenderlamp, KX_GameObject *lightobj,KX_BlenderSceneConverter *converter) diff --git a/source/gameengine/Converter/KX_IpoConvert.h b/source/gameengine/Converter/KX_IpoConvert.h index d77a72a82e2..914422ba83f 100644 --- a/source/gameengine/Converter/KX_IpoConvert.h +++ b/source/gameengine/Converter/KX_IpoConvert.h @@ -36,6 +36,10 @@ struct Object; +class SG_Controller *BL_CreateIPO(struct bAction *action, + class KX_GameObject* gameobj, + class KX_BlenderSceneConverter *converter); + void BL_ConvertIpos(struct Object* blenderobject, class KX_GameObject* gameobj, class KX_BlenderSceneConverter *converter); diff --git a/source/gameengine/Ketsji/BL_Action.cpp b/source/gameengine/Ketsji/BL_Action.cpp index dcd0c1402e1..6cce2a3486d 100644 --- a/source/gameengine/Ketsji/BL_Action.cpp +++ b/source/gameengine/Ketsji/BL_Action.cpp @@ -29,6 +29,7 @@ #include "BL_Action.h" #include "BL_ArmatureObject.h" +#include "KX_IpoConvert.h" #include "KX_GameObject.h" // These three are for getting the action from the logic manager @@ -49,7 +50,8 @@ BL_Action::BL_Action(class KX_GameObject* gameobj, float end, float blendin, short play_mode, - short blend_mode) + short blend_mode, + float playback_speed) : m_obj(gameobj), m_startframe(start), @@ -60,14 +62,26 @@ BL_Action::BL_Action(class KX_GameObject* gameobj, m_localtime(start), m_blendframe(0.f), m_blendstart(0.f), + m_speed(playback_speed), m_pose(NULL), m_blendpose(NULL), + m_sg_contr(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); + + if (m_obj->GetGameObjectType() != SCA_IObject::OBJ_ARMATURE) + { + // Create an SG_Controller + m_sg_contr = BL_CreateIPO(m_action, m_obj, KX_GetActiveScene()->GetSceneConverter()); + m_obj->GetSGNode()->AddSGController(m_sg_contr); + m_sg_contr->SetObject(m_obj->GetSGNode()); + InitIPO(); + } + } BL_Action::~BL_Action() @@ -76,11 +90,25 @@ BL_Action::~BL_Action() game_free_pose(m_pose); if (m_blendpose) game_free_pose(m_blendpose); + if (m_sg_contr) + { + m_obj->GetSGNode()->RemoveSGController(m_sg_contr); + delete m_sg_contr; + } +} + +void BL_Action::InitIPO() +{ + // Initialize the IPO + m_sg_contr->SetOption(SG_Controller::SG_CONTR_IPO_RESET, true); + m_sg_contr->SetOption(SG_Controller::SG_CONTR_IPO_IPO_AS_FORCE, false); + m_sg_contr->SetOption(SG_Controller::SG_CONTR_IPO_IPO_ADD, false); + m_sg_contr->SetOption(SG_Controller::SG_CONTR_IPO_LOCAL, false); } void BL_Action::SetLocalTime(float curtime) { - float dt = (curtime-m_starttime)*KX_KetsjiEngine::GetAnimFrameRate(); + float dt = (curtime-m_starttime)*KX_KetsjiEngine::GetAnimFrameRate()*m_speed; if (m_endframe < m_startframe) dt = -dt; @@ -90,12 +118,18 @@ void BL_Action::SetLocalTime(float curtime) void BL_Action::Update(float curtime) { + // Don't bother if we're done with the animation + if (m_done) + return; + curtime -= KX_KetsjiEngine::GetSuspendedDelta(); SetLocalTime(curtime); // Handle wrap around - if (m_localtime < m_startframe || m_localtime > m_endframe) + bool bforward = m_startframe < m_endframe; + if (bforward && (m_localtime < m_startframe || m_localtime > m_endframe) || + !bforward && (m_localtime > m_startframe || m_localtime < m_endframe)) { switch(m_playmode) { @@ -119,6 +153,9 @@ void BL_Action::Update(float curtime) break; } + + if (!m_done) + InitIPO(); } if (m_obj->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE) @@ -193,6 +230,9 @@ void BL_Action::Update(float curtime) } else { - printf("Only armature actions are currently supported\n"); + InitIPO(); + m_sg_contr->SetSimulatedTime(m_localtime); + m_obj->GetSGNode()->UpdateWorldData(m_localtime); + m_obj->UpdateTransform(); } } diff --git a/source/gameengine/Ketsji/BL_Action.h b/source/gameengine/Ketsji/BL_Action.h index 3d977f3984a..203714a3b2f 100644 --- a/source/gameengine/Ketsji/BL_Action.h +++ b/source/gameengine/Ketsji/BL_Action.h @@ -41,6 +41,7 @@ private: struct bPose* m_pose; struct bPose* m_blendpose; struct PointerRNA *m_ptrrna; + class SG_Controller *m_sg_contr; class KX_GameObject* m_obj; float m_startframe; @@ -53,11 +54,14 @@ private: float m_blendframe; float m_blendstart; + float m_speed; + short m_playmode; short m_blendmode; bool m_done; + void InitIPO(); void SetLocalTime(float curtime); public: BL_Action(class KX_GameObject* gameobj, @@ -66,7 +70,8 @@ public: float end, float blendin, short play_mode, - short blend_mode); + short blend_mode, + float playback_speed); ~BL_Action(); bool IsDone() {return m_done;} diff --git a/source/gameengine/Ketsji/BL_ActionManager.cpp b/source/gameengine/Ketsji/BL_ActionManager.cpp index 359a7389b51..a4c8a7fe93f 100644 --- a/source/gameengine/Ketsji/BL_ActionManager.cpp +++ b/source/gameengine/Ketsji/BL_ActionManager.cpp @@ -49,14 +49,15 @@ void BL_ActionManager::PlayAction(class KX_GameObject* gameobj, short layer, float blendin, short play_mode, - short blend_mode) + short blend_mode, + float playback_speed) { // 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); + m_layers[layer] = new BL_Action(gameobj, name, start, end, blendin, play_mode, blend_mode, playback_speed); } void BL_ActionManager::StopAction(short layer) diff --git a/source/gameengine/Ketsji/BL_ActionManager.h b/source/gameengine/Ketsji/BL_ActionManager.h index 409d6c24e30..a57e0e1ce87 100644 --- a/source/gameengine/Ketsji/BL_ActionManager.h +++ b/source/gameengine/Ketsji/BL_ActionManager.h @@ -49,7 +49,8 @@ public: short layer=0, float blendin=0.f, short play_mode=0, - short blend_mode=0); + short blend_mode=0, + float playback_speed=1.f); void StopAction(short layer); void Update(float); diff --git a/source/gameengine/Ketsji/KX_GameObject.cpp b/source/gameengine/Ketsji/KX_GameObject.cpp index 868dc4591e4..804a3d394ec 100644 --- a/source/gameengine/Ketsji/KX_GameObject.cpp +++ b/source/gameengine/Ketsji/KX_GameObject.cpp @@ -2992,18 +2992,18 @@ KX_PYMETHODDEF_DOC_VARARGS(KX_GameObject, sendMessage, } 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" + "playAction(name, start_frame, end_frame, layer=0, blendin=0, play_mode=ACT_MODE_PLAY, blend_mode=ACT_BLEND_NONE, speed=1.0)\n" "plays an action\n") { const char* name; - float start, end, blendin=0.f; + float start, end, blendin=0.f, speed=1.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}; + static const char *kwlist[] = {"name", "start_frame", "end_frame", "layer", "blendin", "play_mode", "blend_mode", "speed", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "sff|hfhh", const_cast(kwlist), - &name, &start, &end, &layer, &blendin, &play_mode, &blend_mode)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sff|hfhhf", const_cast(kwlist), + &name, &start, &end, &layer, &blendin, &play_mode, &blend_mode, &speed)) return NULL; if (layer < 0 || layer > MAX_ACTION_LAYERS) @@ -3024,7 +3024,7 @@ KX_PYMETHODDEF_DOC(KX_GameObject, playAction, blend_mode = BL_Action::ACT_BLEND_NONE; } - m_actionManager->PlayAction(this, name, start, end, layer, blendin, play_mode, blend_mode); + m_actionManager->PlayAction(this, name, start, end, layer, blendin, play_mode, blend_mode, speed); Py_RETURN_NONE; } diff --git a/source/gameengine/SceneGraph/SG_IObject.cpp b/source/gameengine/SceneGraph/SG_IObject.cpp index 3064e6662b9..b22d210984d 100644 --- a/source/gameengine/SceneGraph/SG_IObject.cpp +++ b/source/gameengine/SceneGraph/SG_IObject.cpp @@ -34,6 +34,8 @@ #include "SG_IObject.h" #include "SG_Controller.h" +#include + SG_Stage gSG_Stage = SG_STAGE_UNKNOWN; SG_IObject:: @@ -71,6 +73,16 @@ AddSGController( void SG_IObject:: +RemoveSGController( + SG_Controller* cont +) { + SGControllerList::iterator contit; + + m_SGcontrollers.erase(std::remove(m_SGcontrollers.begin(), m_SGcontrollers.end(), cont)); +} + + void +SG_IObject:: RemoveAllControllers( ) { m_SGcontrollers.clear(); diff --git a/source/gameengine/SceneGraph/SG_IObject.h b/source/gameengine/SceneGraph/SG_IObject.h index 26e317bdcd9..e709699c08a 100644 --- a/source/gameengine/SceneGraph/SG_IObject.h +++ b/source/gameengine/SceneGraph/SG_IObject.h @@ -180,6 +180,16 @@ public: SG_Controller* cont ); + /** + * Remove a pointer to a controller from this node. + * This does not delete the controller itself! Be careful to + * avoid memory leaks. + */ + void + RemoveSGController( + SG_Controller* cont + ); + /** * Clear the array of pointers to controllers associated with * this node. This does not delete the controllers themselves! -- cgit v1.2.3