Browse Source

Added off-mesh connections to sample 39

Mike3D 10 years ago
parent
commit
7f3a6ef86b

+ 35 - 3
Source/Samples/39_CrowdNavigation/CrowdNavigation.cpp

@@ -42,6 +42,7 @@
 #include <Urho3D/Navigation/NavigationEvents.h>
 #include <Urho3D/Navigation/Obstacle.h>
 #include <Urho3D/Graphics/Octree.h>
+#include <Urho3D/Navigation/OffMeshConnection.h>
 #include <Urho3D/Graphics/Renderer.h>
 #include <Urho3D/Resource/ResourceCache.h>
 #include <Urho3D/Scene/Scene.h>
@@ -119,6 +120,7 @@ void CrowdNavigation::CreateScene()
     light->SetShadowCascade(CascadeParameters(10.0f, 50.0f, 200.0f, 0.0f, 0.8f));
 
     // Create randomly sized boxes. If boxes are big enough, make them occluders
+    Vector< SharedPtr<Node> > boxes;
     for (unsigned i = 0; i < 20; ++i)
     {
         Node* boxNode = scene_->CreateChild("Box");
@@ -130,19 +132,23 @@ void CrowdNavigation::CreateScene()
         boxObject->SetMaterial(cache->GetResource<Material>("Materials/Stone.xml"));
         boxObject->SetCastShadows(true);
         if (size >= 3.0f)
+        {
             boxObject->SetOccluder(true);
+            boxes.Push(SharedPtr<Node>(boxNode));
+        }
     }
 
     // Create a DynamicNavigationMesh component to the scene root
     DynamicNavigationMesh* navMesh = scene_->CreateComponent<DynamicNavigationMesh>();
-    // Enable drawing debug geometry for obstacles
+    // Enable drawing debug geometry for obstacles and off-mesh connections
     navMesh->SetDrawObstacles(true);
+    navMesh->SetDrawOffMeshConnections(true);
     // Set the agent height large enough to exclude the layers under boxes
     navMesh->SetAgentHeight(10.0f);
     // Set nav mesh tilesize to something reasonable
     navMesh->SetTileSize(64);
     // Set nav mesh cell height to minimum (allows agents to be grounded)
-    navMesh->SetCellHeight(0.04f);
+    navMesh->SetCellHeight(0.05f);
     // Create a Navigable component to the scene root. This tags all of the geometry in the scene as being part of the
     // navigation mesh. By default this is recursive, but the recursion could be turned off from Navigable
     scene_->CreateComponent<Navigable>();
@@ -154,6 +160,11 @@ void CrowdNavigation::CreateScene()
     // it will use renderable geometry instead
     navMesh->Build();
 
+    // Create an off-mesh connection to each box to make them climbable (tiny boxes are skipped). A connection is built from 2 nodes.
+    // Note that OffMeshConnections must be added before building the navMesh, but as we are adding Obstacles next, tiles will be automatically rebuilt.
+    // Creating connections post-build here allows us to use FindNearestPoint() to procedurally set accurate positions for the connection
+    CreateBoxOffMeshConnections(navMesh, boxes);
+
     // Create a DetourCrowdManager component to the scene root
     crowdManager_ = scene_->CreateComponent<DetourCrowdManager>();
 
@@ -269,6 +280,26 @@ Node* CrowdNavigation::CreateMushroom(const Vector3& pos)
     return mushroomNode;
 }
 
+void CrowdNavigation::CreateBoxOffMeshConnections(DynamicNavigationMesh* navMesh, Vector< SharedPtr<Node> > boxes)
+{
+    for (unsigned i=0; i < boxes.Size(); ++i)
+    {
+        Node* box = boxes[i];
+        Vector3 boxPos = box->GetPosition();
+        float boxHalfSize = box->GetScale().x_ / 2;
+
+        // Create 2 empty nodes for the start & end points of the connection. Note that order matters only when using one-way/unidirectional connection.
+        Node* connectionStart = scene_->CreateChild("ConnectionStart");
+        connectionStart->SetPosition(navMesh->FindNearestPoint(boxPos + Vector3(boxHalfSize, -boxHalfSize, 0))); // Base of box
+        Node* connectionEnd = connectionStart->CreateChild("ConnectionEnd");
+        connectionEnd->SetWorldPosition(navMesh->FindNearestPoint(boxPos + Vector3(boxHalfSize, boxHalfSize, 0))); // Top of box
+
+        // Create the OffMeshConnection component to one node and link the other node
+        OffMeshConnection* connection = connectionStart->CreateComponent<OffMeshConnection>();
+        connection->SetEndPoint(connectionEnd);
+    }
+}
+
 void CrowdNavigation::SetPathPoint()
 {
     Vector3 hitPos;
@@ -415,9 +446,10 @@ void CrowdNavigation::MoveCamera(float timeStep)
         crowdManager_ = scene_->GetComponent<DetourCrowdManager>();
         agents_ = crowdManager_->GetActiveAgents();
 
-        // Re-enable debug draw for obstacles
+        // Re-enable debug draw for obstacles & off-mesh connections
         DynamicNavigationMesh* navMesh = scene_->GetComponent<DynamicNavigationMesh>();
         navMesh->SetDrawObstacles(true);
+        navMesh->SetDrawOffMeshConnections(true);
     }
 
     // Toggle debug geometry with space

+ 3 - 0
Source/Samples/39_CrowdNavigation/CrowdNavigation.h

@@ -42,6 +42,7 @@ class Scene;
 ///     - Raycasting drawable components
 ///     - Crowd movement management
 ///     - Accessing crowd agents with the crowd manager
+///     - Using off-mesh connections to make boxes climbable
 class CrowdNavigation : public Sample
 {
     OBJECT(CrowdNavigation);
@@ -145,6 +146,8 @@ private:
     void SpawnJack(const Vector3& pos);
     /// Create a mushroom object at position.
     Node* CreateMushroom(const Vector3& pos);
+    /// Create an off-mesh connection for each box to make it climbable.
+    void CreateBoxOffMeshConnections(DynamicNavigationMesh* navMesh, Vector< SharedPtr<Node> > boxes);
     /// Utility function to raycast to the cursor position. Return true if hit.
     bool Raycast(float maxDistance, Vector3& hitPos, Drawable*& hitDrawable);
     /// Handle the logic update event.

+ 30 - 6
bin/Data/LuaScripts/39_CrowdNavigation.lua

@@ -7,6 +7,7 @@
 --     - Raycasting drawable components
 --     - Crowd movement management
 --     - Accessing crowd agents with the crowd manager
+--     - Using off-mesh connections to make boxes climbable
 
 require "LuaScripts/Utilities/Sample"
 
@@ -65,6 +66,7 @@ function CreateScene()
 
     -- Create randomly sized boxes. If boxes are big enough, make them occluders. Occluders will be software rasterized before
     -- rendering to a low-resolution depth-only buffer to test the objects in the view frustum for visibility
+    local boxes = {}
     for i = 1, 20 do
         local boxNode = scene_:CreateChild("Box")
         local size = 1.0 + Random(10.0)
@@ -76,19 +78,21 @@ function CreateScene()
         boxObject.castShadows = true
         if size >= 3.0 then
             boxObject.occluder = true
+            table.insert(boxes, boxNode)
         end
     end
 
     -- Create a DynamicNavigationMesh component to the scene root
     local navMesh = scene_:CreateComponent("DynamicNavigationMesh")
-    -- Enable drawing debug geometry for obstacles
+    -- Enable drawing debug geometry for obstacles and off-mesh connections
     navMesh.drawObstacles = true
+    navMesh.drawOffMeshConnections = true
     -- Set the agent height large enough to exclude the layers under boxes
     navMesh.agentHeight = 10
     -- Set nav mesh tilesize to something reasonable
     navMesh.tileSize = 64
     -- Set nav mesh cell height to minimum (allows agents to be grounded)
-    navMesh.cellHeight = 0.04
+    navMesh.cellHeight = 0.05
     -- Create a Navigable component to the scene root. This tags all of the geometry in the scene as being part of the
     -- navigation mesh. By default this is recursive, but the recursion could be turned off from Navigable
     scene_:CreateComponent("Navigable")
@@ -100,6 +104,11 @@ function CreateScene()
     -- it will use renderable geometry instead
     navMesh:Build()
 
+    -- Create an off-mesh connection for each box to make it climbable (tiny boxes are skipped).
+    -- Note that OffMeshConnections must be added before building the navMesh, but as we are adding Obstacles next, tiles will be automatically rebuilt.
+    -- Creating connections post-build here allows us to use FindNearestPoint() to procedurally set accurate positions for the connection
+    CreateBoxOffMeshConnections(navMesh, boxes)
+
     -- Create a DetourCrowdManager component to the scene root
     crowdManager = scene_:CreateComponent("DetourCrowdManager")
 
@@ -198,6 +207,23 @@ function CreateMushroom(pos)
     return mushroomNode
 end
 
+function CreateBoxOffMeshConnections(navMesh, boxes)
+    for i, box in ipairs(boxes) do
+        local boxPos = box.position
+        local boxHalfSize = box.scale.x / 2
+
+        -- Create 2 empty nodes for the start & end points of the connection. Note that order matters only when using one-way/unidirectional connection.
+        local connectionStart = scene_:CreateChild("ConnectionStart")
+        connectionStart.position = navMesh:FindNearestPoint(boxPos + Vector3(boxHalfSize, -boxHalfSize, 0)) -- Base of box
+        local connectionEnd = connectionStart:CreateChild("ConnectionEnd")
+        connectionEnd.worldPosition = navMesh:FindNearestPoint(boxPos + Vector3(boxHalfSize, boxHalfSize, 0)) -- Top of box
+
+        -- Create the OffMeshConnection component to one node and link the other node
+        local connection = connectionStart:CreateComponent("OffMeshConnection")
+        connection.endPoint = connectionEnd
+    end
+end
+
 function SetPathPoint()
     local hitPos, hitDrawable = Raycast(250.0)
     local navMesh = scene_:GetComponent("DynamicNavigationMesh")
@@ -244,9 +270,6 @@ function AddOrRemoveObject()
 end
 
 function Raycast(maxDistance)
-    local hitPos = nil
-    local hitDrawable = nil
-
     local pos = ui.cursorPosition
     -- Check the cursor is visible and there is no UI element in front of the cursor
     if (not ui.cursor.visible) or (ui:GetElementAt(pos, true) ~= nil) then
@@ -329,9 +352,10 @@ function MoveCamera(timeStep)
         crowdManager = scene_:GetComponent("DetourCrowdManager")
         agents = crowdManager:GetActiveAgents()
 
-        -- Re-enable debug draw for obstacles
+        -- Re-enable debug draw for obstacles & off-mesh connections
         local navMesh = scene_:GetComponent("DynamicNavigationMesh")
         navMesh.drawObstacles = true
+        navMesh.drawOffMeshConnections = true
     end
 end
 

+ 35 - 3
bin/Data/Scripts/39_CrowdNavigation.as

@@ -7,6 +7,7 @@
 //     - Raycasting drawable components
 //     - Crowd movement management
 //     - Accessing crowd agents with the crowd manager
+//     - Using off-mesh connections to make boxes climbable
 
 #include "Scripts/Utilities/Sample.as"
 
@@ -67,6 +68,7 @@ void CreateScene()
 
     // Create randomly sized boxes. If boxes are big enough, make them occluders. Occluders will be software rasterized before
     // rendering to a low-resolution depth-only buffer to test the objects in the view frustum for visibility
+    Array<Node@> boxes;
     for (uint i = 0; i < 20; ++i)
     {
         Node@ boxNode = scene_.CreateChild("Box");
@@ -78,19 +80,23 @@ void CreateScene()
         boxObject.material = cache.GetResource("Material", "Materials/Stone.xml");
         boxObject.castShadows = true;
         if (size >= 3.0f)
+        {
             boxObject.occluder = true;
+            boxes.Push(boxNode);
+        }
     }
 
     // Create a DynamicNavigationMesh component to the scene root
     DynamicNavigationMesh@ navMesh = scene_.CreateComponent("DynamicNavigationMesh");
-    // Enable drawing debug geometry for obstacles
+    // Enable drawing debug geometry for obstacles and off-mesh connections
     navMesh.drawObstacles = true;
+    navMesh.drawOffMeshConnections = true;
     // Set the agent height large enough to exclude the layers under boxes
     navMesh.agentHeight = 10;
     // Set nav mesh tilesize to something reasonable
     navMesh.tileSize = 64;
     // Set nav mesh cell height to minimum (allows agents to be grounded)
-    navMesh.cellHeight = 0.04f;
+    navMesh.cellHeight = 0.05f;
     // Create a Navigable component to the scene root. This tags all of the geometry in the scene as being part of the
     // navigation mesh. By default this is recursive, but the recursion could be turned off from Navigable
     scene_.CreateComponent("Navigable");
@@ -102,6 +108,11 @@ void CreateScene()
     // it will use renderable geometry instead
     navMesh.Build();
 
+    // Create an off-mesh connection to each box to make it climbable (tiny boxes are skipped). A connection is built from 2 nodes.
+    // Note that OffMeshConnections must be added before building the navMesh, but as we are adding Obstacles next, tiles will be automatically rebuilt.
+    // Creating connections post-build here allows us to use FindNearestPoint() to procedurally set accurate positions for the connection
+    CreateBoxOffMeshConnections(navMesh, boxes);
+
     // Create a DetourCrowdManager component to the scene root
     crowdManager = scene_.CreateComponent("DetourCrowdManager");
 
@@ -207,6 +218,26 @@ Node@ SpawnJack(const Vector3& pos)
     return jackNode;
 }
 
+void CreateBoxOffMeshConnections(DynamicNavigationMesh@ navMesh, Array<Node@> boxes)
+{
+    for (uint i=0; i < boxes.length; ++i)
+    {
+        Node@ box = boxes[i];
+        Vector3 boxPos = box.position;
+        float boxHalfSize = box.scale.x / 2;
+
+        // Create 2 empty nodes for the start & end points of the connection. Note that order matters only when using one-way/unidirectional connection.
+        Node@ connectionStart = scene_.CreateChild("ConnectionStart");
+        connectionStart.position = navMesh.FindNearestPoint(boxPos + Vector3(boxHalfSize, -boxHalfSize, 0)); // Base of box
+        Node@ connectionEnd = connectionStart.CreateChild("ConnectionEnd");
+        connectionEnd.worldPosition = navMesh.FindNearestPoint(boxPos + Vector3(boxHalfSize, boxHalfSize, 0)); // Top of box
+
+        // Create the OffMeshConnection component to one node and link the other node
+        OffMeshConnection@ connection = connectionStart.CreateComponent("OffMeshConnection");
+        connection.endPoint = connectionEnd;
+    }
+}
+
 void SetPathPoint()
 {
     Vector3 hitPos;
@@ -365,9 +396,10 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
         // After reload, reacquire crowd manager
         crowdManager = scene_.GetComponent("DetourCrowdManager");
 
-        // Re-enable debug draw for obstacles
+        // Re-enable debug draw for obstacles & off-mesh connections
         DynamicNavigationMesh@ navMesh = scene_.GetComponent("DynamicNavigationMesh");
         navMesh.drawObstacles = true;
+        navMesh.drawOffMeshConnections = true;
     }
 }