|
@@ -13,6 +13,13 @@
|
|
|
|
|
|
|
|
const String INSTRUCTION("instructionText");
|
|
const String INSTRUCTION("instructionText");
|
|
|
|
|
|
|
|
|
|
+bool useStreaming = false;
|
|
|
|
|
+// Used for streaming only
|
|
|
|
|
+const int STREAMING_DISTANCE = 2;
|
|
|
|
|
+Array<VectorBuffer> navigationTilesData;
|
|
|
|
|
+Array<IntVector2> navigationTilesIdx;
|
|
|
|
|
+Array<IntVector2> addedTiles;
|
|
|
|
|
+
|
|
|
void Start()
|
|
void Start()
|
|
|
{
|
|
{
|
|
|
// Execute the common startup for samples
|
|
// Execute the common startup for samples
|
|
@@ -88,6 +95,8 @@ void CreateScene()
|
|
|
|
|
|
|
|
// Create a DynamicNavigationMesh component to the scene root
|
|
// Create a DynamicNavigationMesh component to the scene root
|
|
|
DynamicNavigationMesh@ navMesh = scene_.CreateComponent("DynamicNavigationMesh");
|
|
DynamicNavigationMesh@ navMesh = scene_.CreateComponent("DynamicNavigationMesh");
|
|
|
|
|
+ // Set small tiles to show navigation mesh streaming
|
|
|
|
|
+ navMesh.tileSize = 32;
|
|
|
// Enable drawing debug geometry for obstacles and off-mesh connections
|
|
// Enable drawing debug geometry for obstacles and off-mesh connections
|
|
|
navMesh.drawObstacles = true;
|
|
navMesh.drawObstacles = true;
|
|
|
navMesh.drawOffMeshConnections = true;
|
|
navMesh.drawOffMeshConnections = true;
|
|
@@ -105,6 +114,15 @@ void CreateScene()
|
|
|
// physics geometry from the scene nodes, as it often is simpler, but if it can not find any (like in this example)
|
|
// physics geometry from the scene nodes, as it often is simpler, but if it can not find any (like in this example)
|
|
|
// it will use renderable geometry instead
|
|
// it will use renderable geometry instead
|
|
|
navMesh.Build();
|
|
navMesh.Build();
|
|
|
|
|
+ // Save navigation data (used for streaming only).
|
|
|
|
|
+ IntVector2 numTiles = navMesh.numTiles;
|
|
|
|
|
+ for (int z = 0; z < numTiles.y; ++z)
|
|
|
|
|
+ for (int x = 0; x < numTiles.x; ++x)
|
|
|
|
|
+ {
|
|
|
|
|
+ IntVector2 idx(x, z);
|
|
|
|
|
+ navigationTilesData.Push(navMesh.GetTileData(idx));
|
|
|
|
|
+ navigationTilesIdx.Push(idx);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// Create an off-mesh connection to each box to make it climbable (tiny boxes are skipped). A connection is built from 2 nodes.
|
|
// 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.
|
|
// Note that OffMeshConnections must be added before building the navMesh, but as we are adding Obstacles next, tiles will be automatically rebuilt.
|
|
@@ -161,6 +179,7 @@ void CreateUI()
|
|
|
"LMB to set destination, SHIFT+LMB to spawn a Jack\n"
|
|
"LMB to set destination, SHIFT+LMB to spawn a Jack\n"
|
|
|
"MMB or O key to add obstacles or remove obstacles/agents\n"
|
|
"MMB or O key to add obstacles or remove obstacles/agents\n"
|
|
|
"F5 to save scene, F7 to load\n"
|
|
"F5 to save scene, F7 to load\n"
|
|
|
|
|
+ "Tab to toggle navigation mesh streaming\n"
|
|
|
"Space to toggle debug geometry\n"
|
|
"Space to toggle debug geometry\n"
|
|
|
"F12 to toggle this instruction text";
|
|
"F12 to toggle this instruction text";
|
|
|
instructionText.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15);
|
|
instructionText.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 15);
|
|
@@ -414,6 +433,66 @@ void MoveCamera(float timeStep)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+void SwitchStreaming(bool enabled)
|
|
|
|
|
+{
|
|
|
|
|
+ DynamicNavigationMesh@ navMesh = scene_.GetComponent("DynamicNavigationMesh");
|
|
|
|
|
+ if (enabled)
|
|
|
|
|
+ {
|
|
|
|
|
+ int maxTiles = (2 * STREAMING_DISTANCE + 1) * (2 * STREAMING_DISTANCE + 1);
|
|
|
|
|
+ BoundingBox boundingBox = navMesh.boundingBox;
|
|
|
|
|
+ navMesh.Allocate(boundingBox, maxTiles);
|
|
|
|
|
+ }
|
|
|
|
|
+ else
|
|
|
|
|
+ navMesh.Build();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+void StreamNavMesh()
|
|
|
|
|
+{
|
|
|
|
|
+ DynamicNavigationMesh@ navMesh = scene_.GetComponent("DynamicNavigationMesh");
|
|
|
|
|
+
|
|
|
|
|
+ // Center the navigation mesh at the crowd of jacks
|
|
|
|
|
+ Vector3 averageJackPosition;
|
|
|
|
|
+ Node@ jackGroup = scene_.GetChild("Jacks");
|
|
|
|
|
+ if (jackGroup !is null)
|
|
|
|
|
+ {
|
|
|
|
|
+ for (uint i = 0; i < jackGroup.numChildren; ++i)
|
|
|
|
|
+ averageJackPosition += jackGroup.children[i].worldPosition;
|
|
|
|
|
+ averageJackPosition /= jackGroup.numChildren;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Compute currently loaded area
|
|
|
|
|
+ IntVector2 jackTile = navMesh.GetTileIndex(averageJackPosition);
|
|
|
|
|
+ IntVector2 beginTile = VectorMax(IntVector2(0, 0), jackTile - IntVector2(1, 1) * STREAMING_DISTANCE);
|
|
|
|
|
+ IntVector2 endTile = VectorMin(jackTile + IntVector2(1, 1) * STREAMING_DISTANCE, navMesh.numTiles - IntVector2(1, 1));
|
|
|
|
|
+
|
|
|
|
|
+ // Remove tiles
|
|
|
|
|
+ for (uint i = 0; i < addedTiles.length;)
|
|
|
|
|
+ {
|
|
|
|
|
+ IntVector2 tileIdx = addedTiles[i];
|
|
|
|
|
+ if (beginTile.x <= tileIdx.x && tileIdx.x <= endTile.x && beginTile.y <= tileIdx.y && tileIdx.y <= endTile.y)
|
|
|
|
|
+ ++i;
|
|
|
|
|
+ else
|
|
|
|
|
+ {
|
|
|
|
|
+ addedTiles.Erase(i);
|
|
|
|
|
+ navMesh.RemoveTile(tileIdx);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Add tiles
|
|
|
|
|
+ for (int z = beginTile.y; z <= endTile.y; ++z)
|
|
|
|
|
+ for (int x = beginTile.x; x <= endTile.x; ++x)
|
|
|
|
|
+ {
|
|
|
|
|
+ const IntVector2 tileIdx(x, z);
|
|
|
|
|
+ int tileDataIdx = navigationTilesIdx.Find(tileIdx);
|
|
|
|
|
+ if (!navMesh.HasTile(tileIdx) && tileDataIdx != -1)
|
|
|
|
|
+ {
|
|
|
|
|
+ addedTiles.Push(tileIdx);
|
|
|
|
|
+ navMesh.AddTile(navigationTilesData[tileDataIdx]);
|
|
|
|
|
+ Print("Add tile " + tileIdx.ToString());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
void HandleUpdate(StringHash eventType, VariantMap& eventData)
|
|
void HandleUpdate(StringHash eventType, VariantMap& eventData)
|
|
|
{
|
|
{
|
|
|
// Take the frame time step, which is stored as a float
|
|
// Take the frame time step, which is stored as a float
|
|
@@ -421,6 +500,15 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
|
|
|
|
|
|
|
|
// Move the camera, scale movement with time step
|
|
// Move the camera, scale movement with time step
|
|
|
MoveCamera(timeStep);
|
|
MoveCamera(timeStep);
|
|
|
|
|
+
|
|
|
|
|
+ // Update streaming
|
|
|
|
|
+ if (input.keyPress[KEY_TAB])
|
|
|
|
|
+ {
|
|
|
|
|
+ useStreaming = !useStreaming;
|
|
|
|
|
+ SwitchStreaming(useStreaming);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (useStreaming)
|
|
|
|
|
+ StreamNavMesh();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
|
|
void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
|