Переглянути джерело

Initial work to separate Component's association with its owner node and scene (OnNodeSet() and OnSceneSet()). This should allow components to work properly when moved from one scene to another, or when created initially outside a scene, and be removed from rendering & physics when removed from the scene, even if the node's refcount keeps it alive after the removal.

Lasse Öörni 10 роки тому
батько
коміт
52b739e2a4

+ 5 - 7
Source/Urho3D/Graphics/AnimationController.cpp

@@ -757,14 +757,12 @@ VariantVector AnimationController::GetNodeAnimationStatesAttr() const
     return ret;
 }
 
-void AnimationController::OnNodeSet(Node* node)
+void AnimationController::OnSceneSet(Scene* scene)
 {
-    if (node)
-    {
-        Scene* scene = GetScene();
-        if (scene && IsEnabledEffective())
-            SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(AnimationController, HandleScenePostUpdate));
-    }
+    if (scene && IsEnabledEffective())
+        SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(AnimationController, HandleScenePostUpdate));
+    else if (!scene)
+        UnsubscribeFromEvent(E_SCENEPOSTUPDATE);
 }
 
 AnimationState* AnimationController::AddAnimationState(Animation* animation)

+ 2 - 2
Source/Urho3D/Graphics/AnimationController.h

@@ -174,8 +174,8 @@ public:
     VariantVector GetNodeAnimationStatesAttr() const;
 
 protected:
-    /// Handle node being assigned.
-    virtual void OnNodeSet(Node* node);
+    /// Handle scene being assigned.
+    virtual void OnSceneSet(Scene* scene);
 
 private:
     /// Add an animation state either to AnimatedModel or as a node animation.

+ 6 - 3
Source/Urho3D/Graphics/Drawable.cpp

@@ -359,10 +359,13 @@ void Drawable::LimitVertexLights(bool removeConvertedLights)
 void Drawable::OnNodeSet(Node* node)
 {
     if (node)
-    {
-        AddToOctree();
         node->AddListener(this);
-    }
+}
+
+void Drawable::OnSceneSet(Scene* scene)
+{
+    if (scene)
+        AddToOctree();
     else
         RemoveFromOctree();
 }

+ 2 - 0
Source/Urho3D/Graphics/Drawable.h

@@ -257,6 +257,8 @@ public:
 protected:
     /// Handle node being assigned.
     virtual void OnNodeSet(Node* node);
+    /// Handle scene being assigned.
+    virtual void OnSceneSet(Scene* scene);
     /// Handle node transform being dirtied.
     virtual void OnMarkedDirty(Node* node);
     /// Recalculate the world-space bounding box.

+ 6 - 8
Source/Urho3D/Graphics/ParticleEmitter.cpp

@@ -408,16 +408,14 @@ VariantVector ParticleEmitter::GetParticleBillboardsAttr() const
     return ret;
 }
 
-void ParticleEmitter::OnNodeSet(Node* node)
+void ParticleEmitter::OnSceneSet(Scene* scene)
 {
-    BillboardSet::OnNodeSet(node);
+    BillboardSet::OnSceneSet(scene);
 
-    if (node)
-    {
-        Scene* scene = GetScene();
-        if (scene && IsEnabledEffective())
-            SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(ParticleEmitter, HandleScenePostUpdate));
-    }
+    if (scene && IsEnabledEffective())
+        SubscribeToEvent(scene, E_SCENEPOSTUPDATE, HANDLER(ParticleEmitter, HandleScenePostUpdate));
+    else if (!scene)
+         UnsubscribeFromEvent(E_SCENEPOSTUPDATE);
 }
 
 bool ParticleEmitter::EmitNewParticle()

+ 2 - 2
Source/Urho3D/Graphics/ParticleEmitter.h

@@ -106,8 +106,8 @@ public:
     VariantVector GetParticleBillboardsAttr() const;
 
 protected:
-    /// Handle node being assigned.
-    virtual void OnNodeSet(Node* node);
+    /// Handle scene being assigned.
+    virtual void OnSceneSet(Scene* scene);
 
     /// Create a new particle. Return true if there was room.
     bool EmitNewParticle();

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

@@ -107,17 +107,17 @@ void CrowdAgent::RegisterObject(Context* context)
 void CrowdAgent::OnNodeSet(Node* node)
 {
     if (node)
-    {
-        Scene* scene = GetScene();
-        if (scene)
-        {
-            if (scene == node)
-                LOGERROR(GetTypeName() + " should not be created to the root scene node");
-            crowdManager_ = scene->GetOrCreateComponent<DetourCrowdManager>();
-            AddAgentToCrowd();
-        }
-
         node->AddListener(this);
+}
+
+void CrowdAgent::OnSceneSet(Scene* scene)
+{
+    if (scene)
+    {
+        if (scene == node_)
+            LOGERROR(GetTypeName() + " should not be created to the root scene node");
+        crowdManager_ = scene->GetOrCreateComponent<DetourCrowdManager>();
+        AddAgentToCrowd();
     }
     else
         RemoveAgentFromCrowd();

+ 2 - 0
Source/Urho3D/Navigation/CrowdAgent.h

@@ -133,6 +133,8 @@ protected:
     virtual void OnCrowdAgentReposition(const Vector3& newPos, const Vector3& newVel);
     /// Handle node being assigned.
     virtual void OnNodeSet(Node* node);
+    /// Handle node being assigned.
+    virtual void OnSceneSet(Scene* scene);
     /// \todo Handle node transform being dirtied.
     virtual void OnMarkedDirty(Node* node);
     /// Get internal Detour crowd agent.

+ 14 - 6
Source/Urho3D/Navigation/DetourCrowdManager.cpp

@@ -78,7 +78,7 @@ void DetourCrowdManager::RegisterObject(Context* context)
 
 void DetourCrowdManager::SetNavigationMesh(NavigationMesh* navMesh)
 {
-    navigationMesh_ = WeakPtr<NavigationMesh>(navMesh);
+    navigationMesh_ = navMesh;
     if (navigationMesh_ && !navigationMesh_->navMeshQuery_)
         navigationMesh_->InitializeQuery();
     CreateCrowd();
@@ -514,23 +514,31 @@ void DetourCrowdManager::HandleNavMeshFullRebuild(StringHash eventType, VariantM
     }
 }
 
-void DetourCrowdManager::OnNodeSet(Node* node)
+void DetourCrowdManager::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 (node)
+    if (scene)
     {
-        SubscribeToEvent(node, E_SCENESUBSYSTEMUPDATE, HANDLER(DetourCrowdManager, HandleSceneSubsystemUpdate));
-        SubscribeToEvent(node, E_NAVIGATION_MESH_REBUILT, HANDLER(DetourCrowdManager, HandleNavMeshFullRebuild));
-
+        SubscribeToEvent(scene, E_SCENESUBSYSTEMUPDATE, HANDLER(DetourCrowdManager, HandleSceneSubsystemUpdate));
         NavigationMesh* mesh = GetScene()->GetComponent<NavigationMesh>();
         if (!mesh)
             mesh = GetScene()->GetComponent<DynamicNavigationMesh>();
         if (mesh)
+        {
+            SubscribeToEvent(mesh, E_NAVIGATION_MESH_REBUILT, HANDLER(DetourCrowdManager, HandleNavMeshFullRebuild));
             SetNavigationMesh(mesh);
+        }
         else
             LOGERROR("DetourCrowdManager requires an existing navigation mesh");
     }
+    else
+    {
+        UnsubscribeFromEvent(E_SCENESUBSYSTEMUPDATE);
+        UnsubscribeFromEvent(E_NAVIGATION_MESH_REBUILT);
+        navigationMesh_.Reset();
+    }
+
 }
 
 }

+ 2 - 2
Source/Urho3D/Navigation/DetourCrowdManager.h

@@ -116,8 +116,8 @@ protected:
 protected:
     /// Update the crowd simulation.
     void Update(float delta);
-    /// Handle node being assigned.
-    virtual void OnNodeSet(Node* node);
+    /// Handle scene being assigned.
+    virtual void OnSceneSet(Scene* scene);
     /// Get the detour crowd agent.
     const dtCrowdAgent* GetCrowdAgent(int agent);
     /// Get the internal detour crowd component.

+ 4 - 4
Source/Urho3D/Navigation/Obstacle.cpp

@@ -85,17 +85,17 @@ void Obstacle::SetRadius(float newRadius)
     MarkNetworkUpdate();
 }
 
-void Obstacle::OnNodeSet(Node* node)
+void Obstacle::OnSceneSet(Scene* scene)
 {
-    if (node)
+    if (scene)
     {
-        if (GetScene() == node)
+        if (scene == node_)
         {
             LOGWARNING(GetTypeName() + " should not be created to the root scene node");
             return;
         }
         if (!ownerMesh_)
-            ownerMesh_ = GetScene()->GetComponent<DynamicNavigationMesh>();
+            ownerMesh_ = scene->GetComponent<DynamicNavigationMesh>();
         if (ownerMesh_)
             ownerMesh_->AddObstacle(this);
     }

+ 2 - 2
Source/Urho3D/Navigation/Obstacle.h

@@ -66,8 +66,8 @@ public:
     void DrawDebugGeometry(bool depthTest);
 
 protected:
-    /// Handle node being assigned, identify our DynamicNavigationMesh.
-    virtual void OnNodeSet(Node* node);
+    /// Handle scene being assigned, identify our DynamicNavigationMesh.
+    virtual void OnSceneSet(Scene* scene);
 
 private:
     /// Radius of this obstacle.

+ 19 - 12
Source/Urho3D/Physics/CollisionShape.cpp

@@ -914,18 +914,6 @@ void CollisionShape::OnNodeSet(Node* node)
 {
     if (node)
     {
-        Scene* scene = GetScene();
-        if (scene)
-        {
-            if (scene == node)
-                LOGWARNING(GetTypeName() + " should not be created to the root scene node");
-
-            physicsWorld_ = scene->GetOrCreateComponent<PhysicsWorld>();
-            physicsWorld_->AddCollisionShape(this);
-        }
-        else
-            LOGERROR("Node is detached from scene, can not create collision shape");
-
         node->AddListener(this);
         cachedWorldScale_ = node->GetWorldScale();
 
@@ -934,6 +922,25 @@ void CollisionShape::OnNodeSet(Node* node)
     }
 }
 
+void CollisionShape::OnSceneSet(Scene* scene)
+{
+    if (scene)
+    {
+        if (scene == node_)
+            LOGWARNING(GetTypeName() + " should not be created to the root scene node");
+
+        physicsWorld_ = scene->GetOrCreateComponent<PhysicsWorld>();
+        physicsWorld_->AddCollisionShape(this);
+    }
+    else
+    {
+        ReleaseShape();
+
+        if (physicsWorld_)
+            physicsWorld_->RemoveCollisionShape(this);
+    }
+}
+
 void CollisionShape::OnMarkedDirty(Node* node)
 {
     Vector3 newWorldScale = node_->GetWorldScale();

+ 2 - 0
Source/Urho3D/Physics/CollisionShape.h

@@ -221,6 +221,8 @@ public:
 protected:
     /// Handle node being assigned.
     virtual void OnNodeSet(Node* node);
+    /// Handle scene being assigned.
+    virtual void OnSceneSet(Scene* scene);
     /// Handle node transform being dirtied.
     virtual void OnMarkedDirty(Node* node);
 

+ 19 - 12
Source/Urho3D/Physics/Constraint.cpp

@@ -441,23 +441,30 @@ void Constraint::OnNodeSet(Node* node)
 {
     if (node)
     {
-        Scene* scene = GetScene();
-        if (scene)
-        {
-            if (scene == node)
-                LOGWARNING(GetTypeName() + " should not be created to the root scene node");
-
-            physicsWorld_ = scene->GetOrCreateComponent<PhysicsWorld>();
-            physicsWorld_->AddConstraint(this);
-        }
-        else
-            LOGERROR("Node is detached from scene, can not create constraint");
-
         node->AddListener(this);
         cachedWorldScale_ = node->GetWorldScale();
     }
 }
 
+void Constraint::OnSceneSet(Scene* scene)
+{
+    if (scene)
+    {
+        if (scene == node_)
+            LOGWARNING(GetTypeName() + " should not be created to the root scene node");
+
+        physicsWorld_ = scene->GetOrCreateComponent<PhysicsWorld>();
+        physicsWorld_->AddConstraint(this);
+    }
+    else
+    {
+        ReleaseConstraint();
+
+        if (physicsWorld_)
+            physicsWorld_->RemoveConstraint(this);
+    }
+}
+
 void Constraint::OnMarkedDirty(Node* node)
 {
     /// \todo This does not catch the connected body node's scale changing

+ 2 - 0
Source/Urho3D/Physics/Constraint.h

@@ -136,6 +136,8 @@ public:
 protected:
     /// Handle node being assigned.
     virtual void OnNodeSet(Node* node);
+    /// Handle scene being assigned.
+    virtual void OnSceneSet(Scene* scene);
     /// Handle node transform being dirtied.
     virtual void OnMarkedDirty(Node* node);
     

+ 5 - 3
Source/Urho3D/Physics/PhysicsWorld.cpp

@@ -653,14 +653,16 @@ void PhysicsWorld::CleanupGeometryCache()
     }
 }
 
-void PhysicsWorld::OnNodeSet(Node* node)
+void PhysicsWorld::OnSceneSet(Scene* scene)
 {
     // Subscribe to the scene subsystem update, which will trigger the physics simulation step
-    if (node)
+    if (scene)
     {
         scene_ = GetScene();
-        SubscribeToEvent(node, E_SCENESUBSYSTEMUPDATE, HANDLER(PhysicsWorld, HandleSceneSubsystemUpdate));
+        SubscribeToEvent(scene_, E_SCENESUBSYSTEMUPDATE, HANDLER(PhysicsWorld, HandleSceneSubsystemUpdate));
     }
+    else
+        UnsubscribeFromEvent(E_SCENESUBSYSTEMUPDATE);
 }
 
 void PhysicsWorld::HandleSceneSubsystemUpdate(StringHash eventType, VariantMap& eventData)

+ 2 - 2
Source/Urho3D/Physics/PhysicsWorld.h

@@ -217,8 +217,8 @@ public:
     bool IsApplyingTransforms() const { return applyingTransforms_; }
 
 protected:
-    /// Handle node being assigned.
-    virtual void OnNodeSet(Node* node);
+    /// Handle scene being assigned.
+    virtual void OnSceneSet(Scene* scene);
 
 private:
     /// Handle the scene subsystem update event, step simulation here.

+ 17 - 12
Source/Urho3D/Physics/RigidBody.cpp

@@ -909,22 +909,27 @@ void RigidBody::OnMarkedDirty(Node* node)
 void RigidBody::OnNodeSet(Node* node)
 {
     if (node)
+        node->AddListener(this);
+}
+
+void RigidBody::OnSceneSet(Scene* scene)
+{
+    if (scene)
     {
-        Scene* scene = GetScene();
-        if (scene)
-        {
-            if (scene == node)
-                LOGWARNING(GetTypeName() + " should not be created to the root scene node");
+        if (scene == node_)
+            LOGWARNING(GetTypeName() + " should not be created to the root scene node");
 
-            physicsWorld_ = scene->GetOrCreateComponent<PhysicsWorld>();
-            physicsWorld_->AddRigidBody(this);
+        physicsWorld_ = scene->GetOrCreateComponent<PhysicsWorld>();
+        physicsWorld_->AddRigidBody(this);
 
-            AddBodyToWorld();
-        }
-        else
-            LOGERROR("Node is detached from scene, can not create rigid body");
+        AddBodyToWorld();
+    }
+    else
+    {
+        ReleaseBody();
 
-        node->AddListener(this);
+        if (physicsWorld_)
+            physicsWorld_->RemoveRigidBody(this);
     }
 }
 

+ 2 - 0
Source/Urho3D/Physics/RigidBody.h

@@ -234,6 +234,8 @@ public:
 protected:
     /// Handle node being assigned.
     virtual void OnNodeSet(Node* node);
+    /// Handle scene being assigned.
+    virtual void OnSceneSet(Scene* scene);
     /// Handle node transform being dirtied.
     virtual void OnMarkedDirty(Node* node);
 

+ 4 - 0
Source/Urho3D/Scene/Component.cpp

@@ -216,6 +216,10 @@ void Component::OnNodeSet(Node* node)
 {
 }
 
+void Component::OnSceneSet(Scene* scene)
+{
+}
+
 void Component::OnMarkedDirty(Node* node)
 {
 }

+ 2 - 0
Source/Urho3D/Scene/Component.h

@@ -99,6 +99,8 @@ protected:
     virtual void OnAttributeAnimationRemoved();
     /// Handle scene node being assigned at creation.
     virtual void OnNodeSet(Node* node);
+    /// Handle scene being assigned. This may happen several times during the component's lifetime. Scene-wide subsystems and events are subscribed to here.
+    virtual void OnSceneSet(Scene* scene);
     /// Handle scene node transform dirtied.
     virtual void OnMarkedDirty(Node* node);
     /// Handle scene node enabled status changing.

+ 15 - 7
Source/Urho3D/Scene/LogicComponent.cpp

@@ -78,9 +78,7 @@ void LogicComponent::OnNodeSet(Node* node)
 {
     if (node)
     {
-        // We have been attached to a node. Set initial update event subscription state
-        UpdateEventSubscription();
-        // Then execute the user-defined start function
+        // Execute the user-defined start function
         Start();
     }
     else
@@ -90,12 +88,22 @@ void LogicComponent::OnNodeSet(Node* node)
     }
 }
 
+void LogicComponent::OnSceneSet(Scene* scene)
+{
+    if (scene)
+        UpdateEventSubscription();
+    else
+    {
+        UnsubscribeFromEvent(E_SCENEUPDATE);
+        UnsubscribeFromEvent(E_SCENEPOSTUPDATE);
+        UnsubscribeFromEvent(E_PHYSICSPRESTEP);
+        UnsubscribeFromEvent(E_PHYSICSPOSTSTEP);
+        currentEventMask_ = 0;
+    }
+}
+
 void LogicComponent::UpdateEventSubscription()
 {
-    // If scene node is not assigned yet, no need to update subscription
-    if (!node_)
-        return;
-    
     Scene* scene = GetScene();
     if (!scene)
     {

+ 2 - 0
Source/Urho3D/Scene/LogicComponent.h

@@ -74,6 +74,8 @@ class URHO3D_API LogicComponent : public Component
 protected:
     /// Handle scene node being assigned at creation.
     virtual void OnNodeSet(Node* node);
+    /// Handle scene being assigned.
+    virtual void OnSceneSet(Scene* scene);
     
 private:
     /// Subscribe/unsubscribe to update events based on current enabled state and update event mask.

+ 5 - 5
Source/Urho3D/Scene/Node.cpp

@@ -1419,6 +1419,11 @@ void Node::AddComponent(Component* component, unsigned id, CreateMode mode)
 
     components_.Push(SharedPtr<Component>(component));
 
+    if (component->GetNode())
+        LOGWARNING("Component " + component->GetTypeName() + " already belongs to a node!");
+
+    component->SetNode(this);
+
     // If zero ID specified, or the ID is already taken, let the scene assign
     if (scene_)
     {
@@ -1430,10 +1435,6 @@ void Node::AddComponent(Component* component, unsigned id, CreateMode mode)
     else
         component->SetID(id);
 
-    if(component->GetNode())
-        LOGWARNING("Component " + component->GetTypeName() + " already belongs to a node!");
-
-    component->SetNode(this);
     component->OnMarkedDirty(this);
 
     // Check attributes of the new component on next network update, and mark node dirty in all replication states
@@ -1698,7 +1699,6 @@ void Node::RemoveChild(Vector<SharedPtr<Node> >::Iterator i)
     child->parent_ = 0;
     child->MarkDirty();
     child->MarkNetworkUpdate();
-    // Remove the child from the scene already at this point, in case it is not destroyed immediately
     if (scene_)
         scene_->NodeRemoved(child);
 

+ 3 - 0
Source/Urho3D/Scene/Scene.cpp

@@ -844,6 +844,8 @@ void Scene::ComponentAdded(Component* component)
 
         localComponents_[id] = component;
     }
+
+    component->OnSceneSet(this);
 }
 
 void Scene::ComponentRemoved(Component* component)
@@ -858,6 +860,7 @@ void Scene::ComponentRemoved(Component* component)
         localComponents_.Erase(id);
 
     component->SetID(0);
+    component->OnSceneSet(0);
 }
 
 void Scene::SetVarNamesAttr(const String& value)