Browse Source

Added support for partial navmesh rebuild.
Added navmesh properties (bounding box, number of tiles) to the script API.
Serialize navmesh tiles directly from Detour data, do not keep a duplicate.

Lasse Öörni 12 years ago
parent
commit
3fac48401a

+ 56 - 3
Bin/Data/Scripts/Navigation.as

@@ -41,7 +41,7 @@ void InitConsole()
     engine.CreateDebugHud();
     engine.CreateDebugHud();
     debugHud.style = uiStyle;
     debugHud.style = uiStyle;
     debugHud.mode = DEBUGHUD_SHOW_ALL;
     debugHud.mode = DEBUGHUD_SHOW_ALL;
-    debugHud.SetAppStats("Instructions:", "Shift+LMB to set startpoint, LMB to set endpoint");
+    debugHud.SetAppStats("Instructions:", "\nShift+LMB to set startpoint\nLMB to set endpoint\nMMB to create/delete object and rebuild partial navmesh");
 
 
     engine.CreateConsole();
     engine.CreateConsole();
     console.style = uiStyle;
     console.style = uiStyle;
@@ -352,6 +352,59 @@ void HandleMouseButtonDown(StringHash eventType, VariantMap& eventData)
             }
             }
         }
         }
     }
     }
+    
+    if (button == MOUSEB_MIDDLE)
+    {
+        IntVector2 pos = ui.cursorPosition;
+        if (ui.GetElementAt(pos, true) !is null)
+            return;
+    
+        Ray cameraRay = camera.GetScreenRay(float(pos.x) / graphics.width, float(pos.y) / graphics.height);
+        RayQueryResult result = testScene.octree.RaycastSingle(cameraRay, RAY_TRIANGLE, 1000.0, DRAWABLE_GEOMETRY);
+    
+        bool rebuild = false;
+        BoundingBox rebuildBox;
+    
+        if (result.drawable !is null)
+        {
+            Vector3 rayHitPos = cameraRay.origin + cameraRay.direction * result.distance;
+            if (result.node.name == "Mushroom")
+            {
+                rebuild = true;
+                rebuildBox = result.drawable.worldBoundingBox;
+                result.node.Remove();
+            }
+            else
+            {
+                Node@ objectNode = testScene.CreateChild("Mushroom");
+                objectNode.position = rayHitPos;
+                objectNode.rotation = Quaternion(0, Random(360.0), 0);
+                objectNode.SetScale(5);
+    
+                StaticModel@ object = objectNode.CreateComponent("StaticModel");
+                object.model = cache.GetResource("Model", "Models/Mushroom.mdl");
+                object.material = cache.GetResource("Material", "Materials/Mushroom.xml");
+                object.castShadows = true;
+    
+                RigidBody@ body = objectNode.CreateComponent("RigidBody");
+                CollisionShape@ shape = objectNode.CreateComponent("CollisionShape");
+                shape.SetTriangleMesh(object.model, 0);
+                
+                rebuild = true;
+                rebuildBox = object.worldBoundingBox;
+            }
+        }
+        
+        if (rebuild)
+        {
+            NavigationMesh@ navMesh = testScene.GetComponent("NavigationMesh");
+            navMesh.Build(rebuildBox);
+            
+            // Recalculate path if applicable
+            if (startPosSet && endPosSet)
+                path = navMesh.FindPath(startPos, endPos);
+        }
+    }
 }
 }
 
 
 void HandleMouseButtonUp(StringHash eventType, VariantMap& eventData)
 void HandleMouseButtonUp(StringHash eventType, VariantMap& eventData)
@@ -377,7 +430,7 @@ void HandlePostRenderUpdate()
     }
     }
 
 
     IntVector2 pos = ui.cursorPosition;
     IntVector2 pos = ui.cursorPosition;
-    if (ui.GetElementAt(pos, true) is null && testScene.octree !is null)
+    if (ui.GetElementAt(pos, true) is null)
     {
     {
         Ray cameraRay = camera.GetScreenRay(float(pos.x) / graphics.width, float(pos.y) / graphics.height);
         Ray cameraRay = camera.GetScreenRay(float(pos.x) / graphics.width, float(pos.y) / graphics.height);
         RayQueryResult result = testScene.octree.RaycastSingle(cameraRay, RAY_TRIANGLE, 1000.0, DRAWABLE_GEOMETRY);
         RayQueryResult result = testScene.octree.RaycastSingle(cameraRay, RAY_TRIANGLE, 1000.0, DRAWABLE_GEOMETRY);
@@ -405,4 +458,4 @@ void HandlePostRenderUpdate()
         for (uint i = 0; i < path.length - 1; ++i)
         for (uint i = 0; i < path.length - 1; ++i)
             testScene.debugRenderer.AddLine(path[i], path[i + 1], Color(1.0, 1.0, 1.0), false);
             testScene.debugRenderer.AddLine(path[i], path[i + 1], Color(1.0, 1.0, 1.0), false);
     }
     }
-}
+}

+ 3 - 2
Docs/GettingStarted.dox

@@ -158,8 +158,9 @@ L           Toggle buoyant liquid volume
 A test of navigation mesh generation and path queries. Generates the same static scene as TestScene. To start, run Navigation.bat in the Bin directory, or use the command Urho3D.exe Scripts\Navigation.as. Controls are like in TestScene, except:
 A test of navigation mesh generation and path queries. Generates the same static scene as TestScene. To start, run Navigation.bat in the Bin directory, or use the command Urho3D.exe Scripts\Navigation.as. Controls are like in TestScene, except:
 
 
 \verbatim
 \verbatim
-Left mouse  Set path query end point
-Shift+LMB   Set path query start point
+Left mouse   Set path query end point
+Shift+LMB    Set path query start point
+Middle mouse Create or delete object and rebuild navmesh partially
 \endverbatim
 \endverbatim
 
 
 \section Running_Physics Physics
 \section Running_Physics Physics

+ 5 - 0
Docs/ScriptAPI.dox

@@ -5396,6 +5396,7 @@ Methods:<br>
 - void MarkNetworkUpdate() const
 - void MarkNetworkUpdate() const
 - void DrawDebugGeometry(DebugRenderer@, bool)
 - void DrawDebugGeometry(DebugRenderer@, bool)
 - bool Build()
 - bool Build()
+- bool Build(const BoundingBox&)
 - Vector3[]@ FindPath(const Vector3&, const Vector3&, const Vector3& arg2 = Vector3 ( 1.0 , 1.0 , 1.0 ))
 - Vector3[]@ FindPath(const Vector3&, const Vector3&, const Vector3& arg2 = Vector3 ( 1.0 , 1.0 , 1.0 ))
 
 
 Properties:<br>
 Properties:<br>
@@ -5410,6 +5411,7 @@ Properties:<br>
 - bool enabledEffective (readonly)
 - bool enabledEffective (readonly)
 - uint id (readonly)
 - uint id (readonly)
 - Node@ node (readonly)
 - Node@ node (readonly)
+- int tileSize
 - float cellSize
 - float cellSize
 - float cellHeight
 - float cellHeight
 - float agentHeight
 - float agentHeight
@@ -5423,6 +5425,9 @@ Properties:<br>
 - float detailSampleDistance
 - float detailSampleDistance
 - float detailSampleMaxError
 - float detailSampleMaxError
 - bool initialized (readonly)
 - bool initialized (readonly)
+- BoundingBox boundingBox (readonly)
+- BoundingBox worldBoundingBox (readonly)
+- IntVector2 numTiles (readonly)
 
 
 
 
 ScriptFile
 ScriptFile

+ 5 - 1
Engine/Engine/NavigationAPI.cpp

@@ -45,7 +45,8 @@ static CScriptArray* NavigationMeshFindPath(const Vector3& start, const Vector3&
 void RegisterNavigationMesh(asIScriptEngine* engine)
 void RegisterNavigationMesh(asIScriptEngine* engine)
 {
 {
     RegisterComponent<NavigationMesh>(engine, "NavigationMesh");
     RegisterComponent<NavigationMesh>(engine, "NavigationMesh");
-    engine->RegisterObjectMethod("NavigationMesh", "bool Build()", asMETHOD(NavigationMesh, Build), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "bool Build()", asMETHODPR(NavigationMesh, Build, (void), bool), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "bool Build(const BoundingBox&in)", asMETHODPR(NavigationMesh, Build, (const BoundingBox&), bool), asCALL_THISCALL);
     engine->RegisterObjectMethod("NavigationMesh", "Array<Vector3>@ FindPath(const Vector3&in, const Vector3&in, const Vector3&in extents = Vector3(1.0, 1.0, 1.0))", asFUNCTION(NavigationMeshFindPath), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("NavigationMesh", "Array<Vector3>@ FindPath(const Vector3&in, const Vector3&in, const Vector3&in extents = Vector3(1.0, 1.0, 1.0))", asFUNCTION(NavigationMeshFindPath), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("NavigationMesh", "void set_tileSize(int)", asMETHOD(NavigationMesh, SetTileSize), asCALL_THISCALL);
     engine->RegisterObjectMethod("NavigationMesh", "void set_tileSize(int)", asMETHOD(NavigationMesh, SetTileSize), asCALL_THISCALL);
     engine->RegisterObjectMethod("NavigationMesh", "int get_tileSize() const", asMETHOD(NavigationMesh, GetTileSize), asCALL_THISCALL);
     engine->RegisterObjectMethod("NavigationMesh", "int get_tileSize() const", asMETHOD(NavigationMesh, GetTileSize), asCALL_THISCALL);
@@ -74,6 +75,9 @@ void RegisterNavigationMesh(asIScriptEngine* engine)
     engine->RegisterObjectMethod("NavigationMesh", "void set_detailSampleMaxError(float)", asMETHOD(NavigationMesh, SetDetailSampleMaxError), 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", "float get_detailSampleMaxError() const", asMETHOD(NavigationMesh, GetDetailSampleMaxError), asCALL_THISCALL);
     engine->RegisterObjectMethod("NavigationMesh", "bool get_initialized() const", asMETHOD(NavigationMesh, IsInitialized), asCALL_THISCALL);
     engine->RegisterObjectMethod("NavigationMesh", "bool get_initialized() const", asMETHOD(NavigationMesh, IsInitialized), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "const BoundingBox& get_boundingBox() const", asMETHOD(NavigationMesh, GetBoundingBox), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "BoundingBox get_worldBoundingBox() const", asMETHOD(NavigationMesh, GetWorldBoundingBox), asCALL_THISCALL);
+    engine->RegisterObjectMethod("NavigationMesh", "IntVector2 get_numTiles() const", asMETHOD(NavigationMesh, GetNumTiles), asCALL_THISCALL);
 }
 }
 
 
 void RegisterNavigationAPI(asIScriptEngine* engine)
 void RegisterNavigationAPI(asIScriptEngine* engine)

+ 369 - 278
Engine/Navigation/NavigationMesh.cpp

@@ -27,12 +27,14 @@
 #include "Drawable.h"
 #include "Drawable.h"
 #include "Geometry.h"
 #include "Geometry.h"
 #include "Log.h"
 #include "Log.h"
+#include "MemoryBuffer.h"
 #include "Navigable.h"
 #include "Navigable.h"
 #include "NavigationMesh.h"
 #include "NavigationMesh.h"
 #include "Profiler.h"
 #include "Profiler.h"
 #include "Scene.h"
 #include "Scene.h"
 #include "StaticModel.h"
 #include "StaticModel.h"
 #include "TerrainPatch.h"
 #include "TerrainPatch.h"
+#include "VectorBuffer.h"
 
 
 #include <DetourNavMesh.h>
 #include <DetourNavMesh.h>
 #include <DetourNavMeshBuilder.h>
 #include <DetourNavMeshBuilder.h>
@@ -60,7 +62,7 @@ static const float DEFAULT_DETAIL_SAMPLE_MAX_ERROR = 1.0f;
 
 
 static const int MAX_POLYS = 2048;
 static const int MAX_POLYS = 2048;
 
 
-/// Temporary data for building one tile of the navigation mesh
+/// Temporary data for building one tile of the navigation mesh.
 struct NavigationBuildData
 struct NavigationBuildData
 {
 {
     /// Construct.
     /// Construct.
@@ -129,6 +131,10 @@ OBJECTTYPESTATIC(NavigationMesh);
 
 
 NavigationMesh::NavigationMesh(Context* context) :
 NavigationMesh::NavigationMesh(Context* context) :
     Component(context),
     Component(context),
+    navMesh_(0),
+    navMeshQuery_(0),
+    queryFilter_(new dtQueryFilter()),
+    pathData_(new FindPathData()),
     tileSize_(DEFAULT_TILE_SIZE),
     tileSize_(DEFAULT_TILE_SIZE),
     cellSize_(DEFAULT_CELL_SIZE),
     cellSize_(DEFAULT_CELL_SIZE),
     cellHeight_(DEFAULT_CELL_HEIGHT),
     cellHeight_(DEFAULT_CELL_HEIGHT),
@@ -143,17 +149,13 @@ NavigationMesh::NavigationMesh(Context* context) :
     detailSampleDistance_(DEFAULT_DETAIL_SAMPLE_DISTANCE),
     detailSampleDistance_(DEFAULT_DETAIL_SAMPLE_DISTANCE),
     detailSampleMaxError_(DEFAULT_DETAIL_SAMPLE_MAX_ERROR),
     detailSampleMaxError_(DEFAULT_DETAIL_SAMPLE_MAX_ERROR),
     numTilesX_(0),
     numTilesX_(0),
-    numTilesZ_(0),
-    navMesh_(0),
-    navMeshQuery_(0),
-    queryFilter_(new dtQueryFilter()),
-    pathData_(new FindPathData())
+    numTilesZ_(0)
 {
 {
 }
 }
 
 
 NavigationMesh::~NavigationMesh()
 NavigationMesh::~NavigationMesh()
 {
 {
-    ReleaseNavMesh();
+    ReleaseNavigationMesh();
     
     
     delete queryFilter_;
     delete queryFilter_;
     queryFilter_ = 0;
     queryFilter_ = 0;
@@ -179,7 +181,7 @@ void NavigationMesh::RegisterObject(Context* context)
     ACCESSOR_ATTRIBUTE(NavigationMesh, VAR_FLOAT, "Edge Max Error", GetEdgeMaxError, SetEdgeMaxError, float, DEFAULT_EDGE_MAX_ERROR, 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 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);
     ACCESSOR_ATTRIBUTE(NavigationMesh, VAR_FLOAT, "Detail Sample Max Error", GetDetailSampleMaxError, SetDetailSampleMaxError, float, DEFAULT_DETAIL_SAMPLE_MAX_ERROR, AM_DEFAULT);
-    REF_ACCESSOR_ATTRIBUTE(NavigationMesh, VAR_BUFFER, "Navigation Data", GetNavigationDataAttr, SetNavigationDataAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_FILE | AM_NOEDIT);
+    ACCESSOR_ATTRIBUTE(NavigationMesh, VAR_BUFFER, "Navigation Data", GetNavigationDataAttr, SetNavigationDataAttr, PODVector<unsigned char>, Variant::emptyBuffer, AM_FILE | AM_NOEDIT);
 }
 }
 
 
 void NavigationMesh::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
 void NavigationMesh::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
@@ -285,7 +287,7 @@ bool NavigationMesh::Build()
 {
 {
     PROFILE(BuildNavigationMesh);
     PROFILE(BuildNavigationMesh);
     
     
-    ReleaseNavMesh();
+    ReleaseNavigationMesh();
     
     
     if (!node_)
     if (!node_)
         return false;
         return false;
@@ -293,31 +295,15 @@ bool NavigationMesh::Build()
     if (!node_->GetWorldScale().Equals(Vector3::ONE))
     if (!node_->GetWorldScale().Equals(Vector3::ONE))
         LOGWARNING("Navigation mesh root node has scaling. Agent parameters may not work as intended");
         LOGWARNING("Navigation mesh root node has scaling. Agent parameters may not work as intended");
     
     
-    BoundingBox combinedBoundingBox;
     Vector<GeometryInfo> geometryList;
     Vector<GeometryInfo> geometryList;
-    
-    {
-        PROFILE(CollectNavigationGeometry);
-        
-        // Get Navigable components from child nodes, not from whole scene. This makes it theoretically possible to partition
-        // the scene into several navigation meshes
-        PODVector<Navigable*> navigables;
-        node_->GetComponents<Navigable>(navigables, true);
-        
-        HashSet<Node*> processedNodes;
-        for (unsigned i = 0; i < navigables.Size(); ++i)
-        {
-            if (navigables[i]->IsEnabledEffective())
-                CollectGeometries(geometryList, navigables[i]->GetNode(), processedNodes, navigables[i]->IsRecursive());
-        }
-    }
+    CollectGeometries(geometryList);
     
     
     if (geometryList.Empty())
     if (geometryList.Empty())
         return true; // Nothing to do
         return true; // Nothing to do
     
     
     // Build the combined bounding box
     // Build the combined bounding box
     for (unsigned i = 0; i < geometryList.Size(); ++i)
     for (unsigned i = 0; i < geometryList.Size(); ++i)
-        combinedBoundingBox.Merge(geometryList[i].boundingBox_);
+        boundingBox_.Merge(geometryList[i].boundingBox_);
     
     
     {
     {
         PROFILE(BuildNavigationMesh);
         PROFILE(BuildNavigationMesh);
@@ -325,7 +311,7 @@ bool NavigationMesh::Build()
         // Calculate number of tiles
         // Calculate number of tiles
         int gridW = 0, gridH = 0;
         int gridW = 0, gridH = 0;
         float tileEdgeLength = (float)tileSize_ * cellSize_;
         float tileEdgeLength = (float)tileSize_ * cellSize_;
-        rcCalcGridSize(&combinedBoundingBox.min_.x_, &combinedBoundingBox.max_.x_, cellSize_, &gridW, &gridH);
+        rcCalcGridSize(&boundingBox_.min_.x_, &boundingBox_.max_.x_, cellSize_, &gridW, &gridH);
         numTilesX_ = (gridW + tileSize_ - 1) / tileSize_;
         numTilesX_ = (gridW + tileSize_ - 1) / tileSize_;
         numTilesZ_ = (gridH + tileSize_ - 1) / tileSize_;
         numTilesZ_ = (gridH + tileSize_ - 1) / tileSize_;
         
         
@@ -342,21 +328,12 @@ bool NavigationMesh::Build()
         unsigned maxPolys = 1 << (22 - tileBits);
         unsigned maxPolys = 1 << (22 - tileBits);
         
         
         dtNavMeshParams params;
         dtNavMeshParams params;
-        rcVcopy(params.orig, &combinedBoundingBox.min_.x_);
+        rcVcopy(params.orig, &boundingBox_.min_.x_);
         params.tileWidth = tileEdgeLength;
         params.tileWidth = tileEdgeLength;
         params.tileHeight = tileEdgeLength;
         params.tileHeight = tileEdgeLength;
         params.maxTiles = maxTiles;
         params.maxTiles = maxTiles;
         params.maxPolys = maxPolys;
         params.maxPolys = maxPolys;
         
         
-        navigationDataAttr_.Clear();
-        navigationDataAttr_.WriteBoundingBox(combinedBoundingBox);
-        navigationDataAttr_.WriteFloat(params.tileWidth);
-        navigationDataAttr_.WriteFloat(params.tileHeight);
-        navigationDataAttr_.WriteInt(maxTiles);
-        navigationDataAttr_.WriteInt(maxPolys);
-        navigationDataAttr_.WriteInt(numTilesX_);
-        navigationDataAttr_.WriteInt(numTilesZ_);
-        
         navMesh_ = dtAllocNavMesh();
         navMesh_ = dtAllocNavMesh();
         if (!navMesh_)
         if (!navMesh_)
         {
         {
@@ -367,7 +344,7 @@ bool NavigationMesh::Build()
         if (dtStatusFailed(navMesh_->init(&params)))
         if (dtStatusFailed(navMesh_->init(&params)))
         {
         {
             LOGERROR("Could not initialize navigation mesh");
             LOGERROR("Could not initialize navigation mesh");
-            ReleaseNavMesh();
+            ReleaseNavigationMesh();
             return false;
             return false;
         }
         }
         
         
@@ -378,230 +355,75 @@ bool NavigationMesh::Build()
         {
         {
             for (int x = 0; x < numTilesX_; ++x)
             for (int x = 0; x < numTilesX_; ++x)
             {
             {
-                PROFILE(BuildNavigationMeshTile);
-                
-                BoundingBox tileBoundingBox(Vector3(
-                    combinedBoundingBox.min_.x_ + tileEdgeLength * (float)x,
-                    combinedBoundingBox.min_.y_,
-                    combinedBoundingBox.min_.z_ + tileEdgeLength * (float)z
-                ),
-                Vector3(
-                    combinedBoundingBox.min_.x_ + tileEdgeLength * (float)(x + 1),
-                    combinedBoundingBox.max_.y_,
-                    combinedBoundingBox.min_.z_ + tileEdgeLength * (float)(z + 1)
-                ));
-                
-                NavigationBuildData build;
-                
-                rcConfig cfg;
-                memset(&cfg, 0, sizeof cfg);
-                cfg.cs = cellSize_;
-                cfg.ch = cellHeight_;
-                cfg.walkableSlopeAngle = agentMaxSlope_;
-                cfg.walkableHeight = (int)ceilf(agentHeight_ / cfg.ch);
-                cfg.walkableClimb = (int)floorf(agentMaxClimb_ / cfg.ch);
-                cfg.walkableRadius = (int)ceilf(agentRadius_ / cfg.cs);
-                cfg.maxEdgeLen = (int)(edgeMaxLength_ / cellSize_);
-                cfg.maxSimplificationError = edgeMaxError_;
-                cfg.minRegionArea = (int)sqrtf(regionMinSize_);
-                cfg.mergeRegionArea = (int)sqrtf(regionMergeSize_);
-                cfg.maxVertsPerPoly = 6;
-                cfg.tileSize = tileSize_;
-                cfg.borderSize = cfg.walkableRadius + 3; // Add padding
-                cfg.width = cfg.tileSize + cfg.borderSize * 2;
-                cfg.height = cfg.tileSize + cfg.borderSize * 2;
-                cfg.detailSampleDist = detailSampleDistance_ < 0.9f ? 0.0f : cellSize_ * detailSampleDistance_;
-                cfg.detailSampleMaxError = cellHeight_ * detailSampleMaxError_;
-                
-                rcVcopy(cfg.bmin, &tileBoundingBox.min_.x_);
-                rcVcopy(cfg.bmax, &tileBoundingBox.max_.x_);
-                cfg.bmin[0] -= cfg.borderSize * cfg.cs;
-                cfg.bmin[2] -= cfg.borderSize * cfg.cs;
-                cfg.bmax[0] += cfg.borderSize * cfg.cs;
-                cfg.bmax[2] += cfg.borderSize * cfg.cs;
-                
-                BoundingBox expandedBox(*reinterpret_cast<Vector3*>(cfg.bmin), *reinterpret_cast<Vector3*>(cfg.bmax));
-                GetTileGeometry(build, geometryList, expandedBox);
-                
-                build.heightField_ = rcAllocHeightfield();
-                if (!build.heightField_)
-                {
-                    LOGERROR("Could not allocate heightfield");
-                    continue;
-                }
-                
-                if (!rcCreateHeightfield(build.ctx_, *build.heightField_, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs,
-                    cfg.ch))
-                {
-                    LOGERROR("Could not create heightfield");
-                    continue;
-                }
-                
-                unsigned numTriangles = build.indices_.Size() / 3;
-                SharedArrayPtr<unsigned char> triAreas(new unsigned char[numTriangles]);
-                memset(triAreas.Get(), 0, numTriangles);
-                
-                rcMarkWalkableTriangles(build.ctx_, cfg.walkableSlopeAngle, &build.vertices_[0].x_, build.vertices_.Size(),
-                    &build.indices_[0], numTriangles, triAreas.Get());
-                rcRasterizeTriangles(build.ctx_, &build.vertices_[0].x_, build.vertices_.Size(), &build.indices_[0],
-                    triAreas.Get(), numTriangles, *build.heightField_, cfg.walkableClimb);
-                rcFilterLowHangingWalkableObstacles(build.ctx_, cfg.walkableClimb, *build.heightField_);
-                rcFilterLedgeSpans(build.ctx_, cfg.walkableHeight, cfg.walkableClimb, *build.heightField_);
-                rcFilterWalkableLowHeightSpans(build.ctx_, cfg.walkableHeight, *build.heightField_);
-                
-                build.compactHeightField_ = rcAllocCompactHeightfield();
-                if (!build.compactHeightField_)
-                {
-                    LOGERROR("Could not allocate create compact heightfield");
-                    continue;
-                }
-                if (!rcBuildCompactHeightfield(build.ctx_, cfg.walkableHeight, cfg.walkableClimb, *build.heightField_,
-                    *build.compactHeightField_))
-                {
-                    LOGERROR("Could not build compact heightfield");
-                    continue;
-                }
-                if (!rcErodeWalkableArea(build.ctx_, cfg.walkableRadius, *build.compactHeightField_))
-                {
-                    LOGERROR("Could not erode compact heightfield");
-                    continue;
-                }
-                if (!rcBuildDistanceField(build.ctx_, *build.compactHeightField_))
-                {
-                    LOGERROR("Could not build distance field");
-                    continue;
-                }
-                if (!rcBuildRegions(build.ctx_, *build.compactHeightField_, cfg.borderSize, cfg.minRegionArea,
-                    cfg.mergeRegionArea))
-                {
-                    LOGERROR("Could not build regions");
-                    continue;
-                }
-                
-                build.contourSet_ = rcAllocContourSet();
-                if (!build.contourSet_)
-                {
-                    LOGERROR("Could not allocate contour set");
-                    continue;
-                }
-                if (!rcBuildContours(build.ctx_, *build.compactHeightField_, cfg.maxSimplificationError, cfg.maxEdgeLen,
-                    *build.contourSet_))
-                {
-                    LOGERROR("Could not create contours");
-                    continue;
-                }
-                
-                build.polyMesh_ = rcAllocPolyMesh();
-                if (!build.polyMesh_)
-                {
-                    LOGERROR("Could not allocate poly mesh");
-                    continue;
-                }
-                if (!rcBuildPolyMesh(build.ctx_, *build.contourSet_, cfg.maxVertsPerPoly, *build.polyMesh_))
-                {
-                    LOGERROR("Could not triangulate contours");
-                    continue;
-                }
-                
-                build.polyMeshDetail_ = rcAllocPolyMeshDetail();
-                if (!build.polyMeshDetail_)
-                {
-                    LOGERROR("Could not allocate detail mesh");
-                    continue;
-                }
-                if (!rcBuildPolyMeshDetail(build.ctx_, *build.polyMesh_, *build.compactHeightField_, cfg.detailSampleDist,
-                    cfg.detailSampleMaxError, *build.polyMeshDetail_))
-                {
-                    LOGERROR("Could not build detail mesh");
-                    continue;
-                }
-                
-                // Set polygon flags
-                /// \todo Allow to define custom flags
-                for (int i = 0; i < build.polyMesh_->npolys; ++i)
-                {
-                    if (build.polyMesh_->areas[i] == RC_WALKABLE_AREA)
-                        build.polyMesh_->flags[i] = 0x1;
-                }
-                
-                unsigned char* navData = 0;
-                int navDataSize = 0;
-                
-                dtNavMeshCreateParams params;
-                memset(&params, 0, sizeof params);
-                params.verts = build.polyMesh_->verts;
-                params.vertCount = build.polyMesh_->nverts;
-                params.polys = build.polyMesh_->polys;
-                params.polyAreas = build.polyMesh_->areas;
-                params.polyFlags = build.polyMesh_->flags;
-                params.polyCount = build.polyMesh_->npolys;
-                params.nvp = build.polyMesh_->nvp;
-                params.detailMeshes = build.polyMeshDetail_->meshes;
-                params.detailVerts = build.polyMeshDetail_->verts;
-                params.detailVertsCount = build.polyMeshDetail_->nverts;
-                params.detailTris = build.polyMeshDetail_->tris;
-                params.detailTriCount = build.polyMeshDetail_->ntris;
-                params.walkableHeight = agentHeight_;
-                params.walkableRadius = agentRadius_;
-                params.walkableClimb = agentMaxClimb_;
-                params.tileX = x;
-                params.tileY = z;
-                rcVcopy(params.bmin, build.polyMesh_->bmin);
-                rcVcopy(params.bmax, build.polyMesh_->bmax);
-                params.cs = cfg.cs;
-                params.ch = cfg.ch;
-                params.buildBvTree = true;
-                
-                if (!dtCreateNavMeshData(&params, &navData, &navDataSize))
-                {
-                    LOGERROR("Could not build navigation mesh tile data");
-                    continue;
-                }
-                
-                // Before adding the navmesh tile (which modifies the data) copy the data for serialization
-                navigationDataAttr_.WriteUInt(navDataSize);
-                navigationDataAttr_.Write(navData, navDataSize);
-                
-                if (dtStatusFailed(navMesh_->addTile(navData, navDataSize, DT_TILE_FREE_DATA, 0, 0)))
-                {
-                    LOGERROR("Failed to add navigation mesh tile");
-                    dtFree(navData);
-                    continue;
-                }
-                else
+                if (BuildTile(geometryList, x, z))
                     ++numTiles;
                     ++numTiles;
             }
             }
         }
         }
         
         
-        navMeshQuery_ = dtAllocNavMeshQuery();
-        if (!navMeshQuery_)
-        {
-            LOGERROR("Could not create navigation mesh query");
-            ReleaseNavMesh();
-            return false;
-        }
-        
-        if (dtStatusFailed(navMeshQuery_->init(navMesh_, MAX_POLYS)))
-        {
-            LOGERROR("Could not init navigation mesh query");
-            ReleaseNavMesh();
-            return false;
-        }
-        
         LOGDEBUG("Built navigation mesh with " + String(numTiles) + " tiles");
         LOGDEBUG("Built navigation mesh with " + String(numTiles) + " tiles");
         return true;
         return true;
     }
     }
 }
 }
 
 
+bool NavigationMesh::Build(const BoundingBox& boundingBox)
+{
+    PROFILE(BuildPartialNavigationMesh);
+    
+    /// \todo Partial rebuild does not modify the bounding box. Modifying in Y-direction could be supported
+    if (!node_)
+        return false;
+    
+    if (!navMesh_)
+    {
+        LOGERROR("Navigation mesh must first be built fully before it can be partially rebuilt");
+        return false;
+    }
+    
+    if (!node_->GetWorldScale().Equals(Vector3::ONE))
+        LOGWARNING("Navigation mesh root node has scaling. Agent parameters may not work as intended");
+    
+    BoundingBox localSpaceBox = boundingBox.Transformed(node_->GetWorldTransform().Inverse());
+    
+    float tileEdgeLength = (float)tileSize_ * cellSize_;
+    
+    Vector<GeometryInfo> geometryList;
+    CollectGeometries(geometryList);
+    
+    int sx = Clamp((int)((localSpaceBox.min_.x_ - boundingBox_.min_.x_) / tileEdgeLength), 0, numTilesX_ - 1);
+    int sz = Clamp((int)((localSpaceBox.min_.z_ - boundingBox_.min_.z_) / tileEdgeLength), 0, numTilesZ_ - 1);
+    int ex = Clamp((int)((localSpaceBox.max_.x_ - boundingBox_.min_.x_) / tileEdgeLength), 0, numTilesX_ - 1);
+    int ez = Clamp((int)((localSpaceBox.max_.z_ - boundingBox_.min_.z_) / tileEdgeLength), 0, numTilesZ_ - 1);
+    
+    unsigned numTiles = 0;
+    
+    for (int z = sz; z <= ez; ++z)
+    {
+        for (int x = sx; x <= ex; ++x)
+        {
+            if (BuildTile(geometryList, x, z))
+                ++numTiles;
+        }
+    }
+    
+    LOGDEBUG("Rebuilt " + String(numTiles) + " tiles of the navigation mesh");
+    return true;
+}
+
 void NavigationMesh::FindPath(PODVector<Vector3>& dest, const Vector3& start, const Vector3& end, const Vector3& extents)
 void NavigationMesh::FindPath(PODVector<Vector3>& dest, const Vector3& start, const Vector3& end, const Vector3& extents)
 {
 {
     PROFILE(FindPath);
     PROFILE(FindPath);
     
     
     dest.Clear();
     dest.Clear();
     
     
-    if (!navMesh_ || !navMeshQuery_ || !node_)
+    if (!navMesh_ || !node_)
         return;
         return;
     
     
+    if (!navMeshQuery_)
+    {
+        if (!InitializeQuery())
+            return;
+    }
+    
     // Navigation data is in local space. Transform path points from world to local
     // Navigation data is in local space. Transform path points from world to local
     const Matrix3x4& transform = node_->GetWorldTransform();
     const Matrix3x4& transform = node_->GetWorldTransform();
     Matrix3x4 inverseTransform = transform.Inverse();
     Matrix3x4 inverseTransform = transform.Inverse();
@@ -639,75 +461,128 @@ void NavigationMesh::FindPath(PODVector<Vector3>& dest, const Vector3& start, co
         dest.Push(transform * pathData_->pathPoints_[i]);
         dest.Push(transform * pathData_->pathPoints_[i]);
 }
 }
 
 
-void NavigationMesh::SetNavigationDataAttr(const PODVector<unsigned char>& data)
+BoundingBox NavigationMesh::GetWorldBoundingBox() const
 {
 {
-    ReleaseNavMesh();
+    return node_ ? boundingBox_.Transformed(node_->GetWorldTransform()) : boundingBox_;
+}
+
+void NavigationMesh::SetNavigationDataAttr(PODVector<unsigned char> data)
+{
+    ReleaseNavigationMesh();
     
     
-    navigationDataAttr_ = data;
     if (!data.Size())
     if (!data.Size())
         return;
         return;
     
     
-    navigationDataAttr_.Seek(0);
+    MemoryBuffer buffer(data);
     
     
-    BoundingBox combinedBoundingBox = navigationDataAttr_.ReadBoundingBox();
+    boundingBox_ = buffer.ReadBoundingBox();
+    numTilesX_ = buffer.ReadInt();
+    numTilesZ_ = buffer.ReadInt();
+
     dtNavMeshParams params;
     dtNavMeshParams params;
-    rcVcopy(params.orig, &combinedBoundingBox.min_.x_);
-    params.tileWidth = navigationDataAttr_.ReadFloat();
-    params.tileHeight = navigationDataAttr_.ReadFloat();
-    params.maxTiles = navigationDataAttr_.ReadInt();
-    params.maxPolys = navigationDataAttr_.ReadInt();
-    numTilesX_ = navigationDataAttr_.ReadInt();
-    numTilesZ_ = navigationDataAttr_.ReadInt();
+    rcVcopy(params.orig, &boundingBox_.min_.x_);
+    params.tileWidth = buffer.ReadFloat();
+    params.tileHeight = buffer.ReadFloat();
+    params.maxTiles = buffer.ReadInt();
+    params.maxPolys = buffer.ReadInt();
     
     
     navMesh_ = dtAllocNavMesh();
     navMesh_ = dtAllocNavMesh();
     if (!navMesh_)
     if (!navMesh_)
     {
     {
-        LOGERROR("Could not allocate navmesh");
+        LOGERROR("Could not allocate navigation mesh");
         return;
         return;
     }
     }
     
     
     if (dtStatusFailed(navMesh_->init(&params)))
     if (dtStatusFailed(navMesh_->init(&params)))
     {
     {
-        LOGERROR("Could not initialize navmesh");
-        ReleaseNavMesh();
+        LOGERROR("Could not initialize navigation mesh");
+        ReleaseNavigationMesh();
         return;
         return;
     }
     }
     
     
-    // Add tiles from serialized data
     unsigned numTiles = 0;
     unsigned numTiles = 0;
     
     
-    while (!navigationDataAttr_.IsEof())
+    while (!buffer.IsEof())
     {
     {
-        unsigned navDataSize = navigationDataAttr_.ReadUInt();
+        int x = buffer.ReadInt();
+        int z = buffer.ReadInt();
+        dtTileRef tileRef = buffer.ReadUInt();
+        unsigned navDataSize = buffer.ReadUInt();
+        
         unsigned char* navData = (unsigned char*)dtAlloc(navDataSize, DT_ALLOC_PERM);
         unsigned char* navData = (unsigned char*)dtAlloc(navDataSize, DT_ALLOC_PERM);
-        navigationDataAttr_.Read(navData, navDataSize);
+        if (!navData)
+        {
+            LOGERROR("Could not allocate data for navigation mesh tile");
+            return;
+        }
         
         
+        buffer.Read(navData, navDataSize);
         if (dtStatusFailed(navMesh_->addTile(navData, navDataSize, DT_TILE_FREE_DATA, 0, 0)))
         if (dtStatusFailed(navMesh_->addTile(navData, navDataSize, DT_TILE_FREE_DATA, 0, 0)))
         {
         {
             LOGERROR("Failed to add navigation mesh tile");
             LOGERROR("Failed to add navigation mesh tile");
             dtFree(navData);
             dtFree(navData);
-            continue;
+            return;
         }
         }
         else
         else
             ++numTiles;
             ++numTiles;
     }
     }
     
     
-    navMeshQuery_ = dtAllocNavMeshQuery();
-    if (!navMeshQuery_)
+    LOGDEBUG("Created navigation mesh with " + String(numTiles) + " tiles from serialized data");
+}
+
+PODVector<unsigned char> NavigationMesh::GetNavigationDataAttr() const
+{
+    VectorBuffer ret;
+    
+    if (navMesh_)
     {
     {
-        LOGERROR("Could not create navigation mesh query");
-        ReleaseNavMesh();
-        return;
+        ret.WriteBoundingBox(boundingBox_);
+        ret.WriteInt(numTilesX_);
+        ret.WriteInt(numTilesZ_);
+        
+        const dtNavMeshParams* params = navMesh_->getParams();
+        ret.WriteFloat(params->tileWidth);
+        ret.WriteFloat(params->tileHeight);
+        ret.WriteInt(params->maxTiles);
+        ret.WriteInt(params->maxPolys);
+        
+        const dtNavMesh* navMesh = navMesh_;
+        
+        for (int z = 0; z < numTilesZ_; ++z)
+        {
+            for (int x = 0; x < numTilesX_; ++x)
+            {
+                const dtMeshTile* tile = navMesh->getTileAt(x, z, 0);
+                if (!tile)
+                    continue;
+                
+                ret.WriteInt(x);
+                ret.WriteInt(z);
+                ret.WriteUInt(navMesh->getTileRef(tile));
+                ret.WriteUInt(tile->dataSize);
+                ret.Write(tile->data, tile->dataSize);
+            }
+        }
     }
     }
     
     
-    if (dtStatusFailed(navMeshQuery_->init(navMesh_, MAX_POLYS)))
+    return ret.GetBuffer();
+}
+
+void NavigationMesh::CollectGeometries(Vector<GeometryInfo>& geometryList)
+{
+    PROFILE(CollectNavigationGeometry);
+    
+    // Get Navigable components from child nodes, not from whole scene. This makes it possible to partition
+    // the scene into several navigation meshes
+    PODVector<Navigable*> navigables;
+    node_->GetComponents<Navigable>(navigables, true);
+    
+    HashSet<Node*> processedNodes;
+    for (unsigned i = 0; i < navigables.Size(); ++i)
     {
     {
-        LOGERROR("Could not init navigation mesh query");
-        ReleaseNavMesh();
-        return;
+        if (navigables[i]->IsEnabledEffective())
+            CollectGeometries(geometryList, navigables[i]->GetNode(), processedNodes, navigables[i]->IsRecursive());
     }
     }
-    
-    LOGDEBUG("Built navigation mesh with " + String(numTiles) + " tiles");
 }
 }
 
 
 void NavigationMesh::CollectGeometries(Vector<GeometryInfo>& geometryList, Node* node, HashSet<Node*>& processedNodes, bool recursive)
 void NavigationMesh::CollectGeometries(Vector<GeometryInfo>& geometryList, Node* node, HashSet<Node*>& processedNodes, bool recursive)
@@ -819,7 +694,221 @@ void NavigationMesh::GetTileGeometry(NavigationBuildData& build, Vector<Geometry
     }
     }
 }
 }
 
 
-void NavigationMesh::ReleaseNavMesh()
+bool NavigationMesh::BuildTile(Vector<GeometryInfo>& geometryList, int x, int z)
+{
+    PROFILE(BuildNavigationMeshTile);
+    
+    float tileEdgeLength = (float)tileSize_ * cellSize_;
+    
+    BoundingBox tileBoundingBox(Vector3(
+        boundingBox_.min_.x_ + tileEdgeLength * (float)x,
+        boundingBox_.min_.y_,
+        boundingBox_.min_.z_ + tileEdgeLength * (float)z
+    ),
+    Vector3(
+        boundingBox_.min_.x_ + tileEdgeLength * (float)(x + 1),
+        boundingBox_.max_.y_,
+        boundingBox_.min_.z_ + tileEdgeLength * (float)(z + 1)
+    ));
+    
+    NavigationBuildData build;
+    
+    rcConfig cfg;
+    memset(&cfg, 0, sizeof cfg);
+    cfg.cs = cellSize_;
+    cfg.ch = cellHeight_;
+    cfg.walkableSlopeAngle = agentMaxSlope_;
+    cfg.walkableHeight = (int)ceilf(agentHeight_ / cfg.ch);
+    cfg.walkableClimb = (int)floorf(agentMaxClimb_ / cfg.ch);
+    cfg.walkableRadius = (int)ceilf(agentRadius_ / cfg.cs);
+    cfg.maxEdgeLen = (int)(edgeMaxLength_ / cellSize_);
+    cfg.maxSimplificationError = edgeMaxError_;
+    cfg.minRegionArea = (int)sqrtf(regionMinSize_);
+    cfg.mergeRegionArea = (int)sqrtf(regionMergeSize_);
+    cfg.maxVertsPerPoly = 6;
+    cfg.tileSize = tileSize_;
+    cfg.borderSize = cfg.walkableRadius + 3; // Add padding
+    cfg.width = cfg.tileSize + cfg.borderSize * 2;
+    cfg.height = cfg.tileSize + cfg.borderSize * 2;
+    cfg.detailSampleDist = detailSampleDistance_ < 0.9f ? 0.0f : cellSize_ * detailSampleDistance_;
+    cfg.detailSampleMaxError = cellHeight_ * detailSampleMaxError_;
+    
+    rcVcopy(cfg.bmin, &tileBoundingBox.min_.x_);
+    rcVcopy(cfg.bmax, &tileBoundingBox.max_.x_);
+    cfg.bmin[0] -= cfg.borderSize * cfg.cs;
+    cfg.bmin[2] -= cfg.borderSize * cfg.cs;
+    cfg.bmax[0] += cfg.borderSize * cfg.cs;
+    cfg.bmax[2] += cfg.borderSize * cfg.cs;
+    
+    BoundingBox expandedBox(*reinterpret_cast<Vector3*>(cfg.bmin), *reinterpret_cast<Vector3*>(cfg.bmax));
+    GetTileGeometry(build, geometryList, expandedBox);
+    
+    build.heightField_ = rcAllocHeightfield();
+    if (!build.heightField_)
+    {
+        LOGERROR("Could not allocate heightfield");
+        return false;
+    }
+    
+    if (!rcCreateHeightfield(build.ctx_, *build.heightField_, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs,
+        cfg.ch))
+    {
+        LOGERROR("Could not create heightfield");
+        return false;
+    }
+    
+    unsigned numTriangles = build.indices_.Size() / 3;
+    SharedArrayPtr<unsigned char> triAreas(new unsigned char[numTriangles]);
+    memset(triAreas.Get(), 0, numTriangles);
+    
+    rcMarkWalkableTriangles(build.ctx_, cfg.walkableSlopeAngle, &build.vertices_[0].x_, build.vertices_.Size(),
+        &build.indices_[0], numTriangles, triAreas.Get());
+    rcRasterizeTriangles(build.ctx_, &build.vertices_[0].x_, build.vertices_.Size(), &build.indices_[0],
+        triAreas.Get(), numTriangles, *build.heightField_, cfg.walkableClimb);
+    rcFilterLowHangingWalkableObstacles(build.ctx_, cfg.walkableClimb, *build.heightField_);
+    rcFilterLedgeSpans(build.ctx_, cfg.walkableHeight, cfg.walkableClimb, *build.heightField_);
+    rcFilterWalkableLowHeightSpans(build.ctx_, cfg.walkableHeight, *build.heightField_);
+    
+    build.compactHeightField_ = rcAllocCompactHeightfield();
+    if (!build.compactHeightField_)
+    {
+        LOGERROR("Could not allocate create compact heightfield");
+        return false;
+    }
+    if (!rcBuildCompactHeightfield(build.ctx_, cfg.walkableHeight, cfg.walkableClimb, *build.heightField_,
+        *build.compactHeightField_))
+    {
+        LOGERROR("Could not build compact heightfield");
+        return false;
+    }
+    if (!rcErodeWalkableArea(build.ctx_, cfg.walkableRadius, *build.compactHeightField_))
+    {
+        LOGERROR("Could not erode compact heightfield");
+        return false;
+    }
+    if (!rcBuildDistanceField(build.ctx_, *build.compactHeightField_))
+    {
+        LOGERROR("Could not build distance field");
+        return false;
+    }
+    if (!rcBuildRegions(build.ctx_, *build.compactHeightField_, cfg.borderSize, cfg.minRegionArea,
+        cfg.mergeRegionArea))
+    {
+        LOGERROR("Could not build regions");
+        return false;
+    }
+    
+    build.contourSet_ = rcAllocContourSet();
+    if (!build.contourSet_)
+    {
+        LOGERROR("Could not allocate contour set");
+        return false;
+    }
+    if (!rcBuildContours(build.ctx_, *build.compactHeightField_, cfg.maxSimplificationError, cfg.maxEdgeLen,
+        *build.contourSet_))
+    {
+        LOGERROR("Could not create contours");
+        return false;
+    }
+    
+    build.polyMesh_ = rcAllocPolyMesh();
+    if (!build.polyMesh_)
+    {
+        LOGERROR("Could not allocate poly mesh");
+        return false;
+    }
+    if (!rcBuildPolyMesh(build.ctx_, *build.contourSet_, cfg.maxVertsPerPoly, *build.polyMesh_))
+    {
+        LOGERROR("Could not triangulate contours");
+        return false;
+    }
+    
+    build.polyMeshDetail_ = rcAllocPolyMeshDetail();
+    if (!build.polyMeshDetail_)
+    {
+        LOGERROR("Could not allocate detail mesh");
+        return false;
+    }
+    if (!rcBuildPolyMeshDetail(build.ctx_, *build.polyMesh_, *build.compactHeightField_, cfg.detailSampleDist,
+        cfg.detailSampleMaxError, *build.polyMeshDetail_))
+    {
+        LOGERROR("Could not build detail mesh");
+        return false;
+    }
+    
+    // Set polygon flags
+    /// \todo Allow to define custom flags
+    for (int i = 0; i < build.polyMesh_->npolys; ++i)
+    {
+        if (build.polyMesh_->areas[i] == RC_WALKABLE_AREA)
+            build.polyMesh_->flags[i] = 0x1;
+    }
+    
+    unsigned char* navData = 0;
+    int navDataSize = 0;
+    
+    dtNavMeshCreateParams params;
+    memset(&params, 0, sizeof params);
+    params.verts = build.polyMesh_->verts;
+    params.vertCount = build.polyMesh_->nverts;
+    params.polys = build.polyMesh_->polys;
+    params.polyAreas = build.polyMesh_->areas;
+    params.polyFlags = build.polyMesh_->flags;
+    params.polyCount = build.polyMesh_->npolys;
+    params.nvp = build.polyMesh_->nvp;
+    params.detailMeshes = build.polyMeshDetail_->meshes;
+    params.detailVerts = build.polyMeshDetail_->verts;
+    params.detailVertsCount = build.polyMeshDetail_->nverts;
+    params.detailTris = build.polyMeshDetail_->tris;
+    params.detailTriCount = build.polyMeshDetail_->ntris;
+    params.walkableHeight = agentHeight_;
+    params.walkableRadius = agentRadius_;
+    params.walkableClimb = agentMaxClimb_;
+    params.tileX = x;
+    params.tileY = z;
+    rcVcopy(params.bmin, build.polyMesh_->bmin);
+    rcVcopy(params.bmax, build.polyMesh_->bmax);
+    params.cs = cfg.cs;
+    params.ch = cfg.ch;
+    params.buildBvTree = true;
+    
+    if (!dtCreateNavMeshData(&params, &navData, &navDataSize))
+    {
+        LOGERROR("Could not build navigation mesh tile data");
+        return false;
+    }
+    
+    // Remove previous tile (if any), then add new
+    navMesh_->removeTile(navMesh_->getTileRefAt(x, z, 0), 0, 0);
+    if (dtStatusFailed(navMesh_->addTile(navData, navDataSize, DT_TILE_FREE_DATA, 0, 0)))
+    {
+        LOGERROR("Failed to add navigation mesh tile");
+        dtFree(navData);
+        return false;
+    }
+    
+    return true;
+}
+
+bool NavigationMesh::InitializeQuery()
+{
+    navMeshQuery_ = dtAllocNavMeshQuery();
+    if (!navMeshQuery_)
+    {
+        LOGERROR("Could not create navigation mesh query");
+        return false;
+    }
+    
+    if (dtStatusFailed(navMeshQuery_->init(navMesh_, MAX_POLYS)))
+    {
+        LOGERROR("Could not init navigation mesh query");
+        return false;
+    }
+    
+    return true;
+}
+
+void NavigationMesh::ReleaseNavigationMesh()
 {
 {
     dtFreeNavMesh(navMesh_);
     dtFreeNavMesh(navMesh_);
     navMesh_ = 0;
     navMesh_ = 0;
@@ -829,6 +918,8 @@ void NavigationMesh::ReleaseNavMesh()
     
     
     numTilesX_ = 0;
     numTilesX_ = 0;
     numTilesZ_ = 0;
     numTilesZ_ = 0;
+    boundingBox_.min_ = boundingBox_.max_ = Vector3::ZERO;
+    boundingBox_.defined_ = false;
 }
 }
 
 
 }
 }

+ 29 - 16
Engine/Navigation/NavigationMesh.h

@@ -26,7 +26,6 @@
 #include "BoundingBox.h"
 #include "BoundingBox.h"
 #include "Component.h"
 #include "Component.h"
 #include "Matrix3x4.h"
 #include "Matrix3x4.h"
-#include "VectorBuffer.h"
 
 
 class dtNavMesh;
 class dtNavMesh;
 class dtNavMeshQuery;
 class dtNavMeshQuery;
@@ -93,8 +92,10 @@ public:
     void SetDetailSampleDistance(float distance);
     void SetDetailSampleDistance(float distance);
     /// Set detail sampling maximum error.
     /// Set detail sampling maximum error.
     void SetDetailSampleMaxError(float error);
     void SetDetailSampleMaxError(float error);
-    /// Rebuild the navigation data. Return true if successful.
+    /// Rebuild the navigation mesh. Return true if successful.
     bool Build();
     bool Build();
+    /// Rebuild part of the navigation mesh contained by the world-space bounding box. Return true if successful.
+    bool Build(const BoundingBox& boundingBox);
     /// Find a path between world space points. Return non-empty list of points if successful.
     /// Find a path between world space points. Return non-empty list of points if successful.
     void FindPath(PODVector<Vector3>& dest, const Vector3& start, const Vector3& end, const Vector3& extents = Vector3::ONE);
     void FindPath(PODVector<Vector3>& dest, const Vector3& start, const Vector3& end, const Vector3& extents = Vector3::ONE);
     
     
@@ -126,20 +127,40 @@ public:
     float GetDetailSampleMaxError() const { return detailSampleMaxError_; }
     float GetDetailSampleMaxError() const { return detailSampleMaxError_; }
     /// Return whether has been initialized with valid navigation data.
     /// Return whether has been initialized with valid navigation data.
     bool IsInitialized() const { return navMesh_ != 0; }
     bool IsInitialized() const { return navMesh_ != 0; }
+    /// Return local space bounding box of the navigation mesh.
+    const BoundingBox& GetBoundingBox() const { return boundingBox_; }
+    /// Return world space bounding box of the navigation mesh.
+    BoundingBox GetWorldBoundingBox() const;
+    /// Return number of tiles.
+    IntVector2 GetNumTiles() const { return IntVector2(numTilesX_, numTilesZ_); }
     
     
     /// Set navigation data attribute.
     /// Set navigation data attribute.
-    void SetNavigationDataAttr(const PODVector<unsigned char>& data);
+    void SetNavigationDataAttr(PODVector<unsigned char> data);
     /// Get navigation data attribute.
     /// Get navigation data attribute.
-    const PODVector<unsigned char>& GetNavigationDataAttr() const { return navigationDataAttr_.GetBuffer(); }
+    PODVector<unsigned char> GetNavigationDataAttr() const;
     
     
 private:
 private:
+    /// Collect geometry from under Navigable components.
+    void CollectGeometries(Vector<GeometryInfo>& geometryList);
     /// Visit nodes and collect navigable geometry.
     /// Visit nodes and collect navigable geometry.
     void CollectGeometries(Vector<GeometryInfo>& geometryList, Node* node, HashSet<Node*>& processedNodes, bool recursive);
     void CollectGeometries(Vector<GeometryInfo>& geometryList, Node* node, HashSet<Node*>& processedNodes, bool recursive);
     /// Get geometry data within a bounding box.
     /// Get geometry data within a bounding box.
     void GetTileGeometry(NavigationBuildData& build, Vector<GeometryInfo>& geometryList, BoundingBox& box);
     void GetTileGeometry(NavigationBuildData& build, Vector<GeometryInfo>& geometryList, BoundingBox& box);
-    /// Release the Detour navmesh and the query.
-    void ReleaseNavMesh();
+    /// Build one tile of the navigation mesh. Return true if successful.
+    bool BuildTile(Vector<GeometryInfo>& geometryList, int x, int z);
+    /// Initialize navigation mesh query. Return true if successful.
+    bool InitializeQuery();
+    /// Release the navigation mesh and the query.
+    void ReleaseNavigationMesh();
     
     
+    /// Detour navigation mesh.
+    dtNavMesh* navMesh_;
+    /// Detour navigation mesh query.
+    dtNavMeshQuery* navMeshQuery_;
+    /// Detour navigation mesh query filter.
+    dtQueryFilter* queryFilter_;
+    /// Temporary data for finding a path.
+    FindPathData* pathData_;
     /// Tile size.
     /// Tile size.
     int tileSize_;
     int tileSize_;
     /// Cell size.
     /// Cell size.
@@ -166,20 +187,12 @@ private:
     float detailSampleDistance_;
     float detailSampleDistance_;
     /// Detail sampling maximum error.
     /// Detail sampling maximum error.
     float detailSampleMaxError_;
     float detailSampleMaxError_;
-    /// Detour navmesh.
-    dtNavMesh* navMesh_;
     /// Number of tiles in X direction.
     /// Number of tiles in X direction.
     int numTilesX_;
     int numTilesX_;
     /// Number of tiles in Z direction.
     /// Number of tiles in Z direction.
     int numTilesZ_;
     int numTilesZ_;
-    /// Detour navmesh query.
-    dtNavMeshQuery* navMeshQuery_;
-    /// Detour navmesh query filter.
-    dtQueryFilter* queryFilter_;
-    /// Temporary data for finding a path.
-    FindPathData* pathData_;
-    /// Navigation data attribute. Contains the unmodified Recast data.
-    VectorBuffer navigationDataAttr_;
+    /// Whole navigation mesh bounding box.
+    BoundingBox boundingBox_;
 };
 };
 
 
 }
 }