#include "common/oneworldenv.h"
#include "LabEngine/base/kaimfileopener.h"
namespace
{
class CrowdDispersionBot : public LabEngine::LabEngineObject
{
public:
{
m_gameKitTraverseLogicData = *KY_NEW Kaim::GameKitTraverseLogicData();
m_gameKitTraverseLogicData->EnableCrowdDispersion(crowdDispersionNavTagMap, checkDistance, 1.f);
m_currentDestIdx = destIdx;
botInitConfig.
m_startPosition = m_pos[startIdx] + startOffset;
m_navBot->Init(botInitConfig, botConfig);
m_navBot->SetBotTraverseLogicUserData(m_gameKitTraverseLogicData);
m_navBot->AddToDatabase();
m_navBot->ComputeNewPathToDestination(m_pos[m_currentDestIdx]);
}
void UpdateLogic()
{
if (m_navBot->IsAddedToWorld() == false)
return;
if (m_navBot->GetFollowedPath() !=
KY_NULL && m_navBot->HasReachedPosition(m_pos[m_currentDestIdx], 1.f))
{
m_gameKitTraverseLogicData->GetCrowdDispersionData()->OnBotLogicComputePathToBrandNewDestination();
m_navBot->RemoveFromDatabase();
return;
}
{
if (m_navBot->IsComputingNewPath() == false)
return;
m_navBot->ComputeNewPathToDestination(m_pos[m_currentDestIdx]);
return;
}
{
if (m_navBot->IsComputingNewPath() == true)
m_navBot->CancelAsyncPathComputation();
}
bool needRecomputation = botOutput.
IsPathRecomputationNeeded();
KY_TODO("Why not to put such kind of small independent brick of logic into the GamKit")
if (needRecomputation == false && m_navBot->GetTrajectory())
{
if (trajectory->
GetTrajectoryMode() == Kaim::TrajectoryMode_Shortcut)
{
const Kaim::PositionOnLivePath& targetOnLivePath = trajectory->
m_shortcutTrajectory->m_targetOnPath.m_positionOnLivePath;
{
(targetOnLivePath.
GetPosition() - m_navBot->GetPosition()).GetNormalized2d(targetDir);
needRecomputation = true;
}
}
}
if (needRecomputation == true)
{
m_gameKitTraverseLogicData->GetCrowdDispersionData()->OnBotLogicRecomputePathToSimilarDestination();
if (m_navBot->IsComputingNewPath() == true)
return;
m_navBot->ComputeNewPathToDestination(m_pos[m_currentDestIdx]);
return;
}
}
void UpdatePhysics()
{
if (m_navBot->IsAddedToWorld() == false)
return;
const KyFloat32 simulationTimeInSeconds = 0.016f;
{
return;
}
if ((m_navBot->GetNavTrianglePtr().IsValid() == true) &&
{
Kaim::Vec3f position = m_navBot->ComputeMoveOnNavMeshWithTraverseLogic<Kaim::DefaultTraverseLogic>(botOutput.m_outputVelocity, simulationTimeInSeconds);
m_navBot->SetPositionAndVelocityAndFrontDirection(position, simulationTimeInSeconds);
return;
}
m_navBot->SetPosition(m_navBot->GetPosition() + botOutput.m_outputVelocity * simulationTimeInSeconds);
m_navBot->SetVelocityAndFrontDirection(botOutput.m_outputVelocity);
}
void SendVisualDebug()
{
m_gameKitTraverseLogicData->GetCrowdDispersionData()->SendVisualDebug(displayList);
displayList.PushTextVarg(textPos,
Kaim::VisualColor::Red,
"%.3f", 1.f / m_gameKitTraverseLogicData->GetCrowdDispersionData()->m_input.m_speed);
}
public:
Kaim::Ptr<Kaim::Bot> m_navBot;
Kaim::Ptr<Kaim::GameKitTraverseLogicData> m_gameKitTraverseLogicData;
};
class GraphEdges
{
public:
GraphEdges() {}
: m_a(a), m_b(b), m_maxBotOnEdge(1)
{
m_navTag.SetLayerID(0);
m_navTag.SetSmartObjectID(navTagID);
}
};
class CrowdDispersionEnv : public OneWorldEnv
{
public:
CrowdDispersionEnv()
: m_enableVisualDebug(true)
, m_botColumn(10)
, m_botLine(10)
, m_tagVolumeMaxCount(4)
, m_crowdDispersionCheckDistance(20.f)
, m_denseGraph(false)
, m_noTimeSlicing(false)
{
m_botConfig.m_shortcutTrajectoryConfig.m_advancedConfig.m_forwardMaxDistance = 5.f;
m_botConfig.m_shortcutTrajectoryConfig.m_advancedConfig.m_backwardMaxDistance = 0.5f;
m_botConfig.m_avoidanceConfig.m_minimalTimeToCollision = 0.2f;
m_botConfig.m_avoidanceConfig.m_enableSlowingDown = true;
m_botConfig.m_avoidanceConfig.m_stopCollisionTime = 0.1f;
m_botConfig.m_avoidanceConfig.m_stopWaitTime = 0.1f;
m_botConfig.m_avoidanceConfig.m_waitPassageTimeLimit = 0.1f;
m_botConfig.m_avoidanceConfig.m_utilityFunctionConfig.m_avoidanceAngleSpan = 180;
m_botConfig.m_avoidanceConfig.m_utilityFunctionConfig.m_avoidanceSampleCount = 10;
Kaim::Alg::Random::SeedRandom(0);
}
virtual bool OnBeginEnv()
{
if (OneWorldEnv::OnBeginEnv() == false)
return false;
GetWorld()->SetNumberOfFramesForShortcutTrajectoryPeriodicUpdate(1);
return true;
}
{
std::stringstream strstream;
strstream << GetName() << "_Down_" << destIdx;
KT_LOG_TITLE_BEGIN(strstream.str().c_str());
ScopedCleanUpWorld scopedCleanUpWorld(this);
const KyUInt32 botCount = m_botColumn * m_botLine;
for (
KyUInt32 i = 0; i < m_botColumn; ++i)
{
for (
KyUInt32 j = 0; j < m_botLine; ++j)
{
bots[i+(j*m_botColumn)] = *KY_NEW CrowdDispersionBot(&m_crowdDispersionNavTagMap, database,
0, destIdx, m_crowdDispersionCheckDistance, m_botConfig, m_navigationProfileId);
bots[i+(j*m_botColumn)]->m_gameKitTraverseLogicData->GetCrowdDispersionData()->m_triggerAvoidance = m_botConfig.m_enableAvoidance;
}
}
return UpdateTest(bots, strstream);
}
{
std::stringstream strstream;
strstream << GetName() << "_Up_";
for (
KyUInt32 i = 0; i < startIdxCount; ++i)
strstream << startIdx[i] << "-";
KT_LOG_TITLE_BEGIN(strstream.str().c_str());
ScopedCleanUpWorld scopedCleanUpWorld(this);
const KyUInt32 botCount = m_botColumn * m_botLine * startIdxCount;
for (
KyUInt32 s = 0; s < startIdxCount; ++s)
{
for (
KyUInt32 i = 0; i < m_botColumn; ++i)
{
for (
KyUInt32 j = 0; j < m_botLine; ++j)
{
bots[(s*m_botColumn*m_botLine)+i+(j*m_botColumn)] = *KY_NEW CrowdDispersionBot(&m_crowdDispersionNavTagMap, database,
startIdx[s], 0, m_crowdDispersionCheckDistance, m_botConfig, m_navigationProfileId);
bots[(s*m_botColumn*m_botLine)+i+(j*m_botColumn)]->m_gameKitTraverseLogicData->GetCrowdDispersionData()->m_triggerAvoidance = m_botConfig.m_enableAvoidance;
}
}
}
return UpdateTest(bots, strstream);
}
private:
bool UpdateTest(
Kaim::KyArray<Kaim::Ptr<CrowdDispersionBot> >& bots, std::stringstream& strstream)
{
strstream << ".VisualDebug";
if (m_enableVisualDebug)
{
visualDebugServerConfig.
SetLocalFileSizeLimit(1024);
visualDebugServer = StartVisualDebugUsingLocalFile(strstream.str(), visualDebugServerConfig);
}
while(--frameCount && GetDefaultDatabase()->GetBotsCount() != 0)
{
if (visualDebugServer)
for (
KyUInt32 i = 0; i < bots.GetCount(); ++i)
bots[i]->UpdateLogic();
VisualDebugNavTagCountHashMap("AfterLogic");
for (
KyUInt32 i = 0; i < bots.GetCount(); ++i)
bots[i]->SendVisualDebug();
m_world->Update();
for (
KyUInt32 i = 0; i < bots.GetCount(); ++i)
bots[i]->UpdatePhysics();
}
KT_RETURN_IF(frameCount == 0, false);
return true;
}
{
if (m_noTimeSlicing == true)
{
for (
KyUInt32 i = 0; i < GetWorld()->GetQueryQueueArraysCount(); ++i)
{
for (
KyUInt32 j = 0; j < queryQueueArray->
GetQueueCount(); ++j)
{
}
}
}
m_crowdDispersionNavTagMap.Clear();
m_graphEdges.Clear();
m_volumeExtents.Clear();
KT_RETURN_IF(LoadAndAddNavData(
"GeneratedNavData/lengthenStairs/lengthenStairs.NavData") ==
KY_NULL,
KY_ERROR);
if (m_denseGraph == false)
{
m_graphEdges.PushBack(GraphEdges(
Kaim::Vec3f(18.0f, 9.0f, 0.0f),
Kaim::Vec3f(21.0f, 9.0f, 5.0f), 10, genMetrics));
m_graphEdges.PushBack(GraphEdges(
Kaim::Vec3f(18.0f, 16.0f, 0.0f),
Kaim::Vec3f(21.0f, 16.0f, 5.0f), 11, genMetrics));
m_graphEdges.PushBack(GraphEdges(
Kaim::Vec3f(13.0f, 16.0f, 0.0f),
Kaim::Vec3f(13.0f, 13.0f, 1.5f), 12, genMetrics));
m_graphEdges.PushBack(GraphEdges(
Kaim::Vec3f(13.0f, 9.0f, 0.0f),
Kaim::Vec3f(13.0f, 13.0f, 1.5f), 13, genMetrics));
m_graphEdges.PushBack(GraphEdges(
Kaim::Vec3f(17.0f, 16.0f, 0.0f),
Kaim::Vec3f(17.0f, 13.0f, 3.75f), 14, genMetrics));
m_graphEdges.PushBack(GraphEdges(
Kaim::Vec3f(17.0f, 9.0f, 0.0f),
Kaim::Vec3f(17.0f, 13.0f, 3.75f), 15, genMetrics));
m_graphEdges.PushBack(GraphEdges(
Kaim::Vec3f(22.0f, 6.0f, 5.0f),
Kaim::Vec3f(23.5f, 6.0f, 10.0f), 16, genMetrics));
m_graphEdges.PushBack(GraphEdges(
Kaim::Vec3f(22.0f, 12.0f, 5.0f),
Kaim::Vec3f(23.5f, 12.0f, 10.0f), 16, genMetrics));
m_graphEdges.PushBack(GraphEdges(
Kaim::Vec3f(22.0f, 19.0f, 5.0f),
Kaim::Vec3f(23.5f, 19.0f, 10.0f), 16, genMetrics));
}
else
{
for (
KyFloat32 y = 15.0f; y <= 19.0f; y += 0.5f)
for (
KyFloat32 y = 6.0f; y <= 10.0f; y += 0.5f)
for (
KyFloat32 y = 6.0f; y <= 19.0f; y += 0.5f)
m_graphEdges.PushBack(GraphEdges(
Kaim::Vec3f(22.0f, y, 5.0f),
Kaim::Vec3f(23.5f, y, 10.0f), i++, genMetrics));
for (
KyFloat32 x = 0.0f; x <= 4.0f; x += 0.5f)
{
m_graphEdges.PushBack(GraphEdges(
Kaim::Vec3f(13.0f+x, 9.0f, 0.0f),
Kaim::Vec3f(13.0f+x, 13.0f, 1.5f+x*0.5f), i++, genMetrics));
m_graphEdges.PushBack(GraphEdges(
Kaim::Vec3f(13.0f+x, 16.0f, 0.0f),
Kaim::Vec3f(13.0f+x, 13.0f, 1.5f+x*0.5f), i++, genMetrics));
}
}
m_navigationProfileId = GetWorld()->AddNavigationProfile(gameKitNavigationProfile);
Kaim::Ptr<Kaim::NavData> navDataForNavGraph = *KY_NEW
Kaim::NavData(database);
{
for (
KyUInt32 i = 0; i < m_graphEdges.GetCount(); ++i)
{
KyUInt32 navTagIdx = navGraphBuilder.
AddNavTag(&m_graphEdges[i].m_navTag.GetDynamicNavTag());
}
navGraphBuilder.
Build(navGraphBlobHandler);
m_crowdDispersionNavTagMap.Add(navDataForNavGraph);
}
{
gameKitNavTag.
SetLayerID(0);
gameKitNavTag.
SetSmartObjectID(0);
tagVolumeInitConfig.
m_world = GetWorld();
tagVolumeInitConfig.
m_points.PushBack(m_volumeExtents[0]);
tagVolumeInitConfig.m_points.PushBack(m_volumeExtents[1]);
tagVolumeInitConfig.m_points.PushBack(m_volumeExtents[2]);
tagVolumeInitConfig.m_points.PushBack(m_volumeExtents[3]);
tagVolumeInitConfig.
m_altitudeMin = -1.f;
tagVolumeInitConfig.
m_altitudeMax = 6.f;
tagVolumeInitConfig.
m_navTag = gameKitNavTag.
GetDynamicNavTag();
tagVolume->Init(tagVolumeInitConfig);
}
tagVolume->AddToWorld();
while (tagVolume->GetIntegrationStatus() != Kaim::TagVolume::IntegrationStatus_Integrated)
{
Update();
KT_RETURN_IF(--safeGuard == 0,
KY_ERROR);
}
m_crowdDispersionNavTagMap.Add(tagVolume, 0.5f, m_tagVolumeMaxCount);
}
void VisualDebugNavTagCountHashMap(const char listName[64])
{
for (
KyUInt32 i = 0; i < m_graphEdges.GetCount(); ++i)
{
Kaim::Ptr<Kaim::GameKitCrowdDispersionNavTag> navTagCrowdDispersion;
if (m_crowdDispersionNavTagMap.Get(navTagId, &navTagCrowdDispersion) == false)
continue;
Kaim::Vec3f textPos((m_graphEdges[i].m_a + m_graphEdges[i].m_b) * 0.5f); textPos.
z = 10.f;
std::stringstream strStream;
for (
KyUInt32 i = 0; i < navTagCrowdDispersion->GetTimeIntervalSize(); ++i)
strStream << navTagCrowdDispersion->GetCountAtTimeInterval(i) << ".";
}
navTagId.
m_layerIndex = 0;
Kaim::Ptr<Kaim::GameKitCrowdDispersionNavTag> navTagCrowdDispersion;
if (m_crowdDispersionNavTagMap.Get(navTagId, &navTagCrowdDispersion) == false)
return;
Kaim::Vec3f textPos((m_volumeExtents[0] + m_volumeExtents[1] + m_volumeExtents[2] + m_volumeExtents[3]) * 0.25f, 10.f);
std::stringstream strStream;
for (
KyUInt32 i = 0; i < navTagCrowdDispersion->GetTimeIntervalSize(); ++i)
strStream << navTagCrowdDispersion->GetCountAtTimeInterval(i) << ".";
}
std::string GetName()
{
std::stringstream strstream;
strstream << m_crowdDispersionCheckDistance << (m_denseGraph ? "_dense" : "_sparse");
strstream << (m_botConfig.m_enableAvoidance == false ? "_noAv" :
(m_botConfig.m_avoidanceConfig.m_contactSolverConfig.m_enableSoftContactSolver == false ? "_av" : "_avCS"));
strstream << (m_noTimeSlicing ? "_noTS" : "_TS");
strstream << "_" << m_botLine << "x" << m_botColumn;
return strstream.str();
}
public:
public:
bool m_enableVisualDebug;
bool m_denseGraph;
bool m_noTimeSlicing;
private:
};
#define TEST_ENV_CLASS CrowdDispersionEnv
TEST_ENV
{
m_noTimeSlicing = false;
m_denseGraph = true; m_crowdDispersionCheckDistance = 20.f;
m_botConfig.m_enableAvoidance = true; m_botConfig.m_avoidanceConfig.m_contactSolverConfig.m_enableSoftContactSolver = true;
}
TUTORIAL{ env.m_botLine = 2, env.m_botColumn = 2;
KyUInt32 starts[] = { 1 }; CHECK(env.TestUp(starts,
Kaim::ArraySize(starts))); }
TUTORIAL{ env.m_botLine = 4, env.m_botColumn = 5;
TUTORIAL
{
env.m_botLine = 10, env.m_botColumn = 10;
CHECK(env.TestDown(3));
}
}