Browse Source

Added code to collect navigation mesh geometry.
Added Recast/Detour build parameters to NavigationMesh.
Register navigation components to script.

Lasse Öörni 12 years ago
parent
commit
35c2d83370

+ 12 - 0
Bin/Data/Scripts/Editor/EditorScene.as

@@ -701,6 +701,18 @@ bool SceneSelectAll()
     return true;
 }
 
+bool SceneRebuildNavigation()
+{
+    Array<Component@>@ navMeshes = editorScene.GetComponents("NavigationMesh");
+    for (uint i = 0; i < navMeshes.length; ++i)
+    {
+        NavigationMesh@ navMesh = navMeshes[i];
+        navMesh.Build();
+    }
+    
+    return true;
+}
+
 bool SceneUndo()
 {
     if (undoStackPos > 0)

+ 2 - 0
Bin/Data/Scripts/Editor/EditorUI.as

@@ -175,6 +175,8 @@ void CreateMenuBar()
         popup.AddChild(CreateMenuItem("Unparent", @SceneUnparent, 'U', QUAL_CTRL));
         popup.AddChild(CreateMenuDivider());
         popup.AddChild(CreateMenuItem("Toggle update", @ToggleUpdate, 'P', QUAL_CTRL));
+        popup.AddChild(CreateMenuDivider());
+        popup.AddChild(CreateMenuItem("Rebuild navigation data", @SceneRebuildNavigation));
         FinalizedPopupMenu(popup);
         uiMenuBar.AddChild(menu);
     }

+ 1 - 0
Engine/Engine/Engine.cpp

@@ -269,6 +269,7 @@ bool Engine::InitializeScripting()
         RegisterUIAPI(engine);
         RegisterNetworkAPI(engine);
         RegisterPhysicsAPI(engine);
+        RegisterNavigationAPI(engine);
         RegisterScriptAPI(engine);
         RegisterEngineAPI(engine);
     }

+ 75 - 0
Engine/Engine/NavigationAPI.cpp

@@ -0,0 +1,75 @@
+//
+// Copyright (c) 2008-2013 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "Precompiled.h"
+#include "APITemplates.h"
+#include "Navigable.h"
+#include "NavigationMesh.h"
+
+namespace Urho3D
+{
+
+void RegisterNavigable(asIScriptEngine* engine)
+{
+    RegisterComponent<Navigable>(engine, "Navigable");
+    engine->RegisterObjectMethod("Navigable", "void set_flags(uint)", asMETHOD(Navigable, SetFlags), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Navigable", "uint get_flags() const", asMETHOD(Navigable, GetFlags), asCALL_THISCALL);
+}
+
+void RegisterNavigationMesh(asIScriptEngine* engine)
+{
+    RegisterComponent<NavigationMesh>(engine, "NavigationMesh");
+    engine->RegisterObjectMethod("NavigationMesh", "void Build()", asMETHOD(NavigationMesh, Build), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "void set_cellSize(float)", asMETHOD(NavigationMesh, SetCellSize), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "float get_cellSize() const", asMETHOD(NavigationMesh, GetCellSize), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "void set_cellHeight(float)", asMETHOD(NavigationMesh, SetCellHeight), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "float get_cellHeight() const", asMETHOD(NavigationMesh, GetCellHeight), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "void set_agentHeight(float)", asMETHOD(NavigationMesh, SetAgentHeight), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "float get_agentHeight() const", asMETHOD(NavigationMesh, GetAgentHeight), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "void set_agentRadius(float)", asMETHOD(NavigationMesh, SetAgentRadius), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "float get_agentRadius() const", asMETHOD(NavigationMesh, GetAgentRadius), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "void set_agentMaxClimb(float)", asMETHOD(NavigationMesh, SetAgentMaxClimb), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "float get_agentMaxClimb() const", asMETHOD(NavigationMesh, GetAgentMaxClimb), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "void set_agentMaxSlope(float)", asMETHOD(NavigationMesh, SetAgentMaxSlope), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "float get_agentMaxSlope() const", asMETHOD(NavigationMesh, GetAgentMaxSlope), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "void set_regionMinSize(float)", asMETHOD(NavigationMesh, SetRegionMinSize), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "float get_regionMinSize() const", asMETHOD(NavigationMesh, GetRegionMinSize), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "void set_regionMergeSize(float)", asMETHOD(NavigationMesh, SetRegionMergeSize), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "float get_regionMergeSize() const", asMETHOD(NavigationMesh, GetRegionMergeSize), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "void set_edgeMaxLength(float)", asMETHOD(NavigationMesh, SetEdgeMaxLength), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "float get_edgeMaxLength() const", asMETHOD(NavigationMesh, GetEdgeMaxLength), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "void set_edgeMaxError(float)", asMETHOD(NavigationMesh, SetEdgeMaxError), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "float get_edgeMaxError() const", asMETHOD(NavigationMesh, GetEdgeMaxError), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "void set_detailSampleDistance(float)", asMETHOD(NavigationMesh, SetDetailSampleDistance), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "float get_detailSampleDistance() const", asMETHOD(NavigationMesh, GetDetailSampleDistance), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "void set_detailSampleMaxError(float)", asMETHOD(NavigationMesh, SetDetailSampleMaxError), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "float get_detailSampleMaxError() const", asMETHOD(NavigationMesh, GetDetailSampleMaxError), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "const BoundingBox& get_worldBoundingBox()", asMETHOD(NavigationMesh, GetWorldBoundingBox), asCALL_THISCALL);
+}
+
+void RegisterNavigationAPI(asIScriptEngine* engine)
+{
+    RegisterNavigable(engine);
+    RegisterNavigationMesh(engine);
+}
+
+}

+ 2 - 0
Engine/Engine/ScriptAPI.h

@@ -51,6 +51,8 @@ void RegisterUIAPI(asIScriptEngine* engine);
 void RegisterNetworkAPI(asIScriptEngine* engine);
 /// Register the Physics library to script.
 void RegisterPhysicsAPI(asIScriptEngine* engine);
+/// Register the Navigation library to script.
+void RegisterNavigationAPI(asIScriptEngine* engine);
 /// Register the Script library to script.
 void RegisterScriptAPI(asIScriptEngine* engine);
 /// Register the Engine library to script.

+ 1 - 1
Engine/Navigation/CMakeLists.txt

@@ -7,7 +7,7 @@ file (GLOB H_FILES *.h)
 set (SOURCE_FILES ${CPP_FILES} ${H_FILES})
 
 # Define dependency libs
-set (LIBS ../Container ../Core ../Graphics ../IO ../Math ../Resource ../Scene ../../ThirdParty/Recast/Include ../../ThirdParty/Detour/Include)
+set (LIBS ../Container ../Core ../Graphics ../IO ../Math ../Physics ../Resource ../Scene ../../ThirdParty/Recast/Include ../../ThirdParty/Detour/Include)
 
 # Setup target
 enable_pch ()

+ 58 - 0
Engine/Navigation/Navigable.cpp

@@ -0,0 +1,58 @@
+//
+// Copyright (c) 2008-2013 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "Precompiled.h"
+#include "Context.h"
+#include "Navigable.h"
+
+#include "DebugNew.h"
+
+namespace Urho3D
+{
+
+static const unsigned DEFAULT_FLAGS = 1;
+
+OBJECTTYPESTATIC(Navigable);
+
+Navigable::Navigable(Context* context) :
+    Component(context),
+    flags_(DEFAULT_FLAGS)
+{
+}
+
+Navigable::~Navigable()
+{
+}
+
+void Navigable::RegisterObject(Context* context)
+{
+    context->RegisterFactory<Navigable>();
+    
+    ATTRIBUTE(Navigable, VAR_INT, "Navigation Flags", flags_, DEFAULT_FLAGS, AM_DEFAULT);
+}
+
+void Navigable::SetFlags(unsigned flags)
+{
+    flags_ = flags;
+}
+
+}

+ 54 - 0
Engine/Navigation/Navigable.h

@@ -0,0 +1,54 @@
+//
+// Copyright (c) 2008-2013 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include "Component.h"
+
+namespace Urho3D
+{
+
+/// Navigable geometry properties component. Any geometry components from this node and child nodes will be included in the navigation mesh. Physics geometry is preferred to drawable geometry, if available.
+class Navigable : public Component
+{
+    OBJECT(Navigable);
+    
+public:
+    /// Construct.
+    Navigable(Context* context);
+    /// Destruct.
+    virtual ~Navigable();
+    /// Register object factory.
+    static void RegisterObject(Context* context);
+    
+    /// Set navigation geometry flags, meaning is user-defined.
+    void SetFlags(unsigned flags);
+    
+    /// Return navigation geometry flags.
+    unsigned GetFlags() const { return flags_; }
+    
+private:
+    /// Navigation geometry flags.
+    unsigned flags_;
+};
+
+}

+ 2 - 0
Engine/Navigation/Navigation.cpp

@@ -21,6 +21,7 @@
 //
 
 #include "Precompiled.h"
+#include "Navigable.h"
 #include "NavigationMesh.h"
 
 #include "DebugNew.h"
@@ -30,6 +31,7 @@ namespace Urho3D
 
 void RegisterNavigationLibrary(Context* context)
 {
+    Navigable::RegisterObject(context);
     NavigationMesh::RegisterObject(context);
 }
 

+ 221 - 1
Engine/Navigation/NavigationMesh.cpp

@@ -21,18 +21,51 @@
 //
 
 #include "Precompiled.h"
+#include "CollisionShape.h"
 #include "Context.h"
+#include "Drawable.h"
+#include "Geometry.h"
+#include "Log.h"
+#include "Navigable.h"
 #include "NavigationMesh.h"
+#include "Scene.h"
+#include "StaticModel.h"
+#include "TerrainPatch.h"
 
 #include "DebugNew.h"
 
 namespace Urho3D
 {
 
+static const float DEFAULT_CELL_SIZE = 0.3f;
+static const float DEFAULT_CELL_HEIGHT = 0.2f;
+static const float DEFAULT_AGENT_HEIGHT = 2.0f;
+static const float DEFAULT_AGENT_RADIUS = 0.6f;
+static const float DEFAULT_AGENT_MAX_CLIMB = 0.9f;
+static const float DEFAULT_AGENT_MAX_SLOPE = 45.0f;
+static const float DEFAULT_REGION_MIN_SIZE = 8.0f;
+static const float DEFAULT_REGION_MERGE_SIZE = 20.0f;
+static const float DEFAULT_EDGE_MAX_LENGTH = 12.0f;
+static const float DEFAULT_EDGE_MAX_ERROR = 1.3f;
+static const float DEFAULT_DETAIL_SAMPLE_DISTANCE = 6.0f;
+static const float DEFAULT_DETAIL_SAMPLE_MAX_ERROR = 1.0f;
+
 OBJECTTYPESTATIC(NavigationMesh);
 
 NavigationMesh::NavigationMesh(Context* context) :
-    Component(context)
+    Component(context),
+    cellSize_(DEFAULT_CELL_SIZE),
+    cellHeight_(DEFAULT_CELL_HEIGHT),
+    agentHeight_(DEFAULT_AGENT_HEIGHT),
+    agentRadius_(DEFAULT_AGENT_RADIUS),
+    agentMaxClimb_(DEFAULT_AGENT_MAX_CLIMB),
+    agentMaxSlope_(DEFAULT_AGENT_MAX_SLOPE),
+    regionMinSize_(DEFAULT_REGION_MIN_SIZE),
+    regionMergeSize_(DEFAULT_REGION_MERGE_SIZE),
+    edgeMaxLength_(DEFAULT_EDGE_MAX_LENGTH),
+    edgeMaxError_(DEFAULT_EDGE_MAX_ERROR),
+    detailSampleDistance_(DEFAULT_DETAIL_SAMPLE_DISTANCE),
+    detailSampleMaxError_(DEFAULT_DETAIL_SAMPLE_MAX_ERROR)
 {
 }
 
@@ -43,6 +76,193 @@ NavigationMesh::~NavigationMesh()
 void NavigationMesh::RegisterObject(Context* context)
 {
     context->RegisterFactory<NavigationMesh>();
+    
+    ACCESSOR_ATTRIBUTE(NavigationMesh, VAR_FLOAT, "Cell Size", GetCellSize, SetCellSize, float, DEFAULT_CELL_SIZE, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(NavigationMesh, VAR_FLOAT, "Cell Height", GetCellHeight, SetCellHeight, float, DEFAULT_CELL_HEIGHT, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(NavigationMesh, VAR_FLOAT, "Agent Height", GetAgentHeight, SetAgentHeight, float, DEFAULT_AGENT_HEIGHT, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(NavigationMesh, VAR_FLOAT, "Agent Radius", GetAgentRadius, SetAgentRadius, float, DEFAULT_AGENT_RADIUS, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(NavigationMesh, VAR_FLOAT, "Agent Max Climb", GetAgentMaxClimb, SetAgentMaxClimb, float, DEFAULT_AGENT_MAX_CLIMB, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(NavigationMesh, VAR_FLOAT, "Agent Max Slope", GetAgentMaxSlope, SetAgentMaxSlope, float, DEFAULT_AGENT_MAX_SLOPE, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(NavigationMesh, VAR_FLOAT, "Region Min Size", GetRegionMinSize, SetRegionMinSize, float, DEFAULT_REGION_MIN_SIZE, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(NavigationMesh, VAR_FLOAT, "Region Merge Size", GetRegionMergeSize, SetRegionMergeSize, float, DEFAULT_REGION_MERGE_SIZE, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(NavigationMesh, VAR_FLOAT, "Edge Max Length", GetEdgeMaxLength, SetEdgeMaxLength, float, DEFAULT_EDGE_MAX_LENGTH, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(NavigationMesh, VAR_FLOAT, "Edge Max Error", GetEdgeMaxError, SetEdgeMaxError, float, DEFAULT_EDGE_MAX_ERROR, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(NavigationMesh, VAR_FLOAT, "Detail Sample Distance", GetDetailSampleDistance, SetDetailSampleDistance, float, DEFAULT_DETAIL_SAMPLE_DISTANCE, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(NavigationMesh, VAR_FLOAT, "Detail Sample Max Error", GetDetailSampleMaxError, SetDetailSampleMaxError, float, DEFAULT_DETAIL_SAMPLE_MAX_ERROR, AM_DEFAULT);
+}
+
+void NavigationMesh::SetCellSize(float size)
+{
+    cellSize_ = Max(size, M_EPSILON);
+}
+
+void NavigationMesh::SetCellHeight(float height)
+{
+    cellHeight_ = Max(height, M_EPSILON);
+}
+
+void NavigationMesh::SetAgentHeight(float height)
+{
+    agentHeight_ = Max(height, M_EPSILON);
+}
+
+void NavigationMesh::SetAgentRadius(float radius)
+{
+    agentRadius_ = Max(radius, M_EPSILON);
+}
+
+void NavigationMesh::SetAgentMaxClimb(float maxClimb)
+{
+    agentMaxClimb_ = Max(maxClimb, M_EPSILON);
+}
+
+void NavigationMesh::SetAgentMaxSlope(float maxSlope)
+{
+    agentMaxSlope_ = Max(maxSlope, 0.0f);
+}
+
+void NavigationMesh::SetRegionMinSize(float size)
+{
+    regionMinSize_ = Max(size, M_EPSILON);
+}
+
+void NavigationMesh::SetRegionMergeSize(float size)
+{
+    regionMergeSize_ = Max(size, M_EPSILON);
+}
+
+void NavigationMesh::SetEdgeMaxLength(float length)
+{
+    edgeMaxLength_ = Max(length, M_EPSILON);
+}
+
+void NavigationMesh::SetEdgeMaxError(float error)
+{
+    edgeMaxError_ = Max(error, M_EPSILON);
+}
+
+void NavigationMesh::SetDetailSampleDistance(float distance)
+{
+    detailSampleDistance_ = Max(distance, M_EPSILON);
+}
+
+void NavigationMesh::SetDetailSampleMaxError(float error)
+{
+    detailSampleMaxError_ = Max(error, M_EPSILON);
+}
+
+bool NavigationMesh::Build()
+{
+    worldBoundingBox_.defined_ = false;
+    vertices_.Clear();
+    indices_.Clear();
+    
+    Scene* scene = GetScene();
+    if (!scene)
+        return false;
+    
+    PODVector<Navigable*> navigables;
+    scene->GetComponents<Navigable>(navigables, true);
+    
+    for (unsigned i = 0; i < navigables.Size(); ++i)
+        CollectGeometries(navigables[i]->GetNode(), navigables[i]->GetNode(), navigables[i]->GetFlags());
+    
+    LOGINFO("Navigation mesh has " + String(vertices_.Size()) + " vertices and " + String(indices_.Size()) + " indices");
+    return true;
+}
+
+void NavigationMesh::CollectGeometries(Node* node, Node* baseNode, unsigned flags)
+{
+    // If find a navigable from a child node that's not the current base node, abort so we're not going to add the geometry twice
+    if (node != baseNode && node->HasComponent<Navigable>())
+        return;
+    
+    /// \todo Prefer physics geometry if available
+    PODVector<Drawable*> drawables;
+    node->GetDerivedComponents<Drawable>(drawables);
+    for (unsigned i = 0; i < drawables.Size(); ++i)
+    {
+        /// \todo Evaluate whether should handle other types. Now StaticModel & TerrainPatch are supported, others skipped
+        Drawable* drawable = drawables[i];
+        unsigned numGeometries = drawable->GetBatches().Size();
+        unsigned lodLevel;
+        if (drawable->GetType() == StaticModel::GetTypeStatic())
+            lodLevel = static_cast<StaticModel*>(drawable)->GetOcclusionLodLevel();
+        else if (drawable->GetType() == TerrainPatch::GetTypeStatic())
+            lodLevel = 0;
+        else
+            continue;
+        
+        for (unsigned j = 0; j < numGeometries; ++j)
+            AddGeometry(node, drawable->GetLodGeometry(j, lodLevel));
+    }
+    
+    const Vector<SharedPtr<Node> >& children = node->GetChildren();
+    for(unsigned i = 0; i < children.Size(); ++i)
+        CollectGeometries(children[i], baseNode, flags);
+}
+
+void NavigationMesh::AddGeometry(Node* node, Geometry* geometry)
+{
+    const unsigned char* vertexData;
+    const unsigned char* indexData;
+    unsigned vertexSize;
+    unsigned indexSize;
+    unsigned elementMask;
+    
+    geometry->GetRawData(vertexData, vertexSize, indexData, indexSize, elementMask);
+    if (!vertexData || !indexData || (elementMask & MASK_POSITION) == 0)
+    {
+        LOGERROR("Could not use geometry from node " + node->GetName() + ": vertex or index raw data null or positions missing");
+        return;
+    }
+    
+    unsigned srcIndexStart = geometry->GetIndexStart();
+    unsigned srcIndexCount = geometry->GetIndexCount();
+    unsigned srcVertexStart = geometry->GetVertexStart();
+    unsigned srcVertexCount = geometry->GetVertexCount();
+    
+    if (!srcIndexCount)
+    {
+        LOGERROR("Could not use geometry from node " + node->GetName() + ": no indices");
+        return;
+    }
+    
+    unsigned destVertexStart = vertices_.Size();
+    
+    // Copy needed vertices transformed into world space
+    Matrix3x4 transform = node->GetWorldTransform();
+    
+    for (unsigned i = srcVertexStart; i < srcVertexStart + srcVertexCount; ++i)
+    {
+        Vector3 vertex = transform * *((const Vector3*)(&vertexData[i * vertexSize]));
+        worldBoundingBox_.Merge(vertex);
+        vertices_.Push(vertex);
+    }
+    
+    // Copy remapped indices
+    if (indexSize == sizeof(unsigned))
+    {
+        const unsigned short* indices = ((const unsigned short*)indexData) + srcIndexStart;
+        const unsigned short* indicesEnd = indices + srcIndexCount;
+        
+        while (indices < indicesEnd)
+        {
+            indices_.Push(*indices - srcVertexStart + destVertexStart);
+            ++indices;
+        }
+    }
+    else
+    {
+        const unsigned* indices = ((const unsigned*)indexData) + srcIndexStart;
+        const unsigned* indicesEnd = indices + srcIndexCount;
+        
+        while (indices < indicesEnd)
+        {
+            indices_.Push(*indices - srcVertexStart + destVertexStart);
+            ++indices;
+        }
+    }
 }
 
 }

+ 97 - 1
Engine/Navigation/NavigationMesh.h

@@ -22,12 +22,15 @@
 
 #pragma once
 
+#include "BoundingBox.h"
 #include "Component.h"
 
 namespace Urho3D
 {
 
-/// Navigation mesh component.
+class Geometry;
+
+/// Navigation mesh component. Collects the navigation geometry from the scene and responds to path queries.
 class NavigationMesh : public Component
 {
     OBJECT(NavigationMesh);
@@ -39,6 +42,99 @@ public:
     virtual ~NavigationMesh();
     /// Register object factory.
     static void RegisterObject(Context* context);
+    
+    /// Set cell size.
+    void SetCellSize(float size);
+    /// Set cell height.
+    void SetCellHeight(float height);
+    /// Set navigation agent height.
+    void SetAgentHeight(float height);
+    /// Set navigation agent radius.
+    void SetAgentRadius(float radius);
+    /// Set navigation agent max vertical climb.
+    void SetAgentMaxClimb(float maxClimb);
+    /// Set navigation agent max slope.
+    void SetAgentMaxSlope(float maxSlope);
+    /// Set region minimum size.
+    void SetRegionMinSize(float size);
+    /// Set region merge size.
+    void SetRegionMergeSize(float size);
+    /// Set edge max length.
+    void SetEdgeMaxLength(float length);
+    /// Set edge max error.
+    void SetEdgeMaxError(float error);
+    /// Set detail sampling distance.
+    void SetDetailSampleDistance(float distance);
+    /// Set detail sampling maximum error.
+    void SetDetailSampleMaxError(float error);
+    
+    /// Retun cell size.
+    float GetCellSize() const { return cellSize_; }
+    /// Return cell height.
+    float GetCellHeight() const { return cellHeight_; }
+    /// Return navigation agent height.
+    float GetAgentHeight() const { return agentHeight_; }
+    /// Return navigation agent radius.
+    float GetAgentRadius() const { return agentRadius_; }
+    /// Return navigation agent max vertical climb.
+    float GetAgentMaxClimb() const { return agentMaxClimb_; }
+    /// Return navigation agent max slope.
+    float GetAgentMaxSlope() const { return agentMaxSlope_; }
+    /// Return region minimum size.
+    float GetRegionMinSize() const { return regionMinSize_; }
+    /// Return region merge size.
+    float GetRegionMergeSize() const { return regionMergeSize_; }
+    /// Return edge max length.
+    float GetEdgeMaxLength() const { return edgeMaxLength_; }
+    /// Return edge max error.
+    float GetEdgeMaxError() const { return edgeMaxError_; }
+    /// Return detail sampling distance.
+    float GetDetailSampleDistance() const { return detailSampleDistance_; }
+    /// Return detail sampling maximum error.
+    float GetDetailSampleMaxError() const { return detailSampleMaxError_; }
+    /// Return the world bounding box.
+    const BoundingBox& GetWorldBoundingBox() const { return worldBoundingBox_; }
+    
+    /// Build/rebuild the navigation mesh. Return true if successful.
+    bool Build();
+    
+private:
+    /// Visit nodes and collect navigable geometry.
+    void CollectGeometries(Node* node, Node* baseNode, unsigned flags);
+    /// Add a geometry to the mesh.
+    void AddGeometry(Node* node, Geometry* geometry);
+    
+    /// Cell size.
+    float cellSize_;
+    /// Cell height.
+    float cellHeight_;
+    /// Navigation agent height.
+    float agentHeight_;
+    /// Navigation agent radius.
+    float agentRadius_;
+    /// Navigation agent max vertical climb.
+    float agentMaxClimb_;
+    /// Navigation agent max slope.
+    float agentMaxSlope_;
+    /// Region minimum size.
+    float regionMinSize_;
+    /// Region merge size.
+    float regionMergeSize_;
+    /// Edge max length.
+    float edgeMaxLength_;
+    /// Edge max error.
+    float edgeMaxError_;
+    /// Detail sampling distance.
+    float detailSampleDistance_;
+    /// Detail sampling maximum error.
+    float detailSampleMaxError_;
+    
+    /// World-space bounding box of the navigation mesh.
+    BoundingBox worldBoundingBox_;
+    /// Build phase vertices.
+    PODVector<Vector3> vertices_;
+    /// Build phase triangle indices.
+    PODVector<int> indices_;
 };
 
 }