Browse Source

Do not assume the location of the NavigationMesh component.

Yao Wei Tjong 姚伟忠 10 years ago
parent
commit
a58d855e60

+ 1 - 1
Source/Urho3D/LuaScript/pkgs/Scene/Node.pkg

@@ -173,7 +173,7 @@ class Node : public Animatable
     const VariantMap& GetVars() const;
 
     // template <class T> T* GetComponent() const;
-    Component* GetComponent(const String type) const;
+    Component* GetComponent(const String type, bool recursive = false) const;
 
     // template <class T> void GetComponents(PODVector<T*>& dest, bool recursive = false) const;
     tolua_outside const PODVector<Component*>& NodeGetComponentsWithType @ GetComponents(const String type, bool recursive = false);

+ 1 - 1
Source/Urho3D/Navigation/CrowdAgent.cpp

@@ -583,7 +583,7 @@ void CrowdAgent::OnSceneSet(Scene* scene)
     {
         if (scene == node_)
             LOGERROR(GetTypeName() + " should not be created to the root scene node");
-        crowdManager_ = scene->GetOrCreateComponent<DetourCrowdManager>();
+        crowdManager_ = scene->GetOrCreateComponent<CrowdManager>();
         AddAgentToCrowd();
     }
     else

+ 46 - 24
Source/Urho3D/Navigation/CrowdManager.cpp

@@ -58,9 +58,10 @@ void CrowdAgentUpdateCallback(dtCrowdAgent* ag, float dt)
 CrowdManager::CrowdManager(Context* context) :
     Component(context),
     crowd_(0),
+    navigationMesh_(0),
+    navigationMeshId_(0),
     maxAgents_(DEFAULT_MAX_AGENTS),
     maxAgentRadius_(DEFAULT_MAX_AGENT_RADIUS),
-    navigationMeshId_(0),
     numFilterTypes_(0),
     numObstacleAvoidanceTypes_(0)
 {
@@ -107,7 +108,7 @@ void CrowdManager::ApplyAttributes()
     navigationMeshId_ = navigationMesh_ ? navigationMesh_->GetID() : 0;     // In case of receiving an invalid component id, revert it back to the existing navmesh component id (if any)
 
     // If the Detour crowd initialization parameters have changed then recreate it
-    if (crowd_ && (navMeshChange || crowd_->getAgentCount() != maxAgents_ || crowd_->getMaxAgentRadius() != (maxAgentRadius_ > 0.f ? maxAgentRadius_ : navigationMesh_->GetAgentRadius())))
+    if (crowd_ && (navMeshChange || crowd_->getAgentCount() != maxAgents_ || crowd_->getMaxAgentRadius() != maxAgentRadius_))
         CreateCrowd();
 }
 
@@ -235,10 +236,10 @@ void CrowdManager::SetMaxAgentRadius(float maxAgentRadius)
 
 void CrowdManager::SetNavigationMesh(NavigationMesh* navMesh)
 {
-    if (navMesh != navigationMesh_ && navMesh)
+    if (navMesh != navigationMesh_)     // It is possible to reset navmesh pointer back to 0
     {
         navigationMesh_ = navMesh;
-        navigationMeshId_ = navMesh->GetID();
+        navigationMeshId_ = navMesh ? navMesh->GetID() : 0;
         CreateCrowd();
         MarkNetworkUpdate();
     }
@@ -347,7 +348,7 @@ void CrowdManager::SetObstacleAvoidanceParams(unsigned obstacleAvoidanceType, co
 {
     if (crowd_ && obstacleAvoidanceType < DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS)
     {
-        crowd_->setObstacleAvoidanceParams(obstacleAvoidanceType, (dtObstacleAvoidanceParams*)&params);
+        crowd_->setObstacleAvoidanceParams(obstacleAvoidanceType, reinterpret_cast<const dtObstacleAvoidanceParams*>(&params));
         if (numObstacleAvoidanceTypes_ < obstacleAvoidanceType + 1)
             numObstacleAvoidanceTypes_ = obstacleAvoidanceType + 1;
         MarkNetworkUpdate();
@@ -495,7 +496,7 @@ const CrowdObstacleAvoidanceParams& CrowdManager::GetObstacleAvoidanceParams(uns
 {
     static const CrowdObstacleAvoidanceParams EMPTY_PARAMS = CrowdObstacleAvoidanceParams();
     const dtObstacleAvoidanceParams* params = crowd_ ? crowd_->getObstacleAvoidanceParams(obstacleAvoidanceType) : 0;
-    return params ? *(const CrowdObstacleAvoidanceParams*)params : EMPTY_PARAMS;
+    return params ? *reinterpret_cast<const CrowdObstacleAvoidanceParams*>(params) : EMPTY_PARAMS;
 }
 
 PODVector<CrowdAgent*> CrowdManager::GetAgents(Node* node, bool inCrowdFilter) const
@@ -535,21 +536,21 @@ bool CrowdManager::CreateCrowd()
     crowd_ = dtAllocCrowd();
 
     // Initialize the crowd
-    if (!crowd_->init(maxAgents_, maxAgentRadius_ > 0.f ? maxAgentRadius_ : navigationMesh_->GetAgentRadius(), navigationMesh_->navMesh_, CrowdAgentUpdateCallback))
+    if (maxAgentRadius_ == 0.f)
+        maxAgentRadius_ = navigationMesh_->GetAgentRadius();
+    if (!crowd_->init(maxAgents_, maxAgentRadius_, navigationMesh_->navMesh_, CrowdAgentUpdateCallback))
     {
         LOGERROR("Could not initialize DetourCrowd");
         return false;
     }
 
-    // Reconfigure the newly initialized crowd
     if (recreate)
     {
+        // Reconfigure the newly initialized crowd
         SetFilterTypesAttr(filterTypeConfiguration);
         SetObstacleAvoidanceTypesAttr(obstacleAvoidanceTypeConfiguration);
-    }
 
-    if (recreate)
-    {
+        // Re-add the existing crowd agents
         PODVector<CrowdAgent*> agents = GetAgents();
         for (unsigned i = 0; i < agents.Size(); ++i)
         {
@@ -588,29 +589,37 @@ void CrowdManager::RemoveAgent(CrowdAgent* agent)
     crowd_->removeAgent(agent->GetAgentCrowdId());
 }
 
-void DetourCrowdManager::OnSceneSet(Scene* scene)
+void CrowdManager::OnSceneSet(Scene* scene)
 {
     // Subscribe to the scene subsystem update, which will trigger the crowd update step, and grab a reference
     // to the scene's NavigationMesh
     if (scene)
     {
+        if (scene != node_)
+        {
+            LOGERROR("CrowdManager is a scene component and should only be attached to the scene node");
+            return;
+        }
+
         SubscribeToEvent(scene, E_SCENESUBSYSTEMUPDATE, HANDLER(CrowdManager, HandleSceneSubsystemUpdate));
-        // TODO: Do not assume navmesh component always attach to scene node
-        NavigationMesh* mesh = GetScene()->GetDerivedComponent<NavigationMesh>();
-        if (mesh)
+
+        // Attempt to auto discover a NavigationMesh component (or its derivative) under the scene node
+        NavigationMesh* navMesh = scene->GetDerivedComponent<NavigationMesh>(true);
+        if (navMesh)
         {
-            SubscribeToEvent(mesh, E_NAVIGATION_MESH_REBUILT, HANDLER(CrowdManager, HandleNavMeshFullRebuild));
             navigationMesh_ = navMesh;
             navigationMeshId_ = navMesh->GetID();
             CreateCrowd();
+
+            SubscribeToEvent(navMesh->GetNode(), E_NAVIGATION_MESH_REBUILT, HANDLER(CrowdManager, HandleNavMeshChanged));
+            SubscribeToEvent(navMesh->GetNode(), E_COMPONENTREMOVED, HANDLER(CrowdManager, HandleNavMeshChanged));
         }
-        else
-            LOGERROR("CrowdManager requires an existing navigation mesh");
     }
     else
     {
         UnsubscribeFromEvent(E_SCENESUBSYSTEMUPDATE);
         UnsubscribeFromEvent(E_NAVIGATION_MESH_REBUILT);
+        UnsubscribeFromEvent(E_COMPONENTREMOVED);
         navigationMesh_.Reset();
     }
 }
@@ -634,7 +643,7 @@ const dtQueryFilter* CrowdManager::GetDetourQueryFilter(unsigned filterType) con
 
 void CrowdManager::HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData)
 {
-    // Perform update tick as long as the crowd is initialized and navmesh has not expired
+    // Perform update tick as long as the crowd is initialized and the associated navmesh has not been removed
     if (crowd_ && navigationMesh_)
     {
         using namespace SceneSubsystemUpdate;
@@ -644,12 +653,25 @@ void CrowdManager::HandleSceneSubsystemUpdate(StringHash eventType, VariantMap&
     }
 }
 
-void CrowdManager::HandleNavMeshFullRebuild(StringHash eventType, VariantMap& eventData)
+void CrowdManager::HandleNavMeshChanged(StringHash eventType, VariantMap& eventData)
 {
-    using namespace NavigationMeshRebuilt;
-
-    // The mesh being rebuilt may not have existed before
-    SetNavigationMesh(static_cast<NavigationMesh*>(eventData[P_MESH].GetPtr()));
+    NavigationMesh* navMesh;
+    if (eventType == E_NAVIGATION_MESH_REBUILT)
+    {
+        // The mesh being rebuilt may not have existed before
+        navMesh = static_cast<NavigationMesh*>(eventData[NavigationMeshRebuilt::P_MESH].GetPtr());
+    }
+    else
+    {
+        // eventType == E_COMPONENTREMOVED
+        navMesh = static_cast<NavigationMesh*>(eventData[ComponentRemoved::P_COMPONENT].GetPtr());
+        // Only interested in navmesh component being used to initialized the crowd
+        if (navMesh != navigationMesh_)
+            return;
+        // Since this is a component removed event, reset our own navmesh pointer
+        navMesh = 0;
+    }
+    SetNavigationMesh(navMesh);
 }
 
 }

+ 5 - 5
Source/Urho3D/Navigation/CrowdManager.h

@@ -166,19 +166,19 @@ protected:
 private:
     /// Handle the scene subsystem update event.
     void HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData);
-    /// Handle full rebuilds of the navigation mesh.
-    void HandleNavMeshFullRebuild(StringHash eventType, VariantMap& eventData);
+    /// Handle navigation mesh changed event. It can be navmesh being rebuilt or being removed from its node.
+    void HandleNavMeshChanged(StringHash eventType, VariantMap& eventData);
 
     /// Internal Detour crowd object.
     dtCrowd* crowd_;
     /// NavigationMesh for which the crowd was created.
-    WeakPtr<NavigationMesh> navigationMesh_;
+    NavigationMesh* navigationMesh_;
+    /// The NavigationMesh component Id for pending crowd creation.
+    unsigned navigationMeshId_;
     /// The maximum number of agents the crowd can manage.
     unsigned maxAgents_;
     /// The maximum radius of any agent that will be added to the crowd.
     float maxAgentRadius_;
-    /// The NavigationMesh component Id for pending crowd creation.
-    unsigned navigationMeshId_;
     /// Number of filter types configured in the crowd. Limit to DT_CROWD_MAX_QUERY_FILTER_TYPE.
     unsigned numFilterTypes_;
     /// Number of configured area in each filter type. Limit to DT_MAX_AREAS.

+ 12 - 1
Source/Urho3D/Scene/Node.cpp

@@ -1086,13 +1086,24 @@ const Variant& Node::GetVar(StringHash key) const
     return i != vars_.End() ? i->second_ : Variant::EMPTY;
 }
 
-Component* Node::GetComponent(StringHash type) const
+Component* Node::GetComponent(StringHash type, bool recursive) const
 {
     for (Vector<SharedPtr<Component> >::ConstIterator i = components_.Begin(); i != components_.End(); ++i)
     {
         if ((*i)->GetType() == type)
             return *i;
     }
+
+    if (recursive)
+    {
+        for (Vector<SharedPtr<Node> >::ConstIterator i = children_.Begin(); i != children_.End(); ++i)
+        {
+            Component* component = (*i)->GetComponent(type, true);
+            if (component)
+                return component;
+        }
+    }
+
     return 0;
 }
 

+ 25 - 8
Source/Urho3D/Scene/Node.h

@@ -475,7 +475,7 @@ public:
     /// Return all components of type. Optionally recursive.
     void GetComponents(PODVector<Component*>& dest, StringHash type, bool recursive = false) const;
     /// Return component by type. If there are several, returns the first.
-    Component* GetComponent(StringHash type) const;
+    Component* GetComponent(StringHash type, bool recursive = false) const;
     /// Return whether has a specific component.
     bool HasComponent(StringHash type) const;
 
@@ -489,13 +489,13 @@ public:
     const VariantMap& GetVars() const { return vars_; }
 
     /// Return first component derived from class.
-    template <class T> T* GetDerivedComponent() const;
+    template <class T> T* GetDerivedComponent(bool recursive = false) const;
     /// Return components derived from class.
-    template <class T> void GetDerivedComponents(PODVector<T*>& dest) const;
+    template <class T> void GetDerivedComponents(PODVector<T*>& dest, bool recursive = false, bool clearVector = true) const;
     /// Template version of returning child nodes with a specific component.
     template <class T> void GetChildrenWithComponent(PODVector<Node*>& dest, bool recursive = false) const;
     /// Template version of returning a component by type.
-    template <class T> T* GetComponent() const;
+    template <class T> T* GetComponent(bool recursive = false) const;
     /// Template version of returning all components of type.
     template <class T> void GetComponents(PODVector<T*>& dest, bool recursive = false) const;
     /// Template version of checking whether has a specific component.
@@ -651,7 +651,7 @@ template <class T> void Node::GetChildrenWithComponent(PODVector<Node*>& dest, b
     GetChildrenWithComponent(dest, T::GetTypeStatic(), recursive);
 }
 
-template <class T> T* Node::GetComponent() const { return static_cast<T*>(GetComponent(T::GetTypeStatic())); }
+template <class T> T* Node::GetComponent(bool recursive) const { return static_cast<T*>(GetComponent(T::GetTypeStatic(), recursive)); }
 
 template <class T> void Node::GetComponents(PODVector<T*>& dest, bool recursive) const
 {
@@ -660,7 +660,7 @@ template <class T> void Node::GetComponents(PODVector<T*>& dest, bool recursive)
 
 template <class T> bool Node::HasComponent() const { return HasComponent(T::GetTypeStatic()); }
 
-template <class T> T* Node::GetDerivedComponent() const
+template <class T> T* Node::GetDerivedComponent(bool recursive) const
 {
     for (Vector<SharedPtr<Component> >::ConstIterator i = components_.Begin(); i != components_.End(); ++i)
     {
@@ -669,12 +669,23 @@ template <class T> T* Node::GetDerivedComponent() const
             return component;
     }
 
+    if (recursive)
+    {
+        for (Vector<SharedPtr<Node> >::ConstIterator i = children_.Begin(); i != children_.End(); ++i)
+        {
+            T* component = (*i)->GetDerivedComponent<T>(true);
+            if (component)
+                return component;
+        }
+    }
+
     return 0;
 }
 
-template <class T> void Node::GetDerivedComponents(PODVector<T*>& dest) const
+template <class T> void Node::GetDerivedComponents(PODVector<T*>& dest, bool recursive, bool clearVector) const
 {
-    dest.Clear();
+    if (clearVector)
+        dest.Clear();
 
     for (Vector<SharedPtr<Component> >::ConstIterator i = components_.Begin(); i != components_.End(); ++i)
     {
@@ -682,6 +693,12 @@ template <class T> void Node::GetDerivedComponents(PODVector<T*>& dest) const
         if (component)
             dest.Push(component);
     }
+
+    if (recursive)
+    {
+        for (Vector<SharedPtr<Node> >::ConstIterator i = children_.Begin(); i != children_.End(); ++i)
+            (*i)->GetDerivedComponents<T>(dest, true, false);
+    }
 }
 
 }

+ 3 - 3
Source/Urho3D/Script/APITemplates.h

@@ -523,9 +523,9 @@ static Component* NodeGetComponent(unsigned index, Node* ptr)
         return components[index];
 }
 
-static Component* NodeGetComponentWithType(const String& typeName, Node* ptr)
+static Component* NodeGetComponentWithType(const String& typeName, bool recursive, Node* ptr)
 {
-    return ptr->GetComponent(typeName);
+    return ptr->GetComponent(typeName, recursive);
 }
 
 static CScriptArray* NodeGetComponents(Node* ptr)
@@ -674,7 +674,7 @@ template <class T> void RegisterNode(asIScriptEngine* engine, const char* classN
     engine->RegisterObjectMethod(className, "Node@+ GetChild(const String&in, bool recursive = false) const", asMETHODPR(T, GetChild, (const String&, bool) const, Node*), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "Array<Component@>@ GetComponents() const", asFUNCTION(NodeGetComponents), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "Array<Component@>@ GetComponents(const String&in, bool recursive = false) const", asFUNCTION(NodeGetComponentsWithType), asCALL_CDECL_OBJLAST);
-    engine->RegisterObjectMethod(className, "Component@+ GetComponent(const String&in) const", asFUNCTION(NodeGetComponentWithType), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectMethod(className, "Component@+ GetComponent(const String&in, bool recursive = false) const", asFUNCTION(NodeGetComponentWithType), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "bool HasComponent(const String&in) const", asFUNCTION(NodeHasComponent), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod(className, "Vector3 LocalToWorld(const Vector3&in) const", asMETHODPR(T, LocalToWorld, (const Vector3&) const, Vector3), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "Vector3 LocalToWorld(const Vector4&in) const", asMETHODPR(T, LocalToWorld, (const Vector4&) const, Vector3), asCALL_THISCALL);

+ 2 - 2
Source/Urho3D/Script/SceneAPI.cpp

@@ -292,8 +292,8 @@ static void RegisterScene(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Scene", "void RegisterVar(const String&in)", asMETHOD(Scene, RegisterVar), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void UnregisterVar(const String&in)", asMETHOD(Scene, UnregisterVar), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void UnregisterAllVars(const String&in)", asMETHOD(Scene, UnregisterAllVars), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Scene", "Component@+ GetComponent(uint)", asMETHODPR(Scene, GetComponent, (unsigned) const, Component*), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Scene", "Node@+ GetNode(uint)", asMETHOD(Scene, GetNode), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Scene", "Component@+ GetComponent(uint) const", asMETHODPR(Scene, GetComponent, (unsigned) const, Component*), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Scene", "Node@+ GetNode(uint) const", asMETHOD(Scene, GetNode), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "const String& GetVarName(StringHash) const", asMETHOD(Scene, GetVarName), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void Update(float)", asMETHOD(Scene, Update), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "void set_updateEnabled(bool)", asMETHOD(Scene, SetUpdateEnabled), asCALL_THISCALL);