diff options
Diffstat (limited to 'source/gameengine/Ketsji')
28 files changed, 390 insertions, 235 deletions
diff --git a/source/gameengine/Ketsji/BL_Action.cpp b/source/gameengine/Ketsji/BL_Action.cpp index 9bae119107e..5889f5ed412 100644 --- a/source/gameengine/Ketsji/BL_Action.cpp +++ b/source/gameengine/Ketsji/BL_Action.cpp @@ -56,6 +56,15 @@ extern "C" { #include "BKE_library.h" #include "BKE_global.h" +#include "BLI_threads.h" // for lock + +/* Lock to solve animation thread issues. + * A spin lock is better than a mutex in case of short wait + * because spin lock stop the thread by a loop contrary to mutex + * which switch all memory, process. + */ +static SpinLock BL_ActionLock; + BL_Action::BL_Action(class KX_GameObject* gameobj) : m_action(NULL), @@ -65,8 +74,7 @@ BL_Action::BL_Action(class KX_GameObject* gameobj) m_obj(gameobj), m_startframe(0.f), m_endframe(0.f), - m_endtime(0.f), - m_localtime(0.f), + m_localframe(0.f), m_blendin(0.f), m_blendframe(0.f), m_blendstart(0.f), @@ -252,12 +260,11 @@ bool BL_Action::Play(const char* name, // Now that we have an action, we have something we can play m_starttime = -1.f; // We get the start time on our first update - m_startframe = m_localtime = start; + m_startframe = m_localframe = start; m_endframe = end; m_blendin = blendin; m_playmode = play_mode; m_blendmode = blend_mode; - m_endtime = 0.f; m_blendframe = 0.f; m_blendstart = 0.f; m_speed = playback_speed; @@ -268,11 +275,6 @@ bool BL_Action::Play(const char* name, return true; } -void BL_Action::Stop() -{ - m_done = true; -} - bool BL_Action::IsDone() { return m_done; @@ -298,7 +300,19 @@ bAction *BL_Action::GetAction() float BL_Action::GetFrame() { - return m_localtime; + return m_localframe; +} + +const char *BL_Action::GetName() +{ + if (m_action != NULL) { + return m_action->id.name + 2; + } + else { + return ""; + } + + } void BL_Action::SetFrame(float frame) @@ -309,7 +323,7 @@ void BL_Action::SetFrame(float frame) else if (frame > max(m_startframe, m_endframe)) frame = max(m_startframe, m_endframe); - m_localtime = frame; + m_localframe = frame; m_calc_localtime = false; } @@ -331,12 +345,12 @@ void BL_Action::SetLocalTime(float curtime) if (m_endframe < m_startframe) dt = -dt; - m_localtime = m_startframe + dt; + m_localframe = m_startframe + dt; } void BL_Action::ResetStartTime(float curtime) { - float dt = (m_localtime > m_startframe) ? m_localtime - m_startframe : m_startframe - m_localtime; + float dt = (m_localframe > m_startframe) ? m_localframe - m_startframe : m_startframe - m_localframe; m_starttime = curtime - dt / (KX_KetsjiEngine::GetAnimFrameRate()*m_speed); SetLocalTime(curtime); @@ -384,7 +398,7 @@ void BL_Action::Update(float curtime) curtime -= KX_KetsjiEngine::GetSuspendedDelta(); - // Grab the start time here so we don't end up with a negative m_localtime when + // Grab the start time here so we don't end up with a negative m_localframe when // suspending and resuming scenes. if (m_starttime < 0) m_starttime = curtime; @@ -398,16 +412,16 @@ void BL_Action::Update(float curtime) } // Handle wrap around - if (m_localtime < min(m_startframe, m_endframe) || m_localtime > max(m_startframe, m_endframe)) { + if (m_localframe < min(m_startframe, m_endframe) || m_localframe > max(m_startframe, m_endframe)) { switch (m_playmode) { case ACT_MODE_PLAY: // Clamp - m_localtime = m_endframe; + m_localframe = m_endframe; m_done = true; break; case ACT_MODE_LOOP: // Put the time back to the beginning - m_localtime = m_startframe; + m_localframe = m_startframe; m_starttime = curtime; break; case ACT_MODE_PING_PONG: @@ -430,7 +444,7 @@ void BL_Action::Update(float curtime) obj->GetPose(&m_blendpose); // Extract the pose from the action - obj->SetPoseByAction(m_tmpaction, m_localtime); + obj->SetPoseByAction(m_tmpaction, m_localframe); // Handle blending between armature actions if (m_blendin && m_blendframe<m_blendin) @@ -464,7 +478,7 @@ void BL_Action::Update(float curtime) PointerRNA ptrrna; RNA_id_pointer_create(&key->id, &ptrrna); - animsys_evaluate_action(&ptrrna, m_tmpaction, NULL, m_localtime); + animsys_evaluate_action(&ptrrna, m_tmpaction, NULL, m_localframe); // Handle blending between shape actions if (m_blendin && m_blendframe < m_blendin) @@ -494,15 +508,23 @@ void BL_Action::Update(float curtime) } } - // This isn't thread-safe, so we move it into it's own function for now - //m_obj->UpdateIPO(m_localtime, m_ipo_flags & ACT_IPOFLAG_CHILD); + BLI_spin_lock(&BL_ActionLock); + /* This function is not thread safe because of recursive scene graph transform + * updates on children. e.g: If an object and one of its children is animated, + * the both can write transform at the same time. A thread lock avoid problems. */ + m_obj->UpdateIPO(m_localframe, m_ipo_flags & ACT_IPOFLAG_CHILD); + BLI_spin_unlock(&BL_ActionLock); if (m_done) ClearControllerList(); } -void BL_Action::UpdateIPOs() +void BL_Action::InitLock() +{ + BLI_spin_init(&BL_ActionLock); +} + +void BL_Action::EndLock() { - if (!m_done) - m_obj->UpdateIPO(m_localtime, m_ipo_flags & ACT_IPOFLAG_CHILD); + BLI_spin_end(&BL_ActionLock); } diff --git a/source/gameengine/Ketsji/BL_Action.h b/source/gameengine/Ketsji/BL_Action.h index dd1cd1f69ff..f41b2ef9460 100644 --- a/source/gameengine/Ketsji/BL_Action.h +++ b/source/gameengine/Ketsji/BL_Action.h @@ -48,9 +48,9 @@ private: float m_startframe; float m_endframe; + /// The current action frame. + float m_localframe; float m_starttime; - float m_endtime; - float m_localtime; float m_blendin; float m_blendframe; @@ -94,10 +94,6 @@ public: float playback_speed, short blend_mode); /** - * Stop playing the action - */ - void Stop(); - /** * Whether or not the action is still playing */ bool IsDone(); @@ -105,13 +101,11 @@ public: * Update the action's frame, etc. */ void Update(float curtime); - /** - * Update object IPOs (note: not thread-safe!) - */ - void UpdateIPOs(); // Accessors float GetFrame(); + const char *GetName(); + struct bAction *GetAction(); // Mutators @@ -142,6 +136,11 @@ public: ACT_IPOFLAG_CHILD = 8, }; + /// Initialize a lock for animation thread issues. + static void InitLock(); + /// Finalize a lock for animation thread issues. + static void EndLock(); + #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("GE:BL_Action") #endif diff --git a/source/gameengine/Ketsji/BL_ActionManager.cpp b/source/gameengine/Ketsji/BL_ActionManager.cpp index 975f9ea6da8..491be035d66 100644 --- a/source/gameengine/Ketsji/BL_ActionManager.cpp +++ b/source/gameengine/Ketsji/BL_ActionManager.cpp @@ -53,19 +53,17 @@ BL_Action *BL_ActionManager::GetAction(short layer) return (it != m_layers.end()) ? it->second : 0; } -BL_Action* BL_ActionManager::AddAction(short layer) +float BL_ActionManager::GetActionFrame(short layer) { - BL_Action *action = new BL_Action(m_obj); - m_layers[layer] = action; + BL_Action *action = GetAction(layer); - return action; + return action ? action->GetFrame() : 0.f; } -float BL_ActionManager::GetActionFrame(short layer) +const char *BL_ActionManager::GetActionName(short layer) { BL_Action *action = GetAction(layer); - - return action ? action->GetFrame() : 0.f; + return action ? action->GetName() : ""; } void BL_ActionManager::SetActionFrame(short layer, float frame) @@ -110,8 +108,10 @@ bool BL_ActionManager::PlayAction(const char* name, { // Only this method will create layer if non-existent BL_Action *action = GetAction(layer); - if (!action) - action = AddAction(layer); + if (!action) { + action = new BL_Action(m_obj); + m_layers[layer] = action; + } // Disable layer blending on the first layer if (layer == 0) layer_weight = -1.f; @@ -123,7 +123,10 @@ void BL_ActionManager::StopAction(short layer) { BL_Action *action = GetAction(layer); - if (action) action->Stop(); + if (action) { + m_layers.erase(layer); + delete action; + } } void BL_ActionManager::RemoveTaggedActions() @@ -152,25 +155,10 @@ void BL_ActionManager::Update(float curtime) m_prevUpdate = curtime; BL_ActionMap::iterator it; - for (it = m_layers.begin(); it != m_layers.end(); ) + for (it = m_layers.begin(); it != m_layers.end(); ++it) { - if (it->second->IsDone()) { - delete it->second; - m_layers.erase(it++); - } - else { + if (!it->second->IsDone()) { it->second->Update(curtime); - ++it; } } } - -void BL_ActionManager::UpdateIPOs() -{ - BL_ActionMap::iterator it; - for (it = m_layers.begin(); it != m_layers.end(); ++it) - { - if (!it->second->IsDone()) - it->second->UpdateIPOs(); - } -} diff --git a/source/gameengine/Ketsji/BL_ActionManager.h b/source/gameengine/Ketsji/BL_ActionManager.h index 00e536655c7..69c6d611df0 100644 --- a/source/gameengine/Ketsji/BL_ActionManager.h +++ b/source/gameengine/Ketsji/BL_ActionManager.h @@ -59,11 +59,6 @@ private: */ BL_Action* GetAction(short layer); - /** - * Add new action with given layer - */ - BL_Action* AddAction(short layer); - public: BL_ActionManager(class KX_GameObject* obj); ~BL_ActionManager(); @@ -85,6 +80,11 @@ public: float GetActionFrame(short layer); /** + * Gets the name of the current action + */ + const char *GetActionName(short layer); + + /** * Sets the current frame of an action */ void SetActionFrame(short layer, float frame); @@ -124,11 +124,6 @@ public: */ void Update(float); - /** - * Update object IPOs (note: not thread-safe!) - */ - void UpdateIPOs(); - #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("GE:BL_ActionManager") #endif diff --git a/source/gameengine/Ketsji/KX_CharacterWrapper.cpp b/source/gameengine/Ketsji/KX_CharacterWrapper.cpp index fdf4fa01b03..44f06a9f1eb 100644 --- a/source/gameengine/Ketsji/KX_CharacterWrapper.cpp +++ b/source/gameengine/Ketsji/KX_CharacterWrapper.cpp @@ -25,6 +25,7 @@ #include "KX_CharacterWrapper.h" #include "PHY_ICharacter.h" #include "KX_PyMath.h" +#include "BLI_utildefines.h" KX_CharacterWrapper::KX_CharacterWrapper(PHY_ICharacter* character) : PyObjectPlus(), @@ -116,7 +117,9 @@ int KX_CharacterWrapper::pyattr_set_max_jumps(void *self_v, const KX_PYATTRIBUTE return PY_SET_ATTR_FAIL; } - self->m_character->SetMaxJumps((int)param); + CLAMP(param, 0, 255); + + self->m_character->SetMaxJumps(param); return PY_SET_ATTR_SUCCESS; } diff --git a/source/gameengine/Ketsji/KX_ConstraintActuator.cpp b/source/gameengine/Ketsji/KX_ConstraintActuator.cpp index e07660cef72..fecd60eb212 100644 --- a/source/gameengine/Ketsji/KX_ConstraintActuator.cpp +++ b/source/gameengine/Ketsji/KX_ConstraintActuator.cpp @@ -117,7 +117,7 @@ KX_ConstraintActuator::~KX_ConstraintActuator() // there's nothing to be done here, really.... } /* end of destructor */ -bool KX_ConstraintActuator::RayHit(KX_ClientObjectInfo *client, KX_RayCast *result, void * const data) +bool KX_ConstraintActuator::RayHit(KX_ClientObjectInfo *client, KX_RayCast *result, void *UNUSED(data)) { m_hitObject = client->m_gameobject; @@ -153,7 +153,7 @@ bool KX_ConstraintActuator::RayHit(KX_ClientObjectInfo *client, KX_RayCast *resu /* This function is used to pre-filter the object before casting the ray on them. * This is useful for "X-Ray" option when we want to see "through" unwanted object. */ -bool KX_ConstraintActuator::NeedRayCast(KX_ClientObjectInfo *client) +bool KX_ConstraintActuator::NeedRayCast(KX_ClientObjectInfo *client, void *UNUSED(data)) { if (client->m_type > KX_ClientObjectInfo::ACTOR) { @@ -347,7 +347,7 @@ bool KX_ConstraintActuator::Update(double curtime, bool frame) spc = parent->GetPhysicsController(); } } - KX_RayCast::Callback<KX_ConstraintActuator> callback(this,dynamic_cast<PHY_IPhysicsController*>(spc)); + KX_RayCast::Callback<KX_ConstraintActuator, void> callback(this,dynamic_cast<PHY_IPhysicsController*>(spc)); result = KX_RayCast::RayTest(pe, position, topoint, callback); if (result) { MT_Vector3 newnormal = callback.m_hitNormal; @@ -459,7 +459,7 @@ bool KX_ConstraintActuator::Update(double curtime, bool frame) m_hitObject = NULL; // distance of Fh area is stored in m_minimum MT_Point3 topoint = position + (m_minimumBound+spc->GetRadius()) * direction; - KX_RayCast::Callback<KX_ConstraintActuator> callback(this, spc); + KX_RayCast::Callback<KX_ConstraintActuator, void> callback(this, spc); result = KX_RayCast::RayTest(pe, position, topoint, callback); // we expect a hit object if (!m_hitObject) diff --git a/source/gameengine/Ketsji/KX_ConstraintActuator.h b/source/gameengine/Ketsji/KX_ConstraintActuator.h index edb2e5e0180..af617655d5e 100644 --- a/source/gameengine/Ketsji/KX_ConstraintActuator.h +++ b/source/gameengine/Ketsji/KX_ConstraintActuator.h @@ -37,6 +37,8 @@ #include "MT_Vector3.h" #include "KX_ClientObjectInfo.h" +#include "BLI_utildefines.h" + class KX_RayCast; class KX_GameObject; @@ -113,9 +115,11 @@ protected: KX_ACT_CONSTRAINT_LOCAL = 1024, KX_ACT_CONSTRAINT_DOROTFH = 2048 }; - bool IsValidMode(KX_CONSTRAINTTYPE m); - bool RayHit(KX_ClientObjectInfo* client, KX_RayCast* result, void * const data); - bool NeedRayCast(KX_ClientObjectInfo*); + bool IsValidMode(KX_CONSTRAINTTYPE m); + /// \see KX_RayCast + bool RayHit(KX_ClientObjectInfo *client, KX_RayCast *result, void *UNUSED(data)); + /// \see KX_RayCast + bool NeedRayCast(KX_ClientObjectInfo *client, void *UNUSED(data)); KX_ConstraintActuator(SCA_IObject* gameobj, int posDamptime, diff --git a/source/gameengine/Ketsji/KX_GameActuator.cpp b/source/gameengine/Ketsji/KX_GameActuator.cpp index a23af680104..f1bd253f8b3 100644 --- a/source/gameengine/Ketsji/KX_GameActuator.cpp +++ b/source/gameengine/Ketsji/KX_GameActuator.cpp @@ -41,6 +41,7 @@ #include "KX_Scene.h" #include "KX_KetsjiEngine.h" #include "KX_PythonInit.h" /* for config load/saving */ +#include "RAS_ICanvas.h" #include <stdio.h> #include <stdlib.h> @@ -204,6 +205,17 @@ bool KX_GameActuator::Update() break; #endif // WITH_PYTHON } + case KX_GAME_SCREENSHOT: + { + RAS_ICanvas *canvas = m_ketsjiengine->GetCanvas(); + if (canvas) { + canvas->MakeScreenShot(m_filename); + } + else { + printf("KX_GAME_SCREENSHOT error: Rasterizer not available"); + } + break; + } default: ; /* do nothing? this is an internal error !!! */ } diff --git a/source/gameengine/Ketsji/KX_GameActuator.h b/source/gameengine/Ketsji/KX_GameActuator.h index 0c1c4f0c277..57472836bb2 100644 --- a/source/gameengine/Ketsji/KX_GameActuator.h +++ b/source/gameengine/Ketsji/KX_GameActuator.h @@ -59,6 +59,7 @@ protected: KX_GAME_QUIT, KX_GAME_SAVECFG, KX_GAME_LOADCFG, + KX_GAME_SCREENSHOT, KX_GAME_MAX }; diff --git a/source/gameengine/Ketsji/KX_GameObject.cpp b/source/gameengine/Ketsji/KX_GameObject.cpp index 536670dde1a..1dbcf14af89 100644 --- a/source/gameengine/Ketsji/KX_GameObject.cpp +++ b/source/gameengine/Ketsji/KX_GameObject.cpp @@ -47,7 +47,6 @@ #include "KX_MeshProxy.h" #include "KX_PolyProxy.h" #include <stdio.h> // printf -#include <climits> // USHRT_MAX #include "SG_Controller.h" #include "PHY_IGraphicController.h" #include "SG_Node.h" @@ -108,8 +107,6 @@ KX_GameObject::KX_GameObject( m_bOccluder(false), m_pPhysicsController(NULL), m_pGraphicController(NULL), - m_xray(false), - m_pHitObject(NULL), m_pObstacleSimulation(NULL), m_pInstanceObjects(NULL), m_pDupliGroupObject(NULL), @@ -484,14 +481,14 @@ void KX_GameObject::UpdateActionManager(float curtime) GetActionManager()->Update(curtime); } -void KX_GameObject::UpdateActionIPOs() +float KX_GameObject::GetActionFrame(short layer) { - GetActionManager()->UpdateIPOs(); + return GetActionManager()->GetActionFrame(layer); } -float KX_GameObject::GetActionFrame(short layer) +const char *KX_GameObject::GetActionName(short layer) { - return GetActionManager()->GetActionFrame(layer); + return GetActionManager()->GetActionName(layer); } void KX_GameObject::SetActionFrame(short layer, float frame) @@ -947,6 +944,9 @@ void KX_GameObject::InitIPO(bool ipo_as_force, void KX_GameObject::UpdateIPO(float curframetime, bool recurse) { + /* This function shouldn't call BL_Action::Update, not even indirectly, + * as it will cause deadlock due to the lock in BL_Action::Update. */ + // just the 'normal' update procedure. GetSGNode()->SetSimulatedTime(curframetime,recurse); GetSGNode()->UpdateWorldData(curframetime); @@ -1611,7 +1611,8 @@ void KX_GameObject::Resume(void) { if (m_suspended) { SCA_IObject::Resume(); - if (GetPhysicsController()) + // Child objects must be static, so we block changing to dynamic + if (GetPhysicsController() && !GetParent()) GetPhysicsController()->RestoreDynamics(); m_suspended = false; @@ -1669,10 +1670,11 @@ CListValue* KX_GameObject::GetChildrenRecursive() KX_Scene* KX_GameObject::GetScene() { SG_Node* node = this->GetSGNode(); - if (node == NULL) - // this happens for object in non active layers, rely on static scene then - return KX_GetActiveScene(); - return static_cast<KX_Scene*>(node->GetSGClientInfo()); + if (node == NULL) { + // this happens for object in non active layers, rely on static scene then + return KX_GetActiveScene(); + } + return static_cast<KX_Scene*>(node->GetSGClientInfo()); } /* --------------------------------------------------------------------- @@ -1957,6 +1959,7 @@ PyMethodDef KX_GameObject::Methods[] = { KX_PYMETHODTABLE_KEYWORDS(KX_GameObject, playAction), KX_PYMETHODTABLE(KX_GameObject, stopAction), KX_PYMETHODTABLE(KX_GameObject, getActionFrame), + KX_PYMETHODTABLE(KX_GameObject, getActionName), KX_PYMETHODTABLE(KX_GameObject, setActionFrame), KX_PYMETHODTABLE(KX_GameObject, isPlayingAction), @@ -2353,8 +2356,8 @@ int KX_GameObject::pyattr_set_collisionGroup(void *self_v, const KX_PYATTRIBUTE_ return PY_SET_ATTR_FAIL; } - if (val < 0 || val > USHRT_MAX) { - PyErr_Format(PyExc_AttributeError, "gameOb.collisionGroup = int: KX_GameObject, expected a int bit field between 0 and %i", USHRT_MAX); + if (val == 0 || val & ~((1 << OB_MAX_COL_MASKS) - 1)) { + PyErr_Format(PyExc_AttributeError, "gameOb.collisionGroup = int: KX_GameObject, expected a int bit field, 0 < group < %i", (1 << OB_MAX_COL_MASKS)); return PY_SET_ATTR_FAIL; } @@ -2378,8 +2381,8 @@ int KX_GameObject::pyattr_set_collisionMask(void *self_v, const KX_PYATTRIBUTE_D return PY_SET_ATTR_FAIL; } - if (val < 0 || val > USHRT_MAX) { - PyErr_Format(PyExc_AttributeError, "gameOb.collisionMask = int: KX_GameObject, expected a int bit field between 0 and %i", USHRT_MAX); + if (val == 0 || val & ~((1 << OB_MAX_COL_MASKS) - 1)) { + PyErr_Format(PyExc_AttributeError, "gameOb.collisionMask = int: KX_GameObject, expected a int bit field, 0 < mask < %i", (1 << OB_MAX_COL_MASKS)); return PY_SET_ATTR_FAIL; } @@ -3431,7 +3434,8 @@ PyObject *KX_GameObject::PySuspendDynamics(PyObject *args) PyObject *KX_GameObject::PyRestoreDynamics() { - if (GetPhysicsController()) + // Child objects must be static, so we block changing to dynamic + if (GetPhysicsController() && !GetParent()) GetPhysicsController()->RestoreDynamics(); Py_RETURN_NONE; } @@ -3563,15 +3567,32 @@ KX_PYMETHODDEF_DOC_O(KX_GameObject, getVectTo, return returnValue; } -bool KX_GameObject::RayHit(KX_ClientObjectInfo *client, KX_RayCast *result, void * const data) +struct KX_GameObject::RayCastData +{ + RayCastData(STR_String prop, bool xray, short mask) + :m_prop(prop), + m_xray(xray), + m_mask(mask), + m_hitObject(NULL) + { + } + + STR_String m_prop; + bool m_xray; + unsigned short m_mask; + KX_GameObject *m_hitObject; +}; + +bool KX_GameObject::RayHit(KX_ClientObjectInfo *client, KX_RayCast *result, RayCastData *rayData) { KX_GameObject* hitKXObj = client->m_gameobject; - + // if X-ray option is selected, the unwnted objects were not tested, so get here only with true hit // if not, all objects were tested and the front one may not be the correct one. - if (m_xray || m_testPropName.Length() == 0 || hitKXObj->GetProperty(m_testPropName) != NULL) + if ((rayData->m_xray || rayData->m_prop.Length() == 0 || hitKXObj->GetProperty(rayData->m_prop) != NULL) && + hitKXObj->GetUserCollisionGroup() & rayData->m_mask) { - m_pHitObject = hitKXObj; + rayData->m_hitObject = hitKXObj; return true; } // return true to stop RayCast::RayTest from looping, the above test was decisive @@ -3582,10 +3603,10 @@ bool KX_GameObject::RayHit(KX_ClientObjectInfo *client, KX_RayCast *result, void /* this function is used to pre-filter the object before casting the ray on them. * This is useful for "X-Ray" option when we want to see "through" unwanted object. */ -bool KX_GameObject::NeedRayCast(KX_ClientObjectInfo *client) +bool KX_GameObject::NeedRayCast(KX_ClientObjectInfo *client, RayCastData *rayData) { KX_GameObject* hitKXObj = client->m_gameobject; - + if (client->m_type > KX_ClientObjectInfo::ACTOR) { // Unknown type of object, skip it. @@ -3596,7 +3617,8 @@ bool KX_GameObject::NeedRayCast(KX_ClientObjectInfo *client) // if X-Ray option is selected, skip object that don't match the criteria as we see through them // if not, test all objects because we don't know yet which one will be on front - if (!m_xray || m_testPropName.Length() == 0 || hitKXObj->GetProperty(m_testPropName) != NULL) + if ((!rayData->m_xray || rayData->m_prop.Length() == 0 || hitKXObj->GetProperty(rayData->m_prop) != NULL) && + hitKXObj->GetUserCollisionGroup() & rayData->m_mask) { return true; } @@ -3643,17 +3665,11 @@ KX_PYMETHODDEF_DOC(KX_GameObject, rayCastTo, KX_GameObject *parent = GetParent(); if (!spc && parent) spc = parent->GetPhysicsController(); - - m_pHitObject = NULL; - if (propName) - m_testPropName = propName; - else - m_testPropName.SetLength(0); - KX_RayCast::Callback<KX_GameObject> callback(this,spc); - KX_RayCast::RayTest(pe, fromPoint, toPoint, callback); - if (m_pHitObject) - return m_pHitObject->GetProxy(); + RayCastData rayData(propName, false, (1 << OB_MAX_COL_MASKS) - 1); + KX_RayCast::Callback<KX_GameObject, RayCastData> callback(this, spc, &rayData); + if (KX_RayCast::RayTest(pe, fromPoint, toPoint, callback)) + return rayData.m_hitObject->GetProxy(); Py_RETURN_NONE; } @@ -3704,7 +3720,7 @@ static PyObject *none_tuple_5() } KX_PYMETHODDEF_DOC(KX_GameObject, rayCast, - "rayCast(to,from,dist,prop,face,xray,poly): cast a ray and return 3-tuple (object,hit,normal) or 4-tuple (object,hit,normal,polygon) or 4-tuple (object,hit,normal,polygon,hituv) of contact point with object within dist that matches prop.\n" + "rayCast(to,from,dist,prop,face,xray,poly,mask): cast a ray and return 3-tuple (object,hit,normal) or 4-tuple (object,hit,normal,polygon) or 4-tuple (object,hit,normal,polygon,hituv) of contact point with object within dist that matches prop.\n" " If no hit, return (None,None,None) or (None,None,None,None) or (None,None,None,None,None).\n" " to = 3-tuple or object reference for destination of ray (if object, use center of object)\n" " from = 3-tuple or object reference for origin of ray (if object, use center of object)\n" @@ -3718,6 +3734,7 @@ KX_PYMETHODDEF_DOC(KX_GameObject, rayCast, " 2=>return value is a 5-tuple, the 4th element is the KX_PolyProxy object\n" " and the 5th element is the vector of UV coordinates at the hit point of the None if there is no UV mapping\n" " If 0 or omitted, return value is a 3-tuple\n" +" mask = collision mask: the collision mask that ray can hit, 0 < mask < 65536\n" "Note: The object on which you call this method matters: the ray will ignore it.\n" " prop and xray option interact as follow:\n" " prop off, xray off: return closest hit or no hit if there is no object on the full extend of the ray\n" @@ -3733,8 +3750,9 @@ KX_PYMETHODDEF_DOC(KX_GameObject, rayCast, char *propName = NULL; KX_GameObject *other; int face=0, xray=0, poly=0; + int mask = (1 << OB_MAX_COL_MASKS) - 1; - if (!PyArg_ParseTuple(args,"O|Ofsiii:rayCast", &pyto, &pyfrom, &dist, &propName, &face, &xray, &poly)) { + if (!PyArg_ParseTuple(args,"O|Ofsiiii:rayCast", &pyto, &pyfrom, &dist, &propName, &face, &xray, &poly, &mask)) { return NULL; // Python sets a simple error } @@ -3764,11 +3782,16 @@ KX_PYMETHODDEF_DOC(KX_GameObject, rayCast, fromPoint = other->NodeGetWorldPosition(); } else { - PyErr_SetString(PyExc_TypeError, "gameOb.rayCast(to,from,dist,prop,face,xray,poly): KX_GameObject, the second optional argument to rayCast must be a vector or a KX_GameObject"); + PyErr_SetString(PyExc_TypeError, "gameOb.rayCast(to,from,dist,prop,face,xray,poly,mask): KX_GameObject, the second optional argument to rayCast must be a vector or a KX_GameObject"); return NULL; } } - + + if (mask == 0 || mask & ~((1 << OB_MAX_COL_MASKS) - 1)) { + PyErr_Format(PyExc_TypeError, "gameOb.rayCast(to,from,dist,prop,face,xray,poly,mask): KX_GameObject, mask argument to rayCast must be a int bitfield, 0 < mask < %i", (1 << OB_MAX_COL_MASKS)); + return NULL; + } + if (dist != 0.0f) { MT_Vector3 toDir = toPoint-fromPoint; if (MT_fuzzyZero(toDir.length2())) { @@ -3787,22 +3810,16 @@ KX_PYMETHODDEF_DOC(KX_GameObject, rayCast, KX_GameObject *parent = GetParent(); if (!spc && parent) spc = parent->GetPhysicsController(); - - m_pHitObject = NULL; - if (propName) - m_testPropName = propName; - else - m_testPropName.SetLength(0); - m_xray = xray; + // to get the hit results - KX_RayCast::Callback<KX_GameObject> callback(this,spc,NULL,face,(poly==2)); - KX_RayCast::RayTest(pe, fromPoint, toPoint, callback); + RayCastData rayData(propName, xray, mask); + KX_RayCast::Callback<KX_GameObject, RayCastData> callback(this, spc, &rayData, face, (poly == 2)); - if (m_pHitObject) + if (KX_RayCast::RayTest(pe, fromPoint, toPoint, callback)) { PyObject *returnValue = (poly == 2) ? PyTuple_New(5) : (poly) ? PyTuple_New(4) : PyTuple_New(3); if (returnValue) { // unlikely this would ever fail, if it does python sets an error - PyTuple_SET_ITEM(returnValue, 0, m_pHitObject->GetProxy()); + PyTuple_SET_ITEM(returnValue, 0, rayData.m_hitObject->GetProxy()); PyTuple_SET_ITEM(returnValue, 1, PyObjectFrom(callback.m_hitPoint)); PyTuple_SET_ITEM(returnValue, 2, PyObjectFrom(callback.m_hitNormal)); if (poly) @@ -3921,7 +3938,7 @@ KX_PYMETHODDEF_DOC(KX_GameObject, stopAction, "stopAction(layer=0)\n" "Stop playing the action on the given layer\n") { - short layer=0; + short layer = 0; if (!PyArg_ParseTuple(args, "|h:stopAction", &layer)) return NULL; @@ -3937,7 +3954,7 @@ KX_PYMETHODDEF_DOC(KX_GameObject, getActionFrame, "getActionFrame(layer=0)\n" "Gets the current frame of the action playing in the supplied layer\n") { - short layer=0; + short layer = 0; if (!PyArg_ParseTuple(args, "|h:getActionFrame", &layer)) return NULL; @@ -3947,11 +3964,25 @@ KX_PYMETHODDEF_DOC(KX_GameObject, getActionFrame, return PyFloat_FromDouble(GetActionFrame(layer)); } +KX_PYMETHODDEF_DOC(KX_GameObject, getActionName, + "getActionName(layer=0)\n" + "Gets the name of the current action playing in the supplied layer\n") +{ + short layer = 0; + + if (!PyArg_ParseTuple(args, "|h:getActionName", &layer)) + return NULL; + + layer_check(layer, "getActionName"); + + return PyUnicode_FromString(GetActionName(layer)); +} + KX_PYMETHODDEF_DOC(KX_GameObject, setActionFrame, "setActionFrame(frame, layer=0)\n" "Set the current frame of the action playing in the supplied layer\n") { - short layer=0; + short layer = 0; float frame; if (!PyArg_ParseTuple(args, "f|h:setActionFrame", &frame, &layer)) @@ -3968,7 +3999,7 @@ KX_PYMETHODDEF_DOC(KX_GameObject, isPlayingAction, "isPlayingAction(layer=0)\n" "Checks to see if there is an action playing in the given layer\n") { - short layer=0; + short layer = 0; if (!PyArg_ParseTuple(args, "|h:isPlayingAction", &layer)) return NULL; @@ -4066,11 +4097,11 @@ bool ConvertPythonToGameObject(PyObject *value, KX_GameObject **object, bool py_ } } - if ( PyObject_TypeCheck(value, &KX_GameObject::Type) || - PyObject_TypeCheck(value, &KX_LightObject::Type) || - PyObject_TypeCheck(value, &KX_Camera::Type) || - PyObject_TypeCheck(value, &KX_FontObject::Type) || - PyObject_TypeCheck(value, &KX_NavMeshObject::Type)) + if (PyObject_TypeCheck(value, &KX_GameObject::Type) || + PyObject_TypeCheck(value, &KX_LightObject::Type) || + PyObject_TypeCheck(value, &KX_Camera::Type) || + PyObject_TypeCheck(value, &KX_FontObject::Type) || + PyObject_TypeCheck(value, &KX_NavMeshObject::Type)) { *object = static_cast<KX_GameObject*>BGE_PROXY_REF(value); diff --git a/source/gameengine/Ketsji/KX_GameObject.h b/source/gameengine/Ketsji/KX_GameObject.h index 99c8a6d718b..c2c455dab6a 100644 --- a/source/gameengine/Ketsji/KX_GameObject.h +++ b/source/gameengine/Ketsji/KX_GameObject.h @@ -111,9 +111,6 @@ protected: PHY_IPhysicsController* m_pPhysicsController; PHY_IGraphicController* m_pGraphicController; - STR_String m_testPropName; - bool m_xray; - KX_GameObject* m_pHitObject; SG_Node* m_pSGNode; @@ -131,10 +128,19 @@ protected: BL_ActionManager* GetActionManager(); bool m_bRecordAnimation; + public: bool m_isDeformable; /** + * KX_GameObject custom infos for ray cast, it contains property name, + * collision mask, xray flag and hited object. + * This structure is created during ray cast and passed as argument + * "data" to functions KX_GameObject::NeedRayCast and KX_GameObject::RayHit. + */ + struct RayCastData; + + /** * Helper function for modules that can't include KX_ClientObjectInfo.h */ static KX_GameObject* GetClientObject(KX_ClientObjectInfo* info); @@ -277,6 +283,11 @@ public: float GetActionFrame(short layer); /** + * Gets the name of the current action + */ + const char *GetActionName(short layer); + + /** * Sets the current frame of an action */ void SetActionFrame(short layer, float frame); @@ -316,12 +327,6 @@ public: */ void UpdateActionManager(float curtime); - /** - * Have the action manager update IPOs - * note: not thread-safe! - */ - void UpdateActionIPOs(); - /********************************* * End Animation API *********************************/ @@ -656,8 +661,10 @@ public: return (m_pSGNode && m_pSGNode->GetSGParent() && m_pSGNode->GetSGParent()->IsVertexParent()); } - bool RayHit(KX_ClientObjectInfo* client, KX_RayCast* result, void * const data); - bool NeedRayCast(KX_ClientObjectInfo* client); + /// \see KX_RayCast + bool RayHit(KX_ClientObjectInfo *client, KX_RayCast *result, RayCastData *rayData); + /// \see KX_RayCast + bool NeedRayCast(KX_ClientObjectInfo *client, RayCastData *rayData); /** @@ -1037,6 +1044,7 @@ public: KX_PYMETHOD_DOC(KX_GameObject, playAction); KX_PYMETHOD_DOC(KX_GameObject, stopAction); KX_PYMETHOD_DOC(KX_GameObject, getActionFrame); + KX_PYMETHOD_DOC(KX_GameObject, getActionName); KX_PYMETHOD_DOC(KX_GameObject, setActionFrame); KX_PYMETHOD_DOC(KX_GameObject, isPlayingAction); diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp index c7cf556a2d3..c6e5f734c16 100644 --- a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp +++ b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp @@ -75,6 +75,8 @@ #include "KX_NavMeshObject.h" +#include "BL_Action.h" // For managing action lock. + #define DEFAULT_LOGIC_TIC_RATE 60.0 //#define DEFAULT_PHYSICS_TIC_RATE 60.0 @@ -181,6 +183,8 @@ KX_KetsjiEngine::KX_KetsjiEngine(KX_ISystem* system) #endif m_taskscheduler = BLI_task_scheduler_create(TASK_SCHEDULER_AUTO_THREADS); + + BL_Action::InitLock(); } @@ -200,6 +204,8 @@ KX_KetsjiEngine::~KX_KetsjiEngine() if (m_taskscheduler) BLI_task_scheduler_free(m_taskscheduler); + + BL_Action::EndLock(); } @@ -1019,6 +1025,10 @@ void KX_KetsjiEngine::GetSceneViewport(KX_Scene *scene, KX_Camera* cam, RAS_Rect void KX_KetsjiEngine::UpdateAnimations(KX_Scene *scene) { + if (scene->IsSuspended()) { + return; + } + // Handle the animations independently of the logic time step if (GetRestrictAnimationFPS()) { double anim_timestep = 1.0 / KX_GetActiveScene()->GetAnimationFPS(); @@ -1750,6 +1760,16 @@ void KX_KetsjiEngine::SetAnimRecordMode(bool animation_record, int startFrame) m_currentFrame = startFrame; } +int KX_KetsjiEngine::getAnimRecordFrame() const +{ + return m_currentFrame; +} + +void KX_KetsjiEngine::setAnimRecordFrame(int framenr) +{ + m_currentFrame = framenr; +} + bool KX_KetsjiEngine::GetUseFixedTime(void) const { return m_bFixedTime; diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.h b/source/gameengine/Ketsji/KX_KetsjiEngine.h index b4bd8a64f78..04e09c8db15 100644 --- a/source/gameengine/Ketsji/KX_KetsjiEngine.h +++ b/source/gameengine/Ketsji/KX_KetsjiEngine.h @@ -225,6 +225,9 @@ public: KX_ISceneConverter* GetSceneConverter() { return m_sceneconverter; } void SetAnimRecordMode(bool animation_record, int startFrame); + int getAnimRecordFrame() const; + void setAnimRecordFrame(int framenr); + RAS_IRasterizer* GetRasterizer() { return m_rasterizer; } RAS_ICanvas* GetCanvas() { return m_canvas; } SCA_IInputDevice* GetKeyboardDevice() { return m_keyboarddevice; } @@ -257,7 +260,7 @@ public: void ConvertAndAddScene(const STR_String& scenename,bool overlay); void RemoveScene(const STR_String& scenename); - bool ReplaceScene(const STR_String& oldscene,const STR_String& newscene); + bool ReplaceScene(const STR_String& oldscene,const STR_String& newscene); void SuspendScene(const STR_String& scenename); void ResumeScene(const STR_String& scenename); diff --git a/source/gameengine/Ketsji/KX_MouseFocusSensor.cpp b/source/gameengine/Ketsji/KX_MouseFocusSensor.cpp index 46f27e1a2df..db2cb1fdcfd 100644 --- a/source/gameengine/Ketsji/KX_MouseFocusSensor.cpp +++ b/source/gameengine/Ketsji/KX_MouseFocusSensor.cpp @@ -144,7 +144,7 @@ bool KX_MouseFocusSensor::Evaluate() return result; } -bool KX_MouseFocusSensor::RayHit(KX_ClientObjectInfo *client_info, KX_RayCast *result, void * const data) +bool KX_MouseFocusSensor::RayHit(KX_ClientObjectInfo *client_info, KX_RayCast *result, void *UNUSED(data)) { KX_GameObject* hitKXObj = client_info->m_gameobject; @@ -198,7 +198,7 @@ bool KX_MouseFocusSensor::RayHit(KX_ClientObjectInfo *client_info, KX_RayCast *r /* this function is used to pre-filter the object before casting the ray on them. * This is useful for "X-Ray" option when we want to see "through" unwanted object. */ -bool KX_MouseFocusSensor::NeedRayCast(KX_ClientObjectInfo* client) +bool KX_MouseFocusSensor::NeedRayCast(KX_ClientObjectInfo *client, void *UNUSED(data)) { KX_GameObject *hitKXObj = client->m_gameobject; @@ -356,7 +356,7 @@ bool KX_MouseFocusSensor::ParentObjectHasFocusCamera(KX_Camera *cam) PHY_IPhysicsEnvironment* physics_environment = m_kxscene->GetPhysicsEnvironment(); // get UV mapping - KX_RayCast::Callback<KX_MouseFocusSensor> callback(this,physics_controller,NULL,false,true); + KX_RayCast::Callback<KX_MouseFocusSensor, void> callback(this,physics_controller,NULL,false,true); KX_RayCast::RayTest(physics_environment, m_prevSourcePoint, m_prevTargetPoint, callback); diff --git a/source/gameengine/Ketsji/KX_MouseFocusSensor.h b/source/gameengine/Ketsji/KX_MouseFocusSensor.h index 0c7c8ab676a..dd9295b2ff4 100644 --- a/source/gameengine/Ketsji/KX_MouseFocusSensor.h +++ b/source/gameengine/Ketsji/KX_MouseFocusSensor.h @@ -35,6 +35,8 @@ #include "SCA_MouseSensor.h" +#include "BLI_utildefines.h" + class KX_RayCast; /** @@ -90,8 +92,10 @@ class KX_MouseFocusSensor : public SCA_MouseSensor return result; }; - bool RayHit(KX_ClientObjectInfo* client, KX_RayCast* result, void * const data); - bool NeedRayCast(KX_ClientObjectInfo* client); + /// \see KX_RayCast + bool RayHit(KX_ClientObjectInfo *client, KX_RayCast *result, void *UNUSED(data)); + /// \see KX_RayCast + bool NeedRayCast(KX_ClientObjectInfo *client, void *UNUSED(data)); const MT_Point3& RaySource() const; const MT_Point3& RayTarget() const; diff --git a/source/gameengine/Ketsji/KX_PyConstraintBinding.cpp b/source/gameengine/Ketsji/KX_PyConstraintBinding.cpp index 5877df5f289..a0084662490 100644 --- a/source/gameengine/Ketsji/KX_PyConstraintBinding.cpp +++ b/source/gameengine/Ketsji/KX_PyConstraintBinding.cpp @@ -583,7 +583,7 @@ static PyObject *gPyRemoveConstraint(PyObject *self, { if (PHY_GetActiveEnvironment()) { - PHY_GetActiveEnvironment()->RemoveConstraint(constraintid); + PHY_GetActiveEnvironment()->RemoveConstraintById(constraintid); } } else { diff --git a/source/gameengine/Ketsji/KX_PythonInit.cpp b/source/gameengine/Ketsji/KX_PythonInit.cpp index 4cb632ed739..37768c75ba5 100644 --- a/source/gameengine/Ketsji/KX_PythonInit.cpp +++ b/source/gameengine/Ketsji/KX_PythonInit.cpp @@ -37,9 +37,6 @@ #endif #ifdef WITH_PYTHON -# ifdef _POSIX_C_SOURCE -# undef _POSIX_C_SOURCE -# endif # ifdef _XOPEN_SOURCE # undef _XOPEN_SOURCE # endif @@ -526,6 +523,27 @@ static PyObject *gPyGetPhysicsTicRate(PyObject *) return PyFloat_FromDouble(PHY_GetActiveEnvironment()->GetFixedTimeStep()); } +static PyObject *gPySetAnimRecordFrame(PyObject *, PyObject *args) +{ + int anim_record_frame; + + if (!PyArg_ParseTuple(args, "i:setAnimRecordFrame", &anim_record_frame)) + return NULL; + + if (anim_record_frame < 0 && (U.flag & USER_NONEGFRAMES)) { + PyErr_Format(PyExc_ValueError, "Frame number must be non-negative (was %i).", anim_record_frame); + return NULL; + } + + gp_KetsjiEngine->setAnimRecordFrame(anim_record_frame); + Py_RETURN_NONE; +} + +static PyObject *gPyGetAnimRecordFrame(PyObject *) +{ + return PyLong_FromLong(gp_KetsjiEngine->getAnimRecordFrame()); +} + static PyObject *gPyGetAverageFrameRate(PyObject *) { return PyFloat_FromDouble(KX_KetsjiEngine::GetAverageFrameRate()); @@ -887,6 +905,8 @@ static struct PyMethodDef game_methods[] = { {"setLogicTicRate", (PyCFunction) gPySetLogicTicRate, METH_VARARGS, (const char *)"Sets the logic tic rate"}, {"getPhysicsTicRate", (PyCFunction) gPyGetPhysicsTicRate, METH_NOARGS, (const char *)"Gets the physics tic rate"}, {"setPhysicsTicRate", (PyCFunction) gPySetPhysicsTicRate, METH_VARARGS, (const char *)"Sets the physics tic rate"}, + {"getAnimRecordFrame", (PyCFunction) gPyGetAnimRecordFrame, METH_NOARGS, (const char *)"Gets the current frame number used for animation recording"}, + {"setAnimRecordFrame", (PyCFunction) gPySetAnimRecordFrame, METH_VARARGS, (const char *)"Sets the current frame number used for animation recording"}, {"getExitKey", (PyCFunction) gPyGetExitKey, METH_NOARGS, (const char *)"Gets the key used to exit the game engine"}, {"setExitKey", (PyCFunction) gPySetExitKey, METH_VARARGS, (const char *)"Sets the key used to exit the game engine"}, {"getAverageFrameRate", (PyCFunction) gPyGetAverageFrameRate, METH_NOARGS, (const char *)"Gets the estimated average frame rate"}, @@ -1781,6 +1801,7 @@ PyMODINIT_FUNC initGameLogicPythonBinding() KX_MACRO_addTypesToDict(d, KX_GAME_QUIT, KX_GameActuator::KX_GAME_QUIT); KX_MACRO_addTypesToDict(d, KX_GAME_SAVECFG, KX_GameActuator::KX_GAME_SAVECFG); KX_MACRO_addTypesToDict(d, KX_GAME_LOADCFG, KX_GameActuator::KX_GAME_LOADCFG); + KX_MACRO_addTypesToDict(d, KX_GAME_SCREENSHOT, KX_GameActuator::KX_GAME_SCREENSHOT); /* Scene Actuator Modes */ KX_MACRO_addTypesToDict(d, KX_SCENE_RESTART, KX_SceneActuator::KX_SCENE_RESTART); diff --git a/source/gameengine/Ketsji/KX_RayCast.h b/source/gameengine/Ketsji/KX_RayCast.h index e47ac676eb1..c977fb8f385 100644 --- a/source/gameengine/Ketsji/KX_RayCast.h +++ b/source/gameengine/Ketsji/KX_RayCast.h @@ -44,12 +44,15 @@ struct KX_ClientObjectInfo; /** * Defines a function for doing a ray cast. * - * eg KX_RayCast::RayTest(ignore_physics_controller, physics_environment, frompoint, topoint, result_point, result_normal, KX_RayCast::Callback<KX_MyClass>(this, data) + * eg KX_RayCast::RayTest(ignore_physics_controller, physics_environment, frompoint, topoint, result_point, result_normal, KX_RayCast::Callback<MyClass, MyDataClass>(this, data) * - * Calls myclass->RayHit(client, hit_point, hit_normal, data) for all client + * Calls myclass->NeedRayCast(client, data) for all client in environment + * and myclass->RayHit(client, hit_point, hit_normal, data) for all client * between frompoint and topoint * - * myclass->RayHit should return true to end the raycast, false to ignore the current client. + * myclass->NeedRayCast should return true to ray test the current client. + * + * myclass->RayHit should return true to end the raycast, false to ignore the current client and to continue. * * Returns true if a client was accepted, false if nothing found. */ @@ -80,10 +83,10 @@ public: /** * Callback wrapper. * - * Construct with KX_RayCast::Callback<MyClass>(this, data) + * Construct with KX_RayCast::Callback<MyClass, MyDataClass>(this, data) * and pass to KX_RayCast::RayTest */ - template<class T> class Callback; + template<class T, class dataT> class Callback; /// Public interface. /// Implement bool RayHit in your class to receive ray callbacks. @@ -99,12 +102,18 @@ public: #endif }; -template<class T> class KX_RayCast::Callback : public KX_RayCast +template<class T, class dataT> +class KX_RayCast::Callback : public KX_RayCast { T *self; - void *data; + /** + * Some user info passed as argument in constructor. + * It contains all info needed to check client in NeedRayCast + * and RayHit. + */ + dataT *data; public: - Callback(T *_self, PHY_IPhysicsController* controller=NULL, void *_data = NULL, bool faceNormal=false, bool faceUV=false) + Callback(T *_self, PHY_IPhysicsController *controller = NULL, dataT *_data = NULL, bool faceNormal = false, bool faceUV = false) : KX_RayCast(controller, faceNormal, faceUV), self(_self), data(_data) @@ -127,7 +136,7 @@ public: MT_assert(info && "Physics controller with no client object info"); return false; } - return self->NeedRayCast(info); + return self->NeedRayCast(info, data); } diff --git a/source/gameengine/Ketsji/KX_RaySensor.cpp b/source/gameengine/Ketsji/KX_RaySensor.cpp index c97d233a67b..4ffb5f332db 100644 --- a/source/gameengine/Ketsji/KX_RaySensor.cpp +++ b/source/gameengine/Ketsji/KX_RaySensor.cpp @@ -107,7 +107,7 @@ bool KX_RaySensor::IsPositiveTrigger() return result; } -bool KX_RaySensor::RayHit(KX_ClientObjectInfo *client, KX_RayCast *result, void * const data) +bool KX_RaySensor::RayHit(KX_ClientObjectInfo *client, KX_RayCast *result, void *UNUSED(data)) { KX_GameObject* hitKXObj = client->m_gameobject; @@ -158,7 +158,7 @@ bool KX_RaySensor::RayHit(KX_ClientObjectInfo *client, KX_RayCast *result, void /* this function is used to pre-filter the object before casting the ray on them. * This is useful for "X-Ray" option when we want to see "through" unwanted object. */ -bool KX_RaySensor::NeedRayCast(KX_ClientObjectInfo *client) +bool KX_RaySensor::NeedRayCast(KX_ClientObjectInfo *client, void *UNUSED(data)) { KX_GameObject *hitKXObj = client->m_gameobject; @@ -282,7 +282,7 @@ bool KX_RaySensor::Evaluate() PHY_IPhysicsEnvironment* physics_environment = this->m_scene->GetPhysicsEnvironment(); - KX_RayCast::Callback<KX_RaySensor> callback(this, spc); + KX_RayCast::Callback<KX_RaySensor, void> callback(this, spc); KX_RayCast::RayTest(physics_environment, frompoint, topoint, callback); /* now pass this result to some controller */ diff --git a/source/gameengine/Ketsji/KX_RaySensor.h b/source/gameengine/Ketsji/KX_RaySensor.h index 4604863a233..1901bb04f86 100644 --- a/source/gameengine/Ketsji/KX_RaySensor.h +++ b/source/gameengine/Ketsji/KX_RaySensor.h @@ -38,6 +38,8 @@ #include "SCA_IScene.h" /* only for scene replace */ #include "KX_Scene.h" /* only for scene replace */ +#include "BLI_utildefines.h" + struct KX_ClientObjectInfo; class KX_RayCast; @@ -74,8 +76,10 @@ public: virtual bool IsPositiveTrigger(); virtual void Init(); - bool RayHit(KX_ClientObjectInfo* client, KX_RayCast* result, void * const data); - bool NeedRayCast(KX_ClientObjectInfo* client); + /// \see KX_RayCast + bool RayHit(KX_ClientObjectInfo *client, KX_RayCast *result, void *UNUSED(data)); + /// \see KX_RayCast + bool NeedRayCast(KX_ClientObjectInfo *client, void *UNUSED(data)); virtual void Replace_IScene(SCA_IScene *val) { diff --git a/source/gameengine/Ketsji/KX_SCA_DynamicActuator.cpp b/source/gameengine/Ketsji/KX_SCA_DynamicActuator.cpp index e02eca3db63..96e1cc29de3 100644 --- a/source/gameengine/Ketsji/KX_SCA_DynamicActuator.cpp +++ b/source/gameengine/Ketsji/KX_SCA_DynamicActuator.cpp @@ -121,7 +121,9 @@ bool KX_SCA_DynamicActuator::Update() switch (m_dyn_operation) { case 0: - controller->RestoreDynamics(); + // Child objects must be static, so we block changing to dynamic + if (!obj->GetParent()) + controller->RestoreDynamics(); break; case 1: controller->SuspendDynamics(); diff --git a/source/gameengine/Ketsji/KX_Scene.cpp b/source/gameengine/Ketsji/KX_Scene.cpp index fd709a87def..16d1fdd6ea2 100644 --- a/source/gameengine/Ketsji/KX_Scene.cpp +++ b/source/gameengine/Ketsji/KX_Scene.cpp @@ -584,6 +584,10 @@ KX_GameObject* KX_Scene::AddNodeReplicaObject(class SG_IObject* node, class CVal newctrl->SetNewClientInfo(newobj->getClientInfo()); newobj->SetPhysicsController(newctrl, newobj->IsDynamic()); newctrl->PostProcessReplica(motionstate, parentctrl); + + // Child objects must be static + if (parent) + newctrl->SuspendDynamics(); } return newobj; @@ -1002,17 +1006,28 @@ void KX_Scene::RemoveObject(class CValue* gameobj) //newobj->SetSGNode(0); } +void KX_Scene::RemoveDupliGroup(class CValue *gameobj) +{ + KX_GameObject *newobj = (KX_GameObject *) gameobj; + + if (newobj->IsDupliGroup()) { + for (int i = 0; i < newobj->GetInstanceObjects()->GetCount(); i++) { + CValue *obj = newobj->GetInstanceObjects()->GetValue(i); + DelayedRemoveObject(obj); + } + } +} + void KX_Scene::DelayedRemoveObject(class CValue* gameobj) { - //KX_GameObject* newobj = (KX_GameObject*) gameobj; + RemoveDupliGroup(gameobj); + if (!m_euthanasyobjects->SearchValue(gameobj)) { m_euthanasyobjects->Add(gameobj->AddRef()); - } + } } - - int KX_Scene::NewRemoveObject(class CValue* gameobj) { int ret; @@ -1674,10 +1689,6 @@ void KX_Scene::UpdateAnimations(double curtime) BLI_task_pool_work_and_wait(pool); BLI_task_pool_free(pool); - - for (int i=0; i<m_animatedlist->GetCount(); ++i) { - ((KX_GameObject*)m_animatedlist->GetValue(i))->UpdateActionIPOs(); - } } void KX_Scene::LogicUpdateFrame(double curtime, bool frame) @@ -2015,8 +2026,12 @@ static void MergeScene_GameObject(KX_GameObject* gameobj, KX_Scene *to, KX_Scene to->GetLogicManager()->RegisterGameObjectName(gameobj->GetName(), gameobj); to->GetLogicManager()->RegisterGameObj(gameobj->GetBlenderObject(), gameobj); - for (int i=0; i<gameobj->GetMeshCount(); ++i) - to->GetLogicManager()->RegisterGameMeshName(gameobj->GetMesh(i)->GetName(), gameobj->GetBlenderObject()); + for (int i = 0; i < gameobj->GetMeshCount(); ++i) { + RAS_MeshObject *meshobj = gameobj->GetMesh(i); + // Register the mesh object by name and blender object. + to->GetLogicManager()->RegisterGameMeshName(meshobj->GetName(), gameobj->GetBlenderObject()); + to->GetLogicManager()->RegisterMeshName(meshobj->GetName(), meshobj); + } } bool KX_Scene::MergeScene(KX_Scene *other) @@ -2060,6 +2075,28 @@ bool KX_Scene::MergeScene(KX_Scene *other) MergeScene_GameObject(gameobj, this, other); } + if (env) { + env->MergeEnvironment(env_other); + CListValue *otherObjects = other->GetObjectList(); + + // List of all physics objects to merge (needed by ReplicateConstraints). + std::vector<KX_GameObject *> physicsObjects; + for (unsigned int i = 0; i < otherObjects->GetCount(); ++i) { + KX_GameObject *gameobj = (KX_GameObject *)otherObjects->GetValue(i); + if (gameobj->GetPhysicsController()) { + physicsObjects.push_back(gameobj); + } + } + + for (unsigned int i = 0; i < physicsObjects.size(); ++i) { + KX_GameObject *gameobj = physicsObjects[i]; + // Replicate all constraints in the right physics environment. + gameobj->GetPhysicsController()->ReplicateConstraints(gameobj, physicsObjects); + gameobj->ClearConstraints(); + } + } + + GetTempObjectList()->MergeList(other->GetTempObjectList()); other->GetTempObjectList()->ReleaseAndRemoveAll(); @@ -2075,9 +2112,6 @@ bool KX_Scene::MergeScene(KX_Scene *other) GetLightList()->MergeList(other->GetLightList()); other->GetLightList()->ReleaseAndRemoveAll(); - if (env) - env->MergeEnvironment(env_other); - /* move materials across, assume they both use the same scene-converters * Do this after lights are merged so materials can use the lights in shaders */ diff --git a/source/gameengine/Ketsji/KX_Scene.h b/source/gameengine/Ketsji/KX_Scene.h index 4f7ad98f30d..046ad25c09c 100644 --- a/source/gameengine/Ketsji/KX_Scene.h +++ b/source/gameengine/Ketsji/KX_Scene.h @@ -339,6 +339,7 @@ public: void RemoveNodeDestructObject(SG_IObject* node, CValue* gameobj); void RemoveObject(CValue* gameobj); + void RemoveDupliGroup(CValue *gameobj); void DelayedRemoveObject(CValue* gameobj); int NewRemoveObject(CValue* gameobj); diff --git a/source/gameengine/Ketsji/KX_SoundActuator.cpp b/source/gameengine/Ketsji/KX_SoundActuator.cpp index 14f0598d088..f6f09dd3bda 100644 --- a/source/gameengine/Ketsji/KX_SoundActuator.cpp +++ b/source/gameengine/Ketsji/KX_SoundActuator.cpp @@ -65,7 +65,7 @@ KX_SoundActuator::KX_SoundActuator(SCA_IObject* gameobj, KX_SOUNDACT_TYPE type)//, : SCA_IActuator(gameobj, KX_ACT_SOUND) { - m_sound = AUD_Sound_copy(sound); + m_sound = sound ? AUD_Sound_copy(sound) : NULL; m_handle = NULL; m_volume = volume; m_pitch = pitch; @@ -122,7 +122,9 @@ void KX_SoundActuator::play() break; } - //m_handle = AUD_Device_play(BKE_sound_get_device(), sound, false); + AUD_Device* device = AUD_Device_getCurrent(); + m_handle = AUD_Device_play(device, sound, false); + AUD_Device_free(device); // in case of pingpong, we have to free the sound if(sound != m_sound) diff --git a/source/gameengine/Ketsji/KX_SteeringActuator.cpp b/source/gameengine/Ketsji/KX_SteeringActuator.cpp index 83597f9125a..cd2cd2bae0b 100644 --- a/source/gameengine/Ketsji/KX_SteeringActuator.cpp +++ b/source/gameengine/Ketsji/KX_SteeringActuator.cpp @@ -561,7 +561,7 @@ PyAttributeDef KX_SteeringActuator::Attributes[] = { KX_PYATTRIBUTE_RO_FUNCTION("steeringVec", KX_SteeringActuator, pyattr_get_steeringVec), KX_PYATTRIBUTE_SHORT_RW("facingMode", 0, 6, true, KX_SteeringActuator, m_facingMode), KX_PYATTRIBUTE_INT_RW("pathUpdatePeriod", -1, 100000, true, KX_SteeringActuator, m_pathUpdatePeriod), - KX_PYATTRIBUTE_BOOL_RW("lockZVelocity", KX_SteeringActuator, m_lockzvel), + KX_PYATTRIBUTE_BOOL_RW("lockZVelocity", KX_SteeringActuator, m_lockzvel), { NULL } //Sentinel }; @@ -610,8 +610,7 @@ int KX_SteeringActuator::pyattr_set_navmesh(void *self, const struct KX_PYATTRIB if (!ConvertPythonToGameObject(value, &gameobj, true, "actuator.object = value: KX_SteeringActuator")) return PY_SET_ATTR_FAIL; // ConvertPythonToGameObject sets the error - if (dynamic_cast<KX_NavMeshObject *>(gameobj) == NULL) - { + if (dynamic_cast<KX_NavMeshObject *>(gameobj) == NULL) { PyErr_Format(PyExc_TypeError, "KX_NavMeshObject is expected"); return PY_SET_ATTR_FAIL; } diff --git a/source/gameengine/Ketsji/KX_WorldInfo.cpp b/source/gameengine/Ketsji/KX_WorldInfo.cpp index 111d81cad2e..be8b1ce92fc 100644 --- a/source/gameengine/Ketsji/KX_WorldInfo.cpp +++ b/source/gameengine/Ketsji/KX_WorldInfo.cpp @@ -253,11 +253,11 @@ PyAttributeDef KX_WorldInfo::Attributes[] = { /*----------------------mathutils callbacks ----------------------------*/ /* subtype */ -#define MATHUTILS_VEC_CB_MIST_COLOR 1 -#define MATHUTILS_VEC_CB_BACK_COLOR 2 -#define MATHUTILS_VEC_CB_AMBIENT_COLOR 3 +#define MATHUTILS_COL_CB_MIST_COLOR 1 +#define MATHUTILS_COL_CB_BACK_COLOR 2 +#define MATHUTILS_COL_CB_AMBIENT_COLOR 3 -static unsigned char mathutils_world_vector_cb_index = -1; /* index for our callbacks */ +static unsigned char mathutils_world_color_cb_index = -1; /* index for our callbacks */ static int mathutils_world_generic_check(BaseMathObject *bmo) { @@ -268,20 +268,20 @@ static int mathutils_world_generic_check(BaseMathObject *bmo) return 0; } -static int mathutils_world_vector_get(BaseMathObject *bmo, int subtype) +static int mathutils_world_color_get(BaseMathObject *bmo, int subtype) { KX_WorldInfo *self = static_cast<KX_WorldInfo*>BGE_PROXY_REF(bmo->cb_user); if (self == NULL) return -1; switch (subtype) { - case MATHUTILS_VEC_CB_MIST_COLOR: + case MATHUTILS_COL_CB_MIST_COLOR: copy_v3_v3(bmo->data, self->m_mistcolor); break; - case MATHUTILS_VEC_CB_BACK_COLOR: + case MATHUTILS_COL_CB_BACK_COLOR: copy_v3_v3(bmo->data, self->m_backgroundcolor); break; - case MATHUTILS_VEC_CB_AMBIENT_COLOR: + case MATHUTILS_COL_CB_AMBIENT_COLOR: copy_v3_v3(bmo->data, self->m_ambientcolor); break; default: @@ -290,7 +290,7 @@ static int mathutils_world_vector_get(BaseMathObject *bmo, int subtype) return 0; } -static int mathutils_world_vector_set(BaseMathObject *bmo, int subtype) +static int mathutils_world_color_set(BaseMathObject *bmo, int subtype) { KX_WorldInfo *self = static_cast<KX_WorldInfo*>BGE_PROXY_REF(bmo->cb_user); @@ -298,13 +298,13 @@ static int mathutils_world_vector_set(BaseMathObject *bmo, int subtype) return -1; switch (subtype) { - case MATHUTILS_VEC_CB_MIST_COLOR: + case MATHUTILS_COL_CB_MIST_COLOR: self->setMistColor(bmo->data[0], bmo->data[1], bmo->data[2]); break; - case MATHUTILS_VEC_CB_BACK_COLOR: + case MATHUTILS_COL_CB_BACK_COLOR: self->setBackColor(bmo->data[0], bmo->data[1], bmo->data[2]); break; - case MATHUTILS_VEC_CB_AMBIENT_COLOR: + case MATHUTILS_COL_CB_AMBIENT_COLOR: self->setAmbientColor(bmo->data[0], bmo->data[1], bmo->data[2]); break; default: @@ -313,7 +313,7 @@ static int mathutils_world_vector_set(BaseMathObject *bmo, int subtype) return 0; } -static int mathutils_world_vector_get_index(BaseMathObject *bmo, int subtype, int index) +static int mathutils_world_color_get_index(BaseMathObject *bmo, int subtype, int index) { KX_WorldInfo *self = static_cast<KX_WorldInfo*>BGE_PROXY_REF(bmo->cb_user); @@ -321,19 +321,19 @@ static int mathutils_world_vector_get_index(BaseMathObject *bmo, int subtype, in return -1; switch (subtype) { - case MATHUTILS_VEC_CB_MIST_COLOR: + case MATHUTILS_COL_CB_MIST_COLOR: { const float *color = self->m_mistcolor; bmo->data[index] = color[index]; } break; - case MATHUTILS_VEC_CB_BACK_COLOR: + case MATHUTILS_COL_CB_BACK_COLOR: { const float *color = self->m_backgroundcolor; bmo->data[index] = color[index]; } break; - case MATHUTILS_VEC_CB_AMBIENT_COLOR: + case MATHUTILS_COL_CB_AMBIENT_COLOR: { const float *color = self->m_ambientcolor; bmo->data[index] = color[index]; @@ -345,7 +345,7 @@ static int mathutils_world_vector_get_index(BaseMathObject *bmo, int subtype, in return 0; } -static int mathutils_world_vector_set_index(BaseMathObject *bmo, int subtype, int index) +static int mathutils_world_color_set_index(BaseMathObject *bmo, int subtype, int index) { KX_WorldInfo *self = static_cast<KX_WorldInfo*>BGE_PROXY_REF(bmo->cb_user); @@ -354,17 +354,17 @@ static int mathutils_world_vector_set_index(BaseMathObject *bmo, int subtype, in float color[4]; switch (subtype) { - case MATHUTILS_VEC_CB_MIST_COLOR: + case MATHUTILS_COL_CB_MIST_COLOR: copy_v3_v3(color, self->m_mistcolor); color[index] = bmo->data[index]; self->setMistColor(color[0], color[1], color[2]); break; - case MATHUTILS_VEC_CB_BACK_COLOR: + case MATHUTILS_COL_CB_BACK_COLOR: copy_v3_v3(color, self->m_backgroundcolor); color[index] = bmo->data[index]; self->setBackColor(color[0], color[1], color[2]); break; - case MATHUTILS_VEC_CB_AMBIENT_COLOR: + case MATHUTILS_COL_CB_AMBIENT_COLOR: copy_v3_v3(color, self->m_ambientcolor); color[index] = bmo->data[index]; self->setAmbientColor(color[0], color[1], color[2]); @@ -375,18 +375,18 @@ static int mathutils_world_vector_set_index(BaseMathObject *bmo, int subtype, in return 0; } -static Mathutils_Callback mathutils_world_vector_cb = { +static Mathutils_Callback mathutils_world_color_cb = { mathutils_world_generic_check, - mathutils_world_vector_get, - mathutils_world_vector_set, - mathutils_world_vector_get_index, - mathutils_world_vector_set_index + mathutils_world_color_get, + mathutils_world_color_set, + mathutils_world_color_get_index, + mathutils_world_color_set_index }; void KX_WorldInfo_Mathutils_Callback_Init() { // register mathutils callbacks, ok to run more than once. - mathutils_world_vector_cb_index = Mathutils_RegisterCallback(&mathutils_world_vector_cb); + mathutils_world_color_cb_index = Mathutils_RegisterCallback(&mathutils_world_color_cb); } #endif // USE_MATHUTILS @@ -418,9 +418,9 @@ PyObject *KX_WorldInfo::pyattr_get_mist_typeconst(void *self_v, const KX_PYATTRI PyObject *KX_WorldInfo::pyattr_get_mist_color(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) { #ifdef USE_MATHUTILS - return Vector_CreatePyObject_cb( - BGE_PROXY_FROM_REF_BORROW(self_v), 3, - mathutils_world_vector_cb_index, MATHUTILS_VEC_CB_MIST_COLOR); + return Color_CreatePyObject_cb( + BGE_PROXY_FROM_REF_BORROW(self_v), + mathutils_world_color_cb_index, MATHUTILS_COL_CB_MIST_COLOR); #else KX_WorldInfo *self = static_cast<KX_WorldInfo*>(self_v); return PyObjectFrom(MT_Vector3(self->m_mistcolor)); @@ -444,9 +444,9 @@ PyObject *KX_WorldInfo::pyattr_get_back_color(void *self_v, const KX_PYATTRIBUTE { #ifdef USE_MATHUTILS - return Vector_CreatePyObject_cb( - BGE_PROXY_FROM_REF_BORROW(self_v), 3, - mathutils_world_vector_cb_index, MATHUTILS_VEC_CB_BACK_COLOR); + return Color_CreatePyObject_cb( + BGE_PROXY_FROM_REF_BORROW(self_v), + mathutils_world_color_cb_index, MATHUTILS_COL_CB_BACK_COLOR); #else KX_WorldInfo *self = static_cast<KX_WorldInfo*>(self_v); return PyObjectFrom(MT_Vector3(self->m_backgroundcolor)); @@ -469,9 +469,9 @@ int KX_WorldInfo::pyattr_set_back_color(void *self_v, const KX_PYATTRIBUTE_DEF * PyObject *KX_WorldInfo::pyattr_get_ambient_color(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef) { #ifdef USE_MATHUTILS - return Vector_CreatePyObject_cb( - BGE_PROXY_FROM_REF_BORROW(self_v), 3, - mathutils_world_vector_cb_index, MATHUTILS_VEC_CB_AMBIENT_COLOR); + return Color_CreatePyObject_cb( + BGE_PROXY_FROM_REF_BORROW(self_v), + mathutils_world_color_cb_index, MATHUTILS_COL_CB_AMBIENT_COLOR); #else KX_WorldInfo *self = static_cast<KX_WorldInfo*>(self_v); return PyObjectFrom(MT_Vector3(self->m_ambientcolor)); diff --git a/source/gameengine/Ketsji/KX_WorldInfo.h b/source/gameengine/Ketsji/KX_WorldInfo.h index f2b37ec5587..b155faf2837 100644 --- a/source/gameengine/Ketsji/KX_WorldInfo.h +++ b/source/gameengine/Ketsji/KX_WorldInfo.h @@ -36,10 +36,6 @@ #include "KX_KetsjiEngine.h" #include "EXP_PyObjectPlus.h" -#ifdef WITH_CXX_GUARDEDALLOC -#include "MEM_guardedalloc.h" -#endif - #ifdef USE_MATHUTILS void KX_WorldInfo_Mathutils_Callback_Init(void); #endif @@ -104,10 +100,6 @@ public: static int pyattr_set_ambient_color(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef, PyObject *value); virtual PyObject *py_repr(void); #endif - -#ifdef WITH_CXX_GUARDEDALLOC - MEM_CXX_CLASS_ALLOC_FUNCS("GE:KX_WorldInfo") -#endif }; #endif /* __KX_WORLDINFO_H__ */ diff --git a/source/gameengine/Ketsji/SConscript b/source/gameengine/Ketsji/SConscript index 5b2807d8172..d8dfd3d9bca 100644 --- a/source/gameengine/Ketsji/SConscript +++ b/source/gameengine/Ketsji/SConscript @@ -30,7 +30,8 @@ import sys Import ('env') sources = env.Glob('*.cpp') -defs = env['BF_GL_DEFINITIONS'] +defs = [] +defs += env['BF_GL_DEFINITIONS'] incs = [ '.', |