Add streaming mode to C++ samples 15_Navigation and 39_CrowdNavigation . Fix using outdated navigation mesh data for streaming.
This commit is contained in:
parent
20d2133f13
commit
f84e9d1392
@ -48,7 +48,9 @@ URHO3D_DEFINE_APPLICATION_MAIN(Navigation)
|
||||
|
||||
Navigation::Navigation(Context* context) :
|
||||
Sample(context),
|
||||
drawDebug_(false)
|
||||
drawDebug_(false),
|
||||
useStreaming_(false),
|
||||
streamingDistance_(2)
|
||||
{
|
||||
}
|
||||
|
||||
@ -141,6 +143,8 @@ void Navigation::CreateScene()
|
||||
|
||||
// Create a NavigationMesh component to the scene root
|
||||
NavigationMesh* navMesh = scene_->CreateComponent<NavigationMesh>();
|
||||
// Set small tiles to show navigation mesh streaming
|
||||
navMesh->SetTileSize(32);
|
||||
// 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>();
|
||||
@ -185,6 +189,7 @@ void Navigation::CreateUI()
|
||||
"Use WASD keys to move, RMB to rotate view\n"
|
||||
"LMB to set destination, SHIFT+LMB to teleport\n"
|
||||
"MMB or O key to add or remove obstacles\n"
|
||||
"Tab to toggle navigation mesh streaming\n"
|
||||
"Space to toggle debug geometry"
|
||||
);
|
||||
instructionText->SetFont(cache->GetResource<Font>("Fonts/Anonymous Pro.ttf"), 15);
|
||||
@ -299,7 +304,7 @@ void Navigation::AddOrRemoveObject()
|
||||
Vector3 hitPos;
|
||||
Drawable* hitDrawable;
|
||||
|
||||
if (Raycast(250.0f, hitPos, hitDrawable))
|
||||
if (!useStreaming_ && Raycast(250.0f, hitPos, hitDrawable))
|
||||
{
|
||||
// The part of the navigation mesh we must update, which is the world bounding box of the associated
|
||||
// drawable component
|
||||
@ -390,6 +395,68 @@ void Navigation::FollowPath(float timeStep)
|
||||
}
|
||||
}
|
||||
|
||||
void Navigation::ToggleStreaming(bool enabled)
|
||||
{
|
||||
NavigationMesh* navMesh = scene_->GetComponent<NavigationMesh>();
|
||||
if (enabled)
|
||||
{
|
||||
int maxTiles = (2 * streamingDistance_ + 1) * (2 * streamingDistance_ + 1);
|
||||
BoundingBox boundingBox = navMesh->GetBoundingBox();
|
||||
SaveNavigationData();
|
||||
navMesh->Allocate(boundingBox, maxTiles);
|
||||
}
|
||||
else
|
||||
navMesh->Build();
|
||||
}
|
||||
|
||||
void Navigation::UpdateStreaming()
|
||||
{
|
||||
// Center the navigation mesh at the jack
|
||||
NavigationMesh* navMesh = scene_->GetComponent<NavigationMesh>();
|
||||
const IntVector2 jackTile = navMesh->GetTileIndex(jackNode_->GetWorldPosition());
|
||||
const IntVector2 numTiles = navMesh->GetNumTiles();
|
||||
const IntVector2 beginTile = VectorMax(IntVector2::ZERO, jackTile - IntVector2::ONE * streamingDistance_);
|
||||
const IntVector2 endTile = VectorMin(jackTile + IntVector2::ONE * streamingDistance_, numTiles - IntVector2::ONE);
|
||||
|
||||
// Remove tiles
|
||||
for (HashSet<IntVector2>::Iterator i = addedTiles_.Begin(); i != addedTiles_.End();)
|
||||
{
|
||||
const IntVector2 tileIdx = *i;
|
||||
if (beginTile.x_ <= tileIdx.x_ && tileIdx.x_ <= endTile.x_ && beginTile.y_ <= tileIdx.y_ && tileIdx.y_ <= endTile.y_)
|
||||
++i;
|
||||
else
|
||||
{
|
||||
navMesh->RemoveTile(tileIdx);
|
||||
i = addedTiles_.Erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (!navMesh->HasTile(tileIdx) && tileData_.Contains(tileIdx))
|
||||
{
|
||||
addedTiles_.Insert(tileIdx);
|
||||
navMesh->AddTile(tileData_[tileIdx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Navigation::SaveNavigationData()
|
||||
{
|
||||
NavigationMesh* navMesh = scene_->GetComponent<NavigationMesh>();
|
||||
tileData_.Clear();
|
||||
const IntVector2 numTiles = navMesh->GetNumTiles();
|
||||
for (int z = 0; z < numTiles.y_; ++z)
|
||||
for (int x = 0; x <= numTiles.x_; ++x)
|
||||
{
|
||||
const IntVector2 tileIdx = IntVector2(x, z);
|
||||
tileData_[tileIdx] = navMesh->GetTileData(tileIdx);
|
||||
}
|
||||
}
|
||||
|
||||
void Navigation::HandleUpdate(StringHash eventType, VariantMap& eventData)
|
||||
{
|
||||
using namespace Update;
|
||||
@ -402,6 +469,16 @@ void Navigation::HandleUpdate(StringHash eventType, VariantMap& eventData)
|
||||
|
||||
// Make Jack follow the Detour path
|
||||
FollowPath(timeStep);
|
||||
|
||||
// Update streaming
|
||||
Input* input = GetSubsystem<Input>();
|
||||
if (input->GetKeyPress(KEY_TAB))
|
||||
{
|
||||
useStreaming_ = !useStreaming_;
|
||||
ToggleStreaming(useStreaming_);
|
||||
}
|
||||
if (useStreaming_)
|
||||
UpdateStreaming();
|
||||
}
|
||||
|
||||
void Navigation::HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
|
||||
|
@ -146,6 +146,12 @@ private:
|
||||
bool Raycast(float maxDistance, Vector3& hitPos, Drawable*& hitDrawable);
|
||||
/// Make Jack follow the Detour path.
|
||||
void FollowPath(float timeStep);
|
||||
/// Toggle navigation mesh streaming.
|
||||
void ToggleStreaming(bool enabled);
|
||||
/// Update navigation mesh streaming.
|
||||
void UpdateStreaming();
|
||||
/// Save navigation data for streaming.
|
||||
void SaveNavigationData();
|
||||
/// Handle the logic update event.
|
||||
void HandleUpdate(StringHash eventType, VariantMap& eventData);
|
||||
/// Handle the post-render update event.
|
||||
@ -159,4 +165,12 @@ private:
|
||||
SharedPtr<Node> jackNode_;
|
||||
/// Flag for drawing debug geometry.
|
||||
bool drawDebug_;
|
||||
/// Flag for using navigation mesh streaming.
|
||||
bool useStreaming_;
|
||||
/// Streaming distance.
|
||||
int streamingDistance_;
|
||||
/// Tile data.
|
||||
HashMap<IntVector2, PODVector<unsigned char> > tileData_;
|
||||
/// Added tiles.
|
||||
HashSet<IntVector2> addedTiles_;
|
||||
};
|
||||
|
@ -55,6 +55,7 @@ URHO3D_DEFINE_APPLICATION_MAIN(CrowdNavigation)
|
||||
|
||||
CrowdNavigation::CrowdNavigation(Context* context) :
|
||||
Sample(context),
|
||||
streamingDistance_(2),
|
||||
drawDebug_(false)
|
||||
{
|
||||
}
|
||||
@ -135,6 +136,8 @@ void CrowdNavigation::CreateScene()
|
||||
|
||||
// Create a DynamicNavigationMesh component to the scene root
|
||||
DynamicNavigationMesh* navMesh = scene_->CreateComponent<DynamicNavigationMesh>();
|
||||
// Set small tiles to show navigation mesh streaming
|
||||
navMesh->SetTileSize(32);
|
||||
// Enable drawing debug geometry for obstacles and off-mesh connections
|
||||
navMesh->SetDrawObstacles(true);
|
||||
navMesh->SetDrawOffMeshConnections(true);
|
||||
@ -213,6 +216,7 @@ void CrowdNavigation::CreateUI()
|
||||
"LMB to set destination, SHIFT+LMB to spawn a Jack\n"
|
||||
"MMB or O key to add obstacles or remove obstacles/agents\n"
|
||||
"F5 to save scene, F7 to load\n"
|
||||
"Tab to toggle navigation mesh streaming\n"
|
||||
"Space to toggle debug geometry\n"
|
||||
"F12 to toggle this instruction text"
|
||||
);
|
||||
@ -473,6 +477,78 @@ void CrowdNavigation::MoveCamera(float timeStep)
|
||||
}
|
||||
}
|
||||
|
||||
void CrowdNavigation::ToggleStreaming(bool enabled)
|
||||
{
|
||||
DynamicNavigationMesh* navMesh = scene_->GetComponent<DynamicNavigationMesh>();
|
||||
if (enabled)
|
||||
{
|
||||
int maxTiles = (2 * streamingDistance_ + 1) * (2 * streamingDistance_ + 1);
|
||||
BoundingBox boundingBox = navMesh->GetBoundingBox();
|
||||
SaveNavigationData();
|
||||
navMesh->Allocate(boundingBox, maxTiles);
|
||||
}
|
||||
else
|
||||
navMesh->Build();
|
||||
}
|
||||
|
||||
void CrowdNavigation::UpdateStreaming()
|
||||
{
|
||||
// Center the navigation mesh at the crowd of jacks
|
||||
Vector3 averageJackPosition;
|
||||
if (Node* jackGroup = scene_->GetChild("Jacks"))
|
||||
{
|
||||
const unsigned numJacks = jackGroup->GetNumChildren();
|
||||
for (unsigned i = 0; i < numJacks; ++i)
|
||||
averageJackPosition += jackGroup->GetChild(i)->GetWorldPosition();
|
||||
averageJackPosition /= (float)numJacks;
|
||||
}
|
||||
|
||||
// Compute currently loaded area
|
||||
DynamicNavigationMesh* navMesh = scene_->GetComponent<DynamicNavigationMesh>();
|
||||
const IntVector2 jackTile = navMesh->GetTileIndex(averageJackPosition);
|
||||
const IntVector2 numTiles = navMesh->GetNumTiles();
|
||||
const IntVector2 beginTile = VectorMax(IntVector2::ZERO, jackTile - IntVector2::ONE * streamingDistance_);
|
||||
const IntVector2 endTile = VectorMin(jackTile + IntVector2::ONE * streamingDistance_, numTiles - IntVector2::ONE);
|
||||
|
||||
// Remove tiles
|
||||
for (HashSet<IntVector2>::Iterator i = addedTiles_.Begin(); i != addedTiles_.End();)
|
||||
{
|
||||
const IntVector2 tileIdx = *i;
|
||||
if (beginTile.x_ <= tileIdx.x_ && tileIdx.x_ <= endTile.x_ && beginTile.y_ <= tileIdx.y_ && tileIdx.y_ <= endTile.y_)
|
||||
++i;
|
||||
else
|
||||
{
|
||||
navMesh->RemoveTile(tileIdx);
|
||||
i = addedTiles_.Erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (!navMesh->HasTile(tileIdx) && tileData_.Contains(tileIdx))
|
||||
{
|
||||
addedTiles_.Insert(tileIdx);
|
||||
navMesh->AddTile(tileData_[tileIdx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CrowdNavigation::SaveNavigationData()
|
||||
{
|
||||
DynamicNavigationMesh* navMesh = scene_->GetComponent<DynamicNavigationMesh>();
|
||||
tileData_.Clear();
|
||||
const IntVector2 numTiles = navMesh->GetNumTiles();
|
||||
for (int z = 0; z < numTiles.y_; ++z)
|
||||
for (int x = 0; x <= numTiles.x_; ++x)
|
||||
{
|
||||
const IntVector2 tileIdx = IntVector2(x, z);
|
||||
tileData_[tileIdx] = navMesh->GetTileData(tileIdx);
|
||||
}
|
||||
}
|
||||
|
||||
void CrowdNavigation::HandleUpdate(StringHash eventType, VariantMap& eventData)
|
||||
{
|
||||
using namespace Update;
|
||||
@ -482,6 +558,17 @@ void CrowdNavigation::HandleUpdate(StringHash eventType, VariantMap& eventData)
|
||||
|
||||
// Move the camera, scale movement with time step
|
||||
MoveCamera(timeStep);
|
||||
|
||||
// Update streaming
|
||||
Input* input = GetSubsystem<Input>();
|
||||
if (input->GetKeyPress(KEY_TAB))
|
||||
{
|
||||
useStreaming_ = !useStreaming_;
|
||||
ToggleStreaming(useStreaming_);
|
||||
}
|
||||
if (useStreaming_)
|
||||
UpdateStreaming();
|
||||
|
||||
}
|
||||
|
||||
void CrowdNavigation::HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
|
||||
|
@ -152,6 +152,12 @@ private:
|
||||
void CreateMovingBarrels(DynamicNavigationMesh* navMesh);
|
||||
/// Utility function to raycast to the cursor position. Return true if hit.
|
||||
bool Raycast(float maxDistance, Vector3& hitPos, Drawable*& hitDrawable);
|
||||
/// Toggle navigation mesh streaming.
|
||||
void ToggleStreaming(bool enabled);
|
||||
/// Update navigation mesh streaming.
|
||||
void UpdateStreaming();
|
||||
/// Save navigation data for streaming.
|
||||
void SaveNavigationData();
|
||||
/// Handle the logic update event.
|
||||
void HandleUpdate(StringHash eventType, VariantMap& eventData);
|
||||
/// Handle the post-render update event.
|
||||
@ -163,6 +169,14 @@ private:
|
||||
/// Handle crowd agent formation.
|
||||
void HandleCrowdAgentFormation(StringHash eventType, VariantMap& eventData);
|
||||
|
||||
/// Flag for using navigation mesh streaming.
|
||||
bool useStreaming_;
|
||||
/// Streaming distance.
|
||||
int streamingDistance_;
|
||||
/// Tile data.
|
||||
HashMap<IntVector2, PODVector<unsigned char> > tileData_;
|
||||
/// Added tiles.
|
||||
HashSet<IntVector2> addedTiles_;
|
||||
/// Flag for drawing debug geometry.
|
||||
bool drawDebug_;
|
||||
};
|
||||
|
@ -119,15 +119,6 @@ void CreateScene()
|
||||
// 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
|
||||
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 the camera. Limit far clip distance to match the fog
|
||||
cameraNode = scene_.CreateChild("Camera");
|
||||
@ -273,7 +264,7 @@ void AddOrRemoveObject()
|
||||
Vector3 hitPos;
|
||||
Drawable@ hitDrawable;
|
||||
|
||||
if (Raycast(250.0f, hitPos, hitDrawable))
|
||||
if (!useStreaming && Raycast(250.0f, hitPos, hitDrawable))
|
||||
{
|
||||
// The part of the navigation mesh we must update, which is the world bounding box of the associated
|
||||
// drawable component
|
||||
@ -359,20 +350,21 @@ void FollowPath(float timeStep)
|
||||
}
|
||||
}
|
||||
|
||||
void SwitchStreaming(bool enabled)
|
||||
void ToggleStreaming(bool enabled)
|
||||
{
|
||||
NavigationMesh@ navMesh = scene_.GetComponent("NavigationMesh");
|
||||
if (enabled)
|
||||
{
|
||||
int maxTiles = (2 * STREAMING_DISTANCE + 1) * (2 * STREAMING_DISTANCE + 1);
|
||||
BoundingBox boundingBox = navMesh.boundingBox;
|
||||
SaveNavigationData();
|
||||
navMesh.Allocate(boundingBox, maxTiles);
|
||||
}
|
||||
else
|
||||
navMesh.Build();
|
||||
}
|
||||
|
||||
void StreamNavMesh()
|
||||
void UpdateStreaming()
|
||||
{
|
||||
NavigationMesh@ navMesh = scene_.GetComponent("NavigationMesh");
|
||||
|
||||
@ -409,6 +401,21 @@ void StreamNavMesh()
|
||||
}
|
||||
}
|
||||
|
||||
void SaveNavigationData()
|
||||
{
|
||||
NavigationMesh@ navMesh = scene_.GetComponent("NavigationMesh");
|
||||
navigationTilesData.Clear();
|
||||
navigationTilesIdx.Clear();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void HandleUpdate(StringHash eventType, VariantMap& eventData)
|
||||
{
|
||||
// Take the frame time step, which is stored as a float
|
||||
@ -424,10 +431,10 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
|
||||
if (input.keyPress[KEY_TAB])
|
||||
{
|
||||
useStreaming = !useStreaming;
|
||||
SwitchStreaming(useStreaming);
|
||||
ToggleStreaming(useStreaming);
|
||||
}
|
||||
if (useStreaming)
|
||||
StreamNavMesh();
|
||||
UpdateStreaming();
|
||||
}
|
||||
|
||||
void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
|
||||
|
@ -114,15 +114,6 @@ void CreateScene()
|
||||
// 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
|
||||
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.
|
||||
// Note that OffMeshConnections must be added before building the navMesh, but as we are adding Obstacles next, tiles will be automatically rebuilt.
|
||||
@ -433,20 +424,21 @@ void MoveCamera(float timeStep)
|
||||
}
|
||||
}
|
||||
|
||||
void SwitchStreaming(bool enabled)
|
||||
void ToggleStreaming(bool enabled)
|
||||
{
|
||||
DynamicNavigationMesh@ navMesh = scene_.GetComponent("DynamicNavigationMesh");
|
||||
if (enabled)
|
||||
{
|
||||
int maxTiles = (2 * STREAMING_DISTANCE + 1) * (2 * STREAMING_DISTANCE + 1);
|
||||
BoundingBox boundingBox = navMesh.boundingBox;
|
||||
SaveNavigationData();
|
||||
navMesh.Allocate(boundingBox, maxTiles);
|
||||
}
|
||||
else
|
||||
navMesh.Build();
|
||||
}
|
||||
|
||||
void StreamNavMesh()
|
||||
void UpdateStreaming()
|
||||
{
|
||||
DynamicNavigationMesh@ navMesh = scene_.GetComponent("DynamicNavigationMesh");
|
||||
|
||||
@ -493,6 +485,21 @@ void StreamNavMesh()
|
||||
}
|
||||
}
|
||||
|
||||
void SaveNavigationData()
|
||||
{
|
||||
DynamicNavigationMesh@ navMesh = scene_.GetComponent("DynamicNavigationMesh");
|
||||
navigationTilesData.Clear();
|
||||
navigationTilesIdx.Clear();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void HandleUpdate(StringHash eventType, VariantMap& eventData)
|
||||
{
|
||||
// Take the frame time step, which is stored as a float
|
||||
@ -505,10 +512,10 @@ void HandleUpdate(StringHash eventType, VariantMap& eventData)
|
||||
if (input.keyPress[KEY_TAB])
|
||||
{
|
||||
useStreaming = !useStreaming;
|
||||
SwitchStreaming(useStreaming);
|
||||
ToggleStreaming(useStreaming);
|
||||
}
|
||||
if (useStreaming)
|
||||
StreamNavMesh();
|
||||
UpdateStreaming();
|
||||
}
|
||||
|
||||
void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
|
||||
|
Loading…
Reference in New Issue
Block a user