diff --git a/source/gameengine/Converter/BL_ActionActuator.cpp b/source/gameengine/Converter/BL_ActionActuator.cpp index 1cfd46c7e11..f52f8387a6d 100644 --- a/source/gameengine/Converter/BL_ActionActuator.cpp +++ b/source/gameengine/Converter/BL_ActionActuator.cpp @@ -61,6 +61,49 @@ extern "C" { #include "RNA_define.h" } +BL_ActionActuator::BL_ActionActuator(SCA_IObject* gameobj, + const STR_String& propname, + const STR_String& framepropname, + float starttime, + float endtime, + struct bAction *action, + short playtype, + short blendin, + short priority, + short layer, + float layer_weight, + short ipo_flags, + short end_reset, + float stride) + : SCA_IActuator(gameobj, KX_ACT_ACTION), + + m_lastpos(0, 0, 0), + m_blendframe(0), + m_flag(0), + m_startframe (starttime), + m_endframe(endtime) , + m_starttime(0), + m_localtime(starttime), + m_lastUpdate(-1), + m_blendin(blendin), + m_blendstart(0), + m_stridelength(stride), + m_playtype(playtype), + m_priority(priority), + m_layer(layer), + m_layer_weight(layer_weight), + m_ipo_flags(ipo_flags), + m_pose(NULL), + m_blendpose(NULL), + m_userpose(NULL), + m_action(action), + m_propname(propname), + m_framepropname(framepropname) +{ + if (!end_reset) + m_flag |= ACT_FLAG_CONTINUE; +}; + BL_ActionActuator::~BL_ActionActuator() { if (m_pose) @@ -92,6 +135,7 @@ CValue* BL_ActionActuator::GetReplica() { return replica; } +#if 0 bool BL_ActionActuator::ClampLocalTime() { if (m_startframe < m_endframe) @@ -143,7 +187,7 @@ void BL_ActionActuator::SetLocalTime(float curtime) else m_localtime = m_endframe - delta_time; } - +#endif bool BL_ActionActuator::Update(double curtime, bool frame) { bool bNegativeEvent = false; @@ -156,10 +200,6 @@ bool BL_ActionActuator::Update(double curtime, bool frame) if (!m_action) return false; - // Don't do anything if we're not "active" - if (!frame) - return true; - // Convert playmode if (m_playtype == ACT_ACTION_LOOP_END) play_mode = BL_Action::ACT_MODE_LOOP; @@ -173,32 +213,56 @@ bool BL_ActionActuator::Update(double curtime, bool frame) play_mode = BL_Action::ACT_MODE_PLAY; start = end = prop->GetNumber(); - m_is_going = false; } - + + // Handle events - bNegativeEvent = m_negevent; - bPositiveEvent = m_posevent; - RemoveAllEvents(); - - if (!m_is_going && bPositiveEvent) - { - m_is_going = true; - if (obj->PlayAction(m_action->id.name+2, start, end, m_layer, m_priority, m_blendin, play_mode, m_layer_weight, m_ipo_flags) && m_end_reset) - obj->SetActionFrame(m_layer, m_localtime); + if (frame) + { + bNegativeEvent = m_negevent; + bPositiveEvent = m_posevent; + RemoveAllEvents(); } - else if (m_is_going && bNegativeEvent) - { + + if (bPositiveEvent) + { + if (m_flag & ACT_FLAG_ACTIVE && m_flag & ACT_FLAG_CONTINUE) + start = m_localtime = obj->GetActionFrame(m_layer); + + if (obj->PlayAction(m_action->id.name+2, start, end, m_layer, m_priority, m_blendin, play_mode, m_layer_weight, m_ipo_flags)) + { + m_flag |= ACT_FLAG_ACTIVE; + if (m_flag & ACT_FLAG_CONTINUE) + obj->SetActionFrame(m_layer, m_localtime); + + if (m_playtype == ACT_ACTION_PLAY) + m_flag |= ACT_FLAG_PLAY_END; + else + m_flag &= ~ACT_FLAG_PLAY_END; + } + else + return false; + } + else if ((m_flag & ACT_FLAG_ACTIVE) && bNegativeEvent) + { + bAction *curr_action = obj->GetCurrentAction(m_layer); + if (curr_action && curr_action != m_action) + { + // Someone changed the action on us, so we wont mess with it + // Hopefully there wont be too many problems with two actuators using + // the same action... + m_flag &= ~ACT_FLAG_ACTIVE; + return false; + } + if (m_playtype == ACT_ACTION_LOOP_STOP) { - if (!m_end_reset) - { - obj->StopAction(m_layer); - return false; - } - m_localtime = obj->GetActionFrame(m_layer); obj->StopAction(m_layer); // Stop the action after getting the frame + + // We're done + m_flag &= ~ACT_FLAG_ACTIVE; + return false; } else if (m_playtype == ACT_ACTION_LOOP_END) { @@ -207,7 +271,7 @@ bool BL_ActionActuator::Update(double curtime, bool frame) obj->StopAction(m_layer); obj->PlayAction(m_action->id.name+2, start, end, m_layer, m_priority, 0, BL_Action::ACT_MODE_PLAY, m_layer_weight, m_ipo_flags); - return true; + m_flag |= ACT_FLAG_PLAY_END; } else if (m_playtype == ACT_ACTION_FLIPPER) { @@ -217,14 +281,12 @@ bool BL_ActionActuator::Update(double curtime, bool frame) obj->StopAction(m_layer); obj->PlayAction(m_action->id.name+2, start, end, m_layer, m_priority, 0, BL_Action::ACT_MODE_PLAY, m_layer_weight, m_ipo_flags); - return true; + m_flag |= ACT_FLAG_PLAY_END; } - - m_is_going = false; } // Handle a frame property if it's defined - if (m_is_going && m_framepropname[0] != 0) + if ((m_flag & ACT_FLAG_ACTIVE) && m_framepropname[0] != 0) { CValue* oldprop = obj->GetProperty(m_framepropname); CValue* newval = new CFloatValue(obj->GetActionFrame(m_layer)); @@ -235,10 +297,11 @@ bool BL_ActionActuator::Update(double curtime, bool frame) newval->Release(); } + // Handle a finished animation - if (m_is_going && obj->IsActionDone(m_layer)) + if ((m_flag & ACT_FLAG_PLAY_END) && obj->IsActionDone(m_layer)) { - m_is_going = false; + m_flag &= ~ACT_FLAG_ACTIVE; obj->StopAction(m_layer); return false; } @@ -740,7 +803,7 @@ PyAttributeDef BL_ActionActuator::Attributes[] = { KX_PYATTRIBUTE_FLOAT_RW_CHECK("frame", 0, MAXFRAMEF, BL_ActionActuator, m_localtime, CheckFrame), KX_PYATTRIBUTE_STRING_RW("propName", 0, 31, false, BL_ActionActuator, m_propname), KX_PYATTRIBUTE_STRING_RW("framePropName", 0, 31, false, BL_ActionActuator, m_framepropname), - KX_PYATTRIBUTE_BOOL_RW("useContinue", BL_ActionActuator, m_end_reset), + KX_PYATTRIBUTE_RW_FUNCTION("useContinue", BL_ActionActuator, pyattr_get_use_continue, pyattr_set_use_continue), KX_PYATTRIBUTE_FLOAT_RW_CHECK("blendTime", 0, MAXFRAMEF, BL_ActionActuator, m_blendframe, CheckBlendTime), KX_PYATTRIBUTE_SHORT_RW_CHECK("mode",0,100,false,BL_ActionActuator,m_playtype,CheckType), { NULL } //Sentinel @@ -800,4 +863,22 @@ PyObject* BL_ActionActuator::pyattr_get_channel_names(void *self_v, const KX_PYA return ret; } +PyObject* BL_ActionActuator::pyattr_get_use_continue(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) +{ + BL_ActionActuator* self= static_cast(self_v); + return PyBool_FromLong(self->m_flag & ACT_FLAG_CONTINUE); +} + +int BL_ActionActuator::pyattr_set_use_continue(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value) +{ + BL_ActionActuator* self= static_cast(self_v); + + if (PyObject_IsTrue(value)) + self->m_flag |= ACT_FLAG_CONTINUE; + else + self->m_flag &= ~ACT_FLAG_CONTINUE; + + return PY_SET_ATTR_SUCCESS; +} + #endif // WITH_PYTHON diff --git a/source/gameengine/Converter/BL_ActionActuator.h b/source/gameengine/Converter/BL_ActionActuator.h index 3004aa2e6ca..7b4200fa19b 100644 --- a/source/gameengine/Converter/BL_ActionActuator.h +++ b/source/gameengine/Converter/BL_ActionActuator.h @@ -56,35 +56,8 @@ public: float layer_weight, short ipo_flags, short end_reset, - float stride) - : SCA_IActuator(gameobj, KX_ACT_ACTION), - - m_lastpos(0, 0, 0), - m_blendframe(0), - m_flag(0), - m_startframe (starttime), - m_endframe(endtime) , - m_starttime(0), - m_localtime(starttime), - m_lastUpdate(-1), - m_blendin(blendin), - m_blendstart(0), - m_stridelength(stride), - m_playtype(playtype), - m_priority(priority), - m_layer(layer), - m_layer_weight(layer_weight), - m_ipo_flags(ipo_flags), - m_end_reset(end_reset), - m_is_going(false), - m_pose(NULL), - m_blendpose(NULL), - m_userpose(NULL), - m_action(action), - m_propname(propname), - m_framepropname(framepropname) - { - }; + float stride); + virtual ~BL_ActionActuator(); virtual bool Update(double curtime, bool frame); virtual CValue* GetReplica(); @@ -103,6 +76,9 @@ public: static PyObject* pyattr_get_action(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef); static int pyattr_set_action(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value); static PyObject* pyattr_get_channel_names(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef); + static PyObject* pyattr_get_use_continue(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef); + static int pyattr_set_use_continue(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value); + /* attribute check */ static int CheckFrame(void *self, const PyAttributeDef*) { @@ -147,9 +123,9 @@ public: protected: - void SetStartTime(float curtime); - void SetLocalTime(float curtime); - bool ClampLocalTime(); + //void SetStartTime(float curtime); + //void SetLocalTime(float curtime); + //bool ClampLocalTime(); MT_Point3 m_lastpos; float m_blendframe; @@ -172,8 +148,6 @@ protected: short m_priority; short m_layer; short m_ipo_flags; - bool m_end_reset; - bool m_is_going; struct bPose* m_pose; struct bPose* m_blendpose; struct bPose* m_userpose; @@ -183,10 +157,13 @@ protected: }; enum { - ACT_FLAG_REVERSE = 0x00000001, - ACT_FLAG_LOCKINPUT = 0x00000002, - ACT_FLAG_KEYUP = 0x00000004, - ACT_FLAG_ACTIVE = 0x00000008 + ACT_FLAG_REVERSE = 1<<0, + ACT_FLAG_LOCKINPUT = 1<<1, + ACT_FLAG_KEYUP = 1<<2, + ACT_FLAG_ACTIVE = 1<<3, + ACT_FLAG_CONTINUE = 1<<4, + ACT_FLAG_PLAY_END = 1<<5 + }; #endif diff --git a/source/gameengine/Ketsji/BL_Action.cpp b/source/gameengine/Ketsji/BL_Action.cpp index 04d05d87c06..f1b53fc4151 100644 --- a/source/gameengine/Ketsji/BL_Action.cpp +++ b/source/gameengine/Ketsji/BL_Action.cpp @@ -62,8 +62,7 @@ BL_Action::BL_Action(class KX_GameObject* gameobj) m_blendinpose(NULL), m_sg_contr(NULL), m_ptrrna(NULL), - m_done(true), - m_bcalc_local_time(true) + m_done(true) { if (m_obj->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE) { @@ -197,6 +196,11 @@ void BL_Action::InitIPO() m_sg_contr->SetOption(SG_Controller::SG_CONTR_IPO_LOCAL, m_ipo_flags & ACT_IPOFLAG_LOCAL); } +bAction *BL_Action::GetAction() +{ + return (IsDone()) ? NULL : m_action; +} + float BL_Action::GetFrame() { return m_localtime; @@ -204,14 +208,24 @@ float BL_Action::GetFrame() void BL_Action::SetFrame(float frame) { + float dt; + // Clamp the frame to the start and end frame if (frame < min(m_startframe, m_endframe)) frame = min(m_startframe, m_endframe); else if (frame > max(m_startframe, m_endframe)) frame = max(m_startframe, m_endframe); + + // We don't set m_localtime directly since it's recalculated + // in the next update. So, we modify the value (m_starttime) + // used to calculate m_localtime the next time SetLocalTime() is called. - m_localtime = frame; - m_bcalc_local_time = false; + dt = frame-m_startframe; + + if (m_endframe < m_startframe) + dt = -dt; + + m_starttime -= dt / (KX_KetsjiEngine::GetAnimFrameRate()*m_speed); } void BL_Action::SetLocalTime(float curtime) @@ -263,16 +277,8 @@ void BL_Action::Update(float curtime) if (m_done) return; - // We only want to calculate the current time if we weren't given a frame (e.g., from SetFrame()) - if (m_bcalc_local_time) - { - curtime -= KX_KetsjiEngine::GetSuspendedDelta(); - SetLocalTime(curtime); - } - else - { - m_bcalc_local_time = true; - } + curtime -= KX_KetsjiEngine::GetSuspendedDelta(); + SetLocalTime(curtime); // Handle wrap around if (m_localtime < min(m_startframe, m_endframe) || m_localtime > max(m_startframe, m_endframe)) diff --git a/source/gameengine/Ketsji/BL_Action.h b/source/gameengine/Ketsji/BL_Action.h index eea00b15036..f7c5a811721 100644 --- a/source/gameengine/Ketsji/BL_Action.h +++ b/source/gameengine/Ketsji/BL_Action.h @@ -66,7 +66,6 @@ private: short m_ipo_flags; bool m_done; - bool m_bcalc_local_time; void InitIPO(); void SetLocalTime(float curtime); @@ -91,6 +90,7 @@ public: // Accessors float GetFrame(); + struct bAction *GetAction(); // Mutators void SetFrame(float frame); diff --git a/source/gameengine/Ketsji/BL_ActionManager.cpp b/source/gameengine/Ketsji/BL_ActionManager.cpp index 8d862c38961..c3b4dc5d4db 100644 --- a/source/gameengine/Ketsji/BL_ActionManager.cpp +++ b/source/gameengine/Ketsji/BL_ActionManager.cpp @@ -56,6 +56,12 @@ void BL_ActionManager::SetActionFrame(short layer, float frame) m_layers[layer]->SetFrame(frame); } +struct bAction *BL_ActionManager::GetCurrentAction(short layer) +{ + if (m_layers[layer]) + return m_layers[layer]->GetAction(); +} + bool BL_ActionManager::PlayAction(const char* name, float start, float end, diff --git a/source/gameengine/Ketsji/BL_ActionManager.h b/source/gameengine/Ketsji/BL_ActionManager.h index 3836c6b59d2..c527c7bbd3a 100644 --- a/source/gameengine/Ketsji/BL_ActionManager.h +++ b/source/gameengine/Ketsji/BL_ActionManager.h @@ -56,6 +56,8 @@ public: float GetActionFrame(short layer); void SetActionFrame(short layer, float frame); + struct bAction *GetCurrentAction(short layer); + void StopAction(short layer); bool IsActionDone(short layer); void Update(float); diff --git a/source/gameengine/Ketsji/KX_GameObject.cpp b/source/gameengine/Ketsji/KX_GameObject.cpp index 334868d283a..3404cd227f0 100644 --- a/source/gameengine/Ketsji/KX_GameObject.cpp +++ b/source/gameengine/Ketsji/KX_GameObject.cpp @@ -399,6 +399,11 @@ void KX_GameObject::SetActionFrame(short layer, float frame) GetActionManager()->SetActionFrame(layer, frame); } +bAction *KX_GameObject::GetCurrentAction(short layer) +{ + return GetActionManager()->GetCurrentAction(layer); +} + void KX_GameObject::ProcessReplica() { SCA_IObject::ProcessReplica(); diff --git a/source/gameengine/Ketsji/KX_GameObject.h b/source/gameengine/Ketsji/KX_GameObject.h index 96bd42ba702..6e22dc5fbc3 100644 --- a/source/gameengine/Ketsji/KX_GameObject.h +++ b/source/gameengine/Ketsji/KX_GameObject.h @@ -65,6 +65,7 @@ class PHY_IGraphicController; class PHY_IPhysicsEnvironment; class BL_ActionManager; struct Object; +struct bAction; #ifdef WITH_PYTHON /* utility conversion function */ @@ -232,6 +233,11 @@ public: */ void SetActionFrame(short layer, float frame); + /** + * Gets the currently running action on the given layer + */ + bAction *GetCurrentAction(short layer); + /** * Remove an action from the object's action manager */