2017-09-23 23:50:22 +02:00
|
|
|
#include "navigation_scene.h"
|
2017-09-24 00:16:10 +02:00
|
|
|
#include "animation/animation_scene.h"
|
2017-09-23 23:50:22 +02:00
|
|
|
#include "engine/array.h"
|
|
|
|
#include "engine/crc32.h"
|
2019-10-25 19:33:14 +02:00
|
|
|
#include "engine/crt.h"
|
2017-09-23 23:50:22 +02:00
|
|
|
#include "engine/engine.h"
|
|
|
|
#include "engine/log.h"
|
|
|
|
#include "engine/lumix.h"
|
2019-07-04 20:23:03 +02:00
|
|
|
#include "engine/os.h"
|
2017-09-23 23:50:22 +02:00
|
|
|
#include "engine/profiler.h"
|
2017-11-19 14:04:10 +01:00
|
|
|
#include "engine/reflection.h"
|
2020-02-21 22:09:11 +01:00
|
|
|
#include "engine/universe.h"
|
2020-12-01 00:17:00 +01:00
|
|
|
#include "imgui/IconsFontAwesome5.h"
|
2017-09-23 23:50:22 +02:00
|
|
|
#include "lua_script/lua_script_system.h"
|
|
|
|
#include "renderer/material.h"
|
|
|
|
#include "renderer/model.h"
|
|
|
|
#include "renderer/render_scene.h"
|
|
|
|
#include <DetourAlloc.h>
|
|
|
|
#include <DetourCrowd.h>
|
|
|
|
#include <DetourNavMesh.h>
|
|
|
|
#include <DetourNavMeshBuilder.h>
|
|
|
|
#include <DetourNavMeshQuery.h>
|
|
|
|
#include <Recast.h>
|
|
|
|
|
|
|
|
|
|
|
|
namespace Lumix
|
|
|
|
{
|
|
|
|
|
|
|
|
|
2020-12-01 00:17:00 +01:00
|
|
|
enum class NavigationSceneVersion : i32 {
|
2017-09-23 23:50:22 +02:00
|
|
|
LATEST
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2018-01-12 17:01:26 +01:00
|
|
|
static const ComponentType LUA_SCRIPT_TYPE = Reflection::getComponentType("lua_script");
|
2019-07-04 23:45:31 +02:00
|
|
|
static const ComponentType NAVMESH_ZONE_TYPE = Reflection::getComponentType("navmesh_zone");
|
2017-11-19 14:04:10 +01:00
|
|
|
static const ComponentType NAVMESH_AGENT_TYPE = Reflection::getComponentType("navmesh_agent");
|
2019-09-05 18:17:56 +02:00
|
|
|
static const ComponentType ANIMATOR_TYPE = Reflection::getComponentType("animator");
|
2017-09-23 23:50:22 +02:00
|
|
|
static const int CELLS_PER_TILE_SIDE = 256;
|
|
|
|
static const float CELL_SIZE = 0.3f;
|
|
|
|
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
struct RecastZone {
|
2019-07-05 13:16:39 +02:00
|
|
|
EntityRef entity;
|
2019-07-04 23:45:31 +02:00
|
|
|
NavmeshZone zone;
|
|
|
|
|
|
|
|
dtNavMeshQuery* navquery = nullptr;
|
|
|
|
rcPolyMeshDetail* detail_mesh = nullptr;
|
|
|
|
rcPolyMesh* polymesh = nullptr;
|
|
|
|
dtNavMesh* navmesh = nullptr;
|
|
|
|
rcCompactHeightfield* debug_compact_heightfield = nullptr;
|
|
|
|
rcHeightfield* debug_heightfield = nullptr;
|
|
|
|
rcContourSet* debug_contours = nullptr;
|
|
|
|
dtCrowd* crowd = nullptr;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-09-23 23:50:22 +02:00
|
|
|
struct Agent
|
|
|
|
{
|
|
|
|
enum Flags : u32
|
|
|
|
{
|
|
|
|
USE_ROOT_MOTION = 1 << 0,
|
|
|
|
GET_ROOT_MOTION_FROM_ANIM_CONTROLLER = 1 << 1
|
|
|
|
};
|
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
EntityPtr zone = INVALID_ENTITY;
|
2018-08-19 17:35:37 +02:00
|
|
|
EntityRef entity;
|
2017-09-23 23:50:22 +02:00
|
|
|
float radius;
|
|
|
|
float height;
|
|
|
|
int agent;
|
|
|
|
bool is_finished;
|
|
|
|
u32 flags = 0;
|
2019-07-05 13:16:39 +02:00
|
|
|
Vec3 root_motion = {0, 0, 0};
|
2017-09-23 23:50:22 +02:00
|
|
|
float speed = 0;
|
|
|
|
float yaw_diff = 0;
|
|
|
|
float stop_distance = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2020-02-21 22:09:11 +01:00
|
|
|
struct NavigationSceneImpl final : NavigationScene
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
|
|
|
NavigationSceneImpl(Engine& engine, IPlugin& system, Universe& universe, IAllocator& allocator)
|
|
|
|
: m_allocator(allocator)
|
|
|
|
, m_universe(universe)
|
|
|
|
, m_system(system)
|
|
|
|
, m_engine(engine)
|
|
|
|
, m_num_tiles_x(0)
|
|
|
|
, m_num_tiles_z(0)
|
|
|
|
, m_agents(m_allocator)
|
2019-07-04 23:45:31 +02:00
|
|
|
, m_zones(m_allocator)
|
2017-09-23 23:50:22 +02:00
|
|
|
, m_script_scene(nullptr)
|
|
|
|
, m_on_update(m_allocator)
|
|
|
|
{
|
|
|
|
setGeneratorParams(0.3f, 0.1f, 0.3f, 2.0f, 60.0f, 0.3f);
|
2020-01-07 19:17:48 +01:00
|
|
|
m_universe.entityTransformed().bind<&NavigationSceneImpl::onEntityMoved>(this);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
~NavigationSceneImpl()
|
|
|
|
{
|
2020-01-07 19:17:48 +01:00
|
|
|
m_universe.entityTransformed().unbind<&NavigationSceneImpl::onEntityMoved>(this);
|
2019-07-04 23:45:31 +02:00
|
|
|
for(RecastZone& zone : m_zones) {
|
|
|
|
clearNavmesh(zone);
|
|
|
|
}
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void clear() override
|
|
|
|
{
|
|
|
|
m_agents.clear();
|
2019-07-04 23:45:31 +02:00
|
|
|
m_zones.clear();
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-19 17:35:37 +02:00
|
|
|
void onEntityMoved(EntityRef entity)
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
2019-07-07 11:53:48 +02:00
|
|
|
auto iter = m_agents.find(entity);
|
2017-09-23 23:50:22 +02:00
|
|
|
if (!iter.isValid()) return;
|
2019-07-07 11:53:48 +02:00
|
|
|
if (m_moving_agent == entity) return;
|
2017-09-23 23:50:22 +02:00
|
|
|
if (iter.value().agent < 0) return;
|
|
|
|
const Agent& agent = iter.value();
|
2019-07-07 11:53:48 +02:00
|
|
|
RecastZone& zone = m_zones[(EntityRef)agent.zone];
|
|
|
|
|
2018-10-10 00:17:59 +02:00
|
|
|
const DVec3 pos = m_universe.getPosition(iter.key());
|
2019-07-07 11:53:48 +02:00
|
|
|
const dtCrowdAgent* dt_agent = zone.crowd->getAgent(agent.agent);
|
|
|
|
if ((pos - *(Vec3*)dt_agent->npos).squaredLength() > 0.1f) {
|
|
|
|
const Transform old_zone_tr = m_universe.getTransform(zone.entity);
|
|
|
|
const DVec3 target_pos = old_zone_tr.transform(*(Vec3*)dt_agent->targetPos);
|
2017-09-23 23:50:22 +02:00
|
|
|
float speed = dt_agent->params.maxSpeed;
|
2019-07-07 11:53:48 +02:00
|
|
|
zone.crowd->removeAgent(agent.agent);
|
|
|
|
addCrowdAgent(iter.value(), zone);
|
|
|
|
if (!agent.is_finished) {
|
2017-09-23 23:50:22 +02:00
|
|
|
navigate({entity.index}, target_pos, speed, agent.stop_distance);
|
|
|
|
}
|
2019-07-07 11:53:48 +02:00
|
|
|
}
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
void clearNavmesh(RecastZone& zone) {
|
|
|
|
dtFreeNavMeshQuery(zone.navquery);
|
|
|
|
rcFreePolyMeshDetail(zone.detail_mesh);
|
|
|
|
rcFreePolyMesh(zone.polymesh);
|
|
|
|
dtFreeNavMesh(zone.navmesh);
|
|
|
|
rcFreeCompactHeightfield(zone.debug_compact_heightfield);
|
|
|
|
rcFreeHeightField(zone.debug_heightfield);
|
|
|
|
rcFreeContourSet(zone.debug_contours);
|
|
|
|
dtFreeCrowd(zone.crowd);
|
|
|
|
zone.detail_mesh = nullptr;
|
|
|
|
zone.polymesh = nullptr;
|
|
|
|
zone.navquery = nullptr;
|
|
|
|
zone.navmesh = nullptr;
|
|
|
|
zone.debug_compact_heightfield = nullptr;
|
|
|
|
zone.debug_heightfield = nullptr;
|
|
|
|
zone.debug_contours = nullptr;
|
|
|
|
zone.crowd = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void rasterizeGeometry(const Transform& zone_tr, const AABB& aabb, rcContext& ctx, rcConfig& cfg, rcHeightfield& solid)
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
2019-07-04 23:45:31 +02:00
|
|
|
rasterizeMeshes(zone_tr, aabb, ctx, cfg, solid);
|
|
|
|
rasterizeTerrains(zone_tr, aabb, ctx, cfg, solid);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
void rasterizeTerrains(const Transform& zone_tr, const AABB& aabb, rcContext& ctx, rcConfig& cfg, rcHeightfield& solid)
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
|
|
|
PROFILE_FUNCTION();
|
2019-07-04 20:23:03 +02:00
|
|
|
const float walkable_threshold = cosf(degreesToRadians(60));
|
2017-09-23 23:50:22 +02:00
|
|
|
|
|
|
|
auto render_scene = static_cast<RenderScene*>(m_universe.getScene(crc32("renderer")));
|
|
|
|
if (!render_scene) return;
|
|
|
|
|
2018-08-19 17:35:37 +02:00
|
|
|
EntityPtr entity_ptr = render_scene->getFirstTerrain();
|
2019-07-05 11:32:37 +02:00
|
|
|
while (entity_ptr.isValid()) {
|
2018-08-19 17:35:37 +02:00
|
|
|
const EntityRef entity = (EntityRef)entity_ptr;
|
2019-07-05 11:32:37 +02:00
|
|
|
const Transform terrain_tr = m_universe.getTransform(entity);
|
|
|
|
const Transform to_zone = zone_tr.inverted() * terrain_tr;
|
|
|
|
const IVec2 res = render_scene->getTerrainResolution(entity);
|
2018-01-12 17:01:26 +01:00
|
|
|
float scaleXZ = render_scene->getTerrainXZScale(entity);
|
2019-07-05 11:32:37 +02:00
|
|
|
for (int j = 0; j < res.y; ++j) {
|
|
|
|
for (int i = 0; i < res.x; ++i) {
|
2017-09-23 23:50:22 +02:00
|
|
|
float x = i * scaleXZ;
|
|
|
|
float z = j * scaleXZ;
|
2019-07-05 11:32:37 +02:00
|
|
|
|
|
|
|
const float h0 = render_scene->getTerrainHeightAt(entity, x, z);
|
|
|
|
const Vec3 p0 = to_zone.transform(Vec3(x, h0, z)).toFloat();
|
2017-09-23 23:50:22 +02:00
|
|
|
|
|
|
|
x = (i + 1) * scaleXZ;
|
|
|
|
z = j * scaleXZ;
|
2019-07-05 11:32:37 +02:00
|
|
|
const float h1 = render_scene->getTerrainHeightAt(entity, x, z);
|
|
|
|
const Vec3 p1 = to_zone.transform(Vec3(x, h1, z)).toFloat();
|
2017-09-23 23:50:22 +02:00
|
|
|
|
|
|
|
x = (i + 1) * scaleXZ;
|
|
|
|
z = (j + 1) * scaleXZ;
|
2019-07-05 11:32:37 +02:00
|
|
|
const float h2 = render_scene->getTerrainHeightAt(entity, x, z);
|
|
|
|
const Vec3 p2 = to_zone.transform(Vec3(x, h2, z)).toFloat();
|
2017-09-23 23:50:22 +02:00
|
|
|
|
|
|
|
x = i * scaleXZ;
|
|
|
|
z = (j + 1) * scaleXZ;
|
2019-07-05 11:32:37 +02:00
|
|
|
const float h3 = render_scene->getTerrainHeightAt(entity, x, z);
|
|
|
|
const Vec3 p3 = to_zone.transform(Vec3(x, h3, z)).toFloat();
|
2017-09-23 23:50:22 +02:00
|
|
|
|
|
|
|
Vec3 n = crossProduct(p1 - p0, p0 - p2).normalized();
|
|
|
|
u8 area = n.y > walkable_threshold ? RC_WALKABLE_AREA : 0;
|
|
|
|
rcRasterizeTriangle(&ctx, &p0.x, &p1.x, &p2.x, area, solid);
|
|
|
|
|
|
|
|
n = crossProduct(p2 - p0, p0 - p3).normalized();
|
|
|
|
area = n.y > walkable_threshold ? RC_WALKABLE_AREA : 0;
|
|
|
|
rcRasterizeTriangle(&ctx, &p0.x, &p2.x, &p3.x, area, solid);
|
|
|
|
}
|
|
|
|
}
|
2018-08-19 17:35:37 +02:00
|
|
|
entity_ptr = render_scene->getNextTerrain(entity);
|
2019-07-05 11:32:37 +02:00
|
|
|
}
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
void rasterizeMeshes(const Transform& zone_tr, const AABB& aabb, rcContext& ctx, rcConfig& cfg, rcHeightfield& solid)
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
2019-07-04 23:45:31 +02:00
|
|
|
PROFILE_FUNCTION();
|
2019-07-04 20:23:03 +02:00
|
|
|
const float walkable_threshold = cosf(degreesToRadians(45));
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
const Transform inv_zone_tr = zone_tr.inverted();
|
|
|
|
|
2017-09-23 23:50:22 +02:00
|
|
|
auto render_scene = static_cast<RenderScene*>(m_universe.getScene(crc32("renderer")));
|
|
|
|
if (!render_scene) return;
|
|
|
|
|
|
|
|
u32 no_navigation_flag = Material::getCustomFlag("no_navigation");
|
|
|
|
u32 nonwalkable_flag = Material::getCustomFlag("nonwalkable");
|
2019-07-04 23:45:31 +02:00
|
|
|
for (EntityPtr model_instance = render_scene->getFirstModelInstance();
|
|
|
|
model_instance.isValid();
|
|
|
|
model_instance = render_scene->getNextModelInstance(model_instance))
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
2018-08-19 17:35:37 +02:00
|
|
|
const EntityRef entity = (EntityRef)model_instance;
|
|
|
|
auto* model = render_scene->getModelInstanceModel(entity);
|
2017-09-23 23:50:22 +02:00
|
|
|
if (!model) return;
|
|
|
|
ASSERT(model->isReady());
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
const Transform tr = m_universe.getTransform(entity);
|
2017-09-23 23:50:22 +02:00
|
|
|
AABB model_aabb = model->getAABB();
|
2019-07-04 23:45:31 +02:00
|
|
|
const Transform rel_tr = inv_zone_tr * tr;
|
|
|
|
Matrix mtx = rel_tr.rot.toMatrix();
|
|
|
|
mtx.setTranslation(rel_tr.pos.toFloat());
|
|
|
|
mtx.multiply3x3(rel_tr.scale);
|
2017-09-23 23:50:22 +02:00
|
|
|
model_aabb.transform(mtx);
|
|
|
|
if (!model_aabb.overlaps(aabb)) continue;
|
|
|
|
|
2020-10-28 19:29:22 +01:00
|
|
|
auto lod = model->getLODIndices()[0];
|
2019-07-04 23:45:31 +02:00
|
|
|
for (int mesh_idx = lod.from; mesh_idx <= lod.to; ++mesh_idx) {
|
2017-11-10 00:48:38 +01:00
|
|
|
Mesh& mesh = model->getMesh(mesh_idx);
|
|
|
|
bool is16 = mesh.areIndices16();
|
|
|
|
|
2017-09-23 23:50:22 +02:00
|
|
|
if (mesh.material->isCustomFlag(no_navigation_flag)) continue;
|
|
|
|
bool is_walkable = !mesh.material->isCustomFlag(nonwalkable_flag);
|
2017-11-10 00:48:38 +01:00
|
|
|
auto* vertices = &mesh.vertices[0];
|
2019-07-04 23:45:31 +02:00
|
|
|
if (is16) {
|
2020-05-01 16:14:00 +02:00
|
|
|
const u16* indices16 = (const u16*)mesh.indices.data();
|
|
|
|
for (i32 i = 0; i < (i32)mesh.indices.size() / 2; i += 3) {
|
2017-11-10 00:48:38 +01:00
|
|
|
Vec3 a = mtx.transformPoint(vertices[indices16[i]]);
|
|
|
|
Vec3 b = mtx.transformPoint(vertices[indices16[i + 1]]);
|
|
|
|
Vec3 c = mtx.transformPoint(vertices[indices16[i + 2]]);
|
2017-09-23 23:50:22 +02:00
|
|
|
|
|
|
|
Vec3 n = crossProduct(a - b, a - c).normalized();
|
|
|
|
u8 area = n.y > walkable_threshold && is_walkable ? RC_WALKABLE_AREA : 0;
|
|
|
|
rcRasterizeTriangle(&ctx, &a.x, &b.x, &c.x, area, solid);
|
|
|
|
}
|
|
|
|
}
|
2019-07-04 23:45:31 +02:00
|
|
|
else {
|
2020-05-01 16:14:00 +02:00
|
|
|
const u32* indices32 = (const u32*)mesh.indices.data();
|
|
|
|
for (i32 i = 0; i < (i32)mesh.indices.size() / 4; i += 3) {
|
2017-11-10 00:48:38 +01:00
|
|
|
Vec3 a = mtx.transformPoint(vertices[indices32[i]]);
|
|
|
|
Vec3 b = mtx.transformPoint(vertices[indices32[i + 1]]);
|
|
|
|
Vec3 c = mtx.transformPoint(vertices[indices32[i + 2]]);
|
2017-09-23 23:50:22 +02:00
|
|
|
|
|
|
|
Vec3 n = crossProduct(a - b, a - c).normalized();
|
|
|
|
u8 area = n.y > walkable_threshold && is_walkable ? RC_WALKABLE_AREA : 0;
|
|
|
|
rcRasterizeTriangle(&ctx, &a.x, &b.x, &c.x, area, solid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-07-04 23:45:31 +02:00
|
|
|
}
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void onPathFinished(const Agent& agent)
|
|
|
|
{
|
|
|
|
if (!m_script_scene) return;
|
|
|
|
|
2018-01-12 17:01:26 +01:00
|
|
|
if (!m_universe.hasComponent(agent.entity, LUA_SCRIPT_TYPE)) return;
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2018-01-12 17:01:26 +01:00
|
|
|
for (int i = 0, c = m_script_scene->getScriptCount(agent.entity); i < c; ++i)
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
2018-01-12 17:01:26 +01:00
|
|
|
auto* call = m_script_scene->beginFunctionCall(agent.entity, i, "onPathFinished");
|
2017-09-23 23:50:22 +02:00
|
|
|
if (!call) continue;
|
|
|
|
|
|
|
|
m_script_scene->endFunctionCall();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-19 17:35:37 +02:00
|
|
|
bool isFinished(EntityRef entity) override
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
2018-01-12 17:01:26 +01:00
|
|
|
return m_agents[entity].is_finished;
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-19 17:35:37 +02:00
|
|
|
float getAgentSpeed(EntityRef entity) override
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
2018-01-12 17:01:26 +01:00
|
|
|
return m_agents[entity].speed;
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-19 17:35:37 +02:00
|
|
|
float getAgentYawDiff(EntityRef entity) override
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
2018-01-12 17:01:26 +01:00
|
|
|
return m_agents[entity].yaw_diff;
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
void setAgentRootMotion(EntityRef entity, const Vec3& root_motion) override
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
2018-01-12 17:01:26 +01:00
|
|
|
m_agents[entity].root_motion = root_motion;
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
void update(RecastZone& zone, float time_delta) {
|
|
|
|
if (!zone.crowd) return;
|
|
|
|
zone.crowd->update(time_delta, nullptr);
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
const Transform inv_tr = m_universe.getTransform(zone.entity).inverted();
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
for (auto& agent : m_agents) {
|
2017-09-23 23:50:22 +02:00
|
|
|
if (agent.agent < 0) continue;
|
2019-07-05 13:16:39 +02:00
|
|
|
if (agent.zone != zone.entity) continue;
|
|
|
|
|
|
|
|
const dtCrowdAgent* dt_agent = zone.crowd->getAgent(agent.agent);
|
2019-07-12 23:52:18 +02:00
|
|
|
//if (dt_agent->paused) continue;
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
const Vec3 pos = inv_tr.transform(m_universe.getPosition(agent.entity)).toFloat();
|
|
|
|
const Quat rot = m_universe.getRotation(agent.entity);
|
|
|
|
const Vec3 diff = *(Vec3*)dt_agent->npos - pos;
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
const Vec3 velocity = *(Vec3*)dt_agent->nvel;
|
2017-09-23 23:50:22 +02:00
|
|
|
agent.speed = diff.length() / time_delta;
|
|
|
|
agent.yaw_diff = 0;
|
2019-07-05 13:16:39 +02:00
|
|
|
if (velocity.squaredLength() > 0) {
|
2019-07-29 18:03:08 +02:00
|
|
|
float wanted_yaw = atan2f(velocity.x, velocity.z);
|
2017-09-23 23:50:22 +02:00
|
|
|
float current_yaw = rot.toEuler().y;
|
2019-07-04 20:23:03 +02:00
|
|
|
agent.yaw_diff = angleDiff(wanted_yaw, current_yaw);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
void update(float time_delta, bool paused) override {
|
|
|
|
PROFILE_FUNCTION();
|
2017-09-23 23:50:22 +02:00
|
|
|
if (paused) return;
|
2019-07-05 13:16:39 +02:00
|
|
|
|
|
|
|
for (RecastZone& zone : m_zones) {
|
|
|
|
update(zone, time_delta);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void lateUpdate(RecastZone& zone, float time_delta) {
|
|
|
|
if (!zone.crowd) return;
|
|
|
|
|
|
|
|
const Transform zone_tr = m_universe.getTransform(zone.entity);
|
|
|
|
const Transform inv_zone_tr = zone_tr.inverted();
|
2017-09-23 23:50:22 +02:00
|
|
|
|
|
|
|
static const u32 ANIMATION_HASH = crc32("animation");
|
|
|
|
auto* anim_scene = (AnimationScene*)m_universe.getScene(ANIMATION_HASH);
|
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
for (Agent& agent : m_agents) {
|
2017-09-23 23:50:22 +02:00
|
|
|
if (agent.agent < 0) continue;
|
2019-07-05 13:16:39 +02:00
|
|
|
if (agent.zone != zone.entity) continue;
|
|
|
|
|
|
|
|
const dtCrowdAgent* dt_agent = zone.crowd->getAgent(agent.agent);
|
2019-07-12 23:52:18 +02:00
|
|
|
//if (dt_agent->paused) continue;
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
DVec3 pos = m_universe.getPosition(agent.entity);
|
2017-09-23 23:50:22 +02:00
|
|
|
Quat rot = m_universe.getRotation(agent.entity);
|
2019-07-05 13:16:39 +02:00
|
|
|
if (agent.flags & Agent::GET_ROOT_MOTION_FROM_ANIM_CONTROLLER && anim_scene) {
|
2019-09-05 18:17:56 +02:00
|
|
|
if (anim_scene->getUniverse().hasComponent(agent.entity, ANIMATOR_TYPE)) {
|
|
|
|
LocalRigidTransform root_motion = anim_scene->getAnimatorRootMotion(agent.entity);
|
2019-07-05 13:16:39 +02:00
|
|
|
agent.root_motion = root_motion.pos;
|
2018-01-12 17:01:26 +01:00
|
|
|
//m_universe.setRotation(agent.entity, m_universe.getRotation(agent.entity) * root_motion.rot);
|
|
|
|
}
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
2019-07-05 13:16:39 +02:00
|
|
|
if (agent.flags & Agent::USE_ROOT_MOTION) {
|
|
|
|
*(Vec3*)dt_agent->npos = inv_zone_tr.transform(pos + rot.rotate(agent.root_motion)).toFloat();
|
|
|
|
agent.root_motion = Vec3::ZERO;
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
zone.crowd->doMove(time_delta);
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
for (auto& agent : m_agents) {
|
2017-09-23 23:50:22 +02:00
|
|
|
if (agent.agent < 0) continue;
|
2019-07-05 13:16:39 +02:00
|
|
|
if (agent.zone != zone.entity) continue;
|
|
|
|
|
|
|
|
const dtCrowdAgent* dt_agent = zone.crowd->getAgent(agent.agent);
|
2019-07-12 23:52:18 +02:00
|
|
|
//if (dt_agent->paused) continue;
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-07 11:53:48 +02:00
|
|
|
m_moving_agent = agent.entity;
|
2019-07-05 13:16:39 +02:00
|
|
|
m_universe.setPosition(agent.entity, zone_tr.transform(*(Vec3*)dt_agent->npos));
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
if ((agent.flags & Agent::USE_ROOT_MOTION) == 0) {
|
2017-09-23 23:50:22 +02:00
|
|
|
Vec3 vel = *(Vec3*)dt_agent->nvel;
|
|
|
|
vel.y = 0;
|
|
|
|
float len = vel.length();
|
2019-07-05 13:16:39 +02:00
|
|
|
if (len > 0) {
|
2017-09-23 23:50:22 +02:00
|
|
|
vel *= 1 / len;
|
|
|
|
float angle = atan2f(vel.x, vel.z);
|
|
|
|
Quat wanted_rot(Vec3(0, 1, 0), angle);
|
|
|
|
Quat old_rot = m_universe.getRotation(agent.entity);
|
2019-09-05 18:17:56 +02:00
|
|
|
Quat new_rot = nlerp(wanted_rot, old_rot, 0.90f);
|
2017-09-23 23:50:22 +02:00
|
|
|
m_universe.setRotation(agent.entity, new_rot);
|
|
|
|
}
|
|
|
|
}
|
2019-07-05 13:16:39 +02:00
|
|
|
else if (agent.flags & Agent::GET_ROOT_MOTION_FROM_ANIM_CONTROLLER && anim_scene) {
|
2019-09-05 18:17:56 +02:00
|
|
|
if (anim_scene->getUniverse().hasComponent(agent.entity, ANIMATOR_TYPE)) {
|
|
|
|
LocalRigidTransform root_motion = anim_scene->getAnimatorRootMotion(agent.entity);
|
2018-01-12 17:01:26 +01:00
|
|
|
m_universe.setRotation(agent.entity, m_universe.getRotation(agent.entity) * root_motion.rot);
|
|
|
|
}
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
if (dt_agent->ncorners == 0 && dt_agent->targetState != DT_CROWDAGENT_TARGET_REQUESTING) {
|
|
|
|
if (!agent.is_finished) {
|
|
|
|
zone.crowd->resetMoveTarget(agent.agent);
|
2017-09-23 23:50:22 +02:00
|
|
|
agent.is_finished = true;
|
|
|
|
onPathFinished(agent);
|
|
|
|
}
|
|
|
|
}
|
2019-07-05 13:16:39 +02:00
|
|
|
else if (dt_agent->ncorners == 1 && agent.stop_distance > 0) {
|
2017-09-23 23:50:22 +02:00
|
|
|
Vec3 diff = *(Vec3*)dt_agent->targetPos - *(Vec3*)dt_agent->npos;
|
2019-07-05 13:16:39 +02:00
|
|
|
if (diff.squaredLength() < agent.stop_distance * agent.stop_distance) {
|
|
|
|
zone.crowd->resetMoveTarget(agent.agent);
|
2017-09-23 23:50:22 +02:00
|
|
|
agent.is_finished = true;
|
|
|
|
onPathFinished(agent);
|
|
|
|
}
|
|
|
|
}
|
2019-07-05 13:16:39 +02:00
|
|
|
else {
|
2017-09-23 23:50:22 +02:00
|
|
|
agent.is_finished = false;
|
|
|
|
}
|
2019-07-07 11:53:48 +02:00
|
|
|
m_moving_agent = INVALID_ENTITY;
|
2019-07-05 13:16:39 +02:00
|
|
|
}
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
void lateUpdate(float time_delta, bool paused) override {
|
|
|
|
PROFILE_FUNCTION();
|
|
|
|
if (paused) return;
|
|
|
|
for (RecastZone& zone : m_zones) {
|
|
|
|
lateUpdate(zone, time_delta);
|
|
|
|
}
|
|
|
|
}
|
2017-09-23 23:50:22 +02:00
|
|
|
|
|
|
|
static float distancePtLine2d(const float* pt, const float* p, const float* q)
|
|
|
|
{
|
|
|
|
float pqx = q[0] - p[0];
|
|
|
|
float pqz = q[2] - p[2];
|
|
|
|
float dx = pt[0] - p[0];
|
|
|
|
float dz = pt[2] - p[2];
|
|
|
|
float d = pqx*pqx + pqz*pqz;
|
|
|
|
float t = pqx*dx + pqz*dz;
|
|
|
|
if (d != 0) t /= d;
|
|
|
|
dx = p[0] + t*pqx - pt[0];
|
|
|
|
dz = p[2] + t*pqz - pt[2];
|
|
|
|
return dx*dx + dz*dz;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
static void drawPoly(RenderScene* render_scene, const Transform& tr, const dtMeshTile& tile, const dtPoly& poly)
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
|
|
|
const unsigned int ip = (unsigned int)(&poly - tile.polys);
|
|
|
|
const dtPolyDetail& pd = tile.detailMeshes[ip];
|
|
|
|
|
|
|
|
for (int i = 0; i < pd.triCount; ++i)
|
|
|
|
{
|
|
|
|
Vec3 v[3];
|
|
|
|
const unsigned char* t = &tile.detailTris[(pd.triBase + i) * 4];
|
|
|
|
for (int k = 0; k < 3; ++k)
|
|
|
|
{
|
|
|
|
if (t[k] < poly.vertCount)
|
|
|
|
{
|
|
|
|
v[k] = *(Vec3*)&tile.verts[poly.verts[t[k]] * 3];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
v[k] = *(Vec3*)&tile.detailVerts[(pd.vertBase + t[k] - poly.vertCount) * 3];
|
|
|
|
}
|
|
|
|
}
|
2019-07-04 23:45:31 +02:00
|
|
|
render_scene->addDebugTriangle(tr.transform(v[0]), tr.transform(v[1]), tr.transform(v[2]), 0xff00aaff);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for (int k = 0; k < pd.triCount; ++k)
|
|
|
|
{
|
|
|
|
const unsigned char* t = &tile.detailTris[(pd.triBase + k) * 4];
|
|
|
|
const float* tv[3];
|
|
|
|
for (int m = 0; m < 3; ++m)
|
|
|
|
{
|
|
|
|
if (t[m] < poly.vertCount)
|
|
|
|
tv[m] = &tile.verts[poly.verts[t[m]] * 3];
|
|
|
|
else
|
|
|
|
tv[m] = &tile.detailVerts[(pd.vertBase + (t[m] - poly.vertCount)) * 3];
|
|
|
|
}
|
|
|
|
for (int m = 0, n = 2; m < 3; n = m++)
|
|
|
|
{
|
|
|
|
if (((t[3] >> (n * 2)) & 0x3) == 0) continue; // Skip inner detail edges.
|
2019-07-04 23:45:31 +02:00
|
|
|
render_scene->addDebugLine(tr.transform(*(Vec3*)tv[n]), tr.transform(*(Vec3*)tv[m]), 0xff0000ff);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-19 17:35:37 +02:00
|
|
|
const dtCrowdAgent* getDetourAgent(EntityRef entity) override
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
2018-01-12 17:01:26 +01:00
|
|
|
auto iter = m_agents.find(entity);
|
2019-07-04 23:45:31 +02:00
|
|
|
if (!iter.isValid()) return nullptr;
|
|
|
|
|
2017-09-23 23:50:22 +02:00
|
|
|
const Agent& agent = iter.value();
|
|
|
|
if (agent.agent < 0) return nullptr;
|
2019-07-04 23:45:31 +02:00
|
|
|
if (!agent.zone.isValid()) return nullptr;
|
|
|
|
|
|
|
|
auto zone_iter = m_zones.find((EntityRef)agent.zone);
|
|
|
|
if (!zone_iter.isValid()) return nullptr;
|
|
|
|
|
|
|
|
dtCrowd* crowd = zone_iter.value().crowd;
|
|
|
|
|
|
|
|
if (!crowd) return nullptr;
|
|
|
|
|
|
|
|
return crowd->getAgent(agent.agent);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-19 17:35:37 +02:00
|
|
|
void debugDrawPath(EntityRef entity) override
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
2019-07-05 17:05:19 +02:00
|
|
|
auto render_scene = static_cast<RenderScene*>(m_universe.getScene(crc32("renderer")));
|
2017-09-23 23:50:22 +02:00
|
|
|
if (!render_scene) return;
|
2019-07-05 17:05:19 +02:00
|
|
|
|
|
|
|
auto agent_iter = m_agents.find(entity);
|
|
|
|
if (!agent_iter.isValid()) return;
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-05 17:05:19 +02:00
|
|
|
const Agent& agent = agent_iter.value();
|
2017-09-23 23:50:22 +02:00
|
|
|
if (agent.agent < 0) return;
|
2019-07-05 17:05:19 +02:00
|
|
|
|
|
|
|
const RecastZone& zone = m_zones[(EntityRef)agent.zone];
|
|
|
|
if (!zone.crowd) return;
|
|
|
|
|
|
|
|
const Transform zone_tr = m_universe.getTransform(zone.entity);
|
|
|
|
const dtCrowdAgent* dt_agent = zone.crowd->getAgent(agent.agent);
|
2017-09-23 23:50:22 +02:00
|
|
|
|
|
|
|
const dtPolyRef* path = dt_agent->corridor.getPath();
|
|
|
|
const int npath = dt_agent->corridor.getPathCount();
|
2019-07-05 17:05:19 +02:00
|
|
|
for (int j = 0; j < npath; ++j) {
|
2017-09-23 23:50:22 +02:00
|
|
|
dtPolyRef ref = path[j];
|
|
|
|
const dtMeshTile* tile = nullptr;
|
|
|
|
const dtPoly* poly = nullptr;
|
2019-07-05 17:05:19 +02:00
|
|
|
if (dtStatusFailed(zone.navmesh->getTileAndPolyByRef(ref, &tile, &poly))) continue;
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-05 17:05:19 +02:00
|
|
|
drawPoly(render_scene, zone_tr, *tile, *poly);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Vec3 prev = *(Vec3*)dt_agent->npos;
|
2019-07-05 17:05:19 +02:00
|
|
|
for (int i = 0; i < dt_agent->ncorners; ++i) {
|
2017-09-23 23:50:22 +02:00
|
|
|
Vec3 tmp = *(Vec3*)&dt_agent->cornerVerts[i * 3];
|
2019-07-07 11:22:25 +02:00
|
|
|
render_scene->addDebugLine(zone_tr.transform(prev), zone_tr.transform(tmp), 0xffff0000);
|
2017-09-23 23:50:22 +02:00
|
|
|
prev = tmp;
|
|
|
|
}
|
2019-07-07 11:22:25 +02:00
|
|
|
render_scene->addDebugCross(zone_tr.transform(*(Vec3*)dt_agent->targetPos), 1.0f, 0xffffffff);
|
2019-07-05 17:05:19 +02:00
|
|
|
const Vec3 vel = *(Vec3*)dt_agent->vel;
|
|
|
|
const DVec3 pos = m_universe.getPosition(entity);
|
|
|
|
render_scene->addDebugLine(pos, pos + zone_tr.rot.rotate(vel), 0xff0000ff);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
bool hasDebugDrawData(EntityRef zone) const override
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
2019-07-04 23:45:31 +02:00
|
|
|
return m_zones[zone].debug_contours != nullptr;
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
void debugDrawContours(EntityRef zone_entity) override {
|
2017-09-23 23:50:22 +02:00
|
|
|
auto render_scene = static_cast<RenderScene*>(m_universe.getScene(crc32("renderer")));
|
|
|
|
if (!render_scene) return;
|
2019-07-04 23:45:31 +02:00
|
|
|
|
|
|
|
const RecastZone& zone = m_zones[zone_entity];
|
|
|
|
if (!zone.debug_contours) return;
|
|
|
|
|
|
|
|
const Transform tr = m_universe.getTransform(zone_entity);
|
2017-09-23 23:50:22 +02:00
|
|
|
|
|
|
|
Vec3 orig = m_debug_tile_origin;
|
2019-07-04 23:45:31 +02:00
|
|
|
float cs = zone.debug_contours->cs;
|
|
|
|
float ch = zone.debug_contours->ch;
|
|
|
|
for (int i = 0; i < zone.debug_contours->nconts; ++i) {
|
|
|
|
const rcContour& c = zone.debug_contours->conts[i];
|
2017-09-23 23:50:22 +02:00
|
|
|
|
|
|
|
if (c.nverts < 2) continue;
|
|
|
|
|
|
|
|
Vec3 first =
|
|
|
|
orig + Vec3((float)c.verts[0] * cs, (float)c.verts[1] * ch, (float)c.verts[2] * cs);
|
|
|
|
Vec3 prev = first;
|
2019-07-04 23:45:31 +02:00
|
|
|
for (int j = 1; j < c.nverts; ++j) {
|
2017-09-23 23:50:22 +02:00
|
|
|
const int* v = &c.verts[j * 4];
|
|
|
|
Vec3 cur = orig + Vec3((float)v[0] * cs, (float)v[1] * ch, (float)v[2] * cs);
|
2019-07-04 23:45:31 +02:00
|
|
|
render_scene->addDebugLine(tr.transform(prev), tr.transform(cur), i & 1 ? 0xffff00ff : 0xffff0000);
|
2017-09-23 23:50:22 +02:00
|
|
|
prev = cur;
|
|
|
|
}
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
render_scene->addDebugLine(tr.transform(prev), tr.transform(first), i & 1 ? 0xffff00ff : 0xffff0000);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
bool isNavmeshReady(EntityRef zone) const override { return m_zones[zone].navmesh != nullptr; }
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-05 20:08:51 +02:00
|
|
|
struct LoadCallback {
|
|
|
|
LoadCallback(NavigationSceneImpl& scene, EntityRef entity)
|
|
|
|
: scene(scene)
|
|
|
|
, entity(entity)
|
|
|
|
{}
|
|
|
|
|
|
|
|
void fileLoaded(u64 size, const u8* mem, bool success) {
|
|
|
|
auto iter = scene.m_zones.find(entity);
|
|
|
|
if (!iter.isValid()) {
|
|
|
|
LUMIX_DELETE(scene.m_allocator, this);
|
|
|
|
return;
|
|
|
|
}
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-05 20:08:51 +02:00
|
|
|
if (!success) {
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("Could not load navmesh");
|
2019-07-05 20:08:51 +02:00
|
|
|
LUMIX_DELETE(scene.m_allocator, this);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
RecastZone& zone = iter.value();
|
|
|
|
if (!scene.initNavmesh(zone)) {
|
|
|
|
LUMIX_DELETE(scene.m_allocator, this);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
InputMemoryStream file(mem, size);
|
|
|
|
file.read(&scene.m_num_tiles_x, sizeof(scene.m_num_tiles_x));
|
|
|
|
file.read(&scene.m_num_tiles_z, sizeof(scene.m_num_tiles_z));
|
|
|
|
dtNavMeshParams params;
|
|
|
|
file.read(¶ms, sizeof(params));
|
|
|
|
if (dtStatusFailed(zone.navmesh->init(¶ms))) {
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("Could not init Detour navmesh");
|
2019-07-05 20:08:51 +02:00
|
|
|
LUMIX_DELETE(scene.m_allocator, this);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (int j = 0; j < scene.m_num_tiles_z; ++j) {
|
|
|
|
for (int i = 0; i < scene.m_num_tiles_x; ++i) {
|
|
|
|
int data_size;
|
|
|
|
file.read(&data_size, sizeof(data_size));
|
|
|
|
u8* data = (u8*)dtAlloc(data_size, DT_ALLOC_PERM);
|
|
|
|
file.read(data, data_size);
|
|
|
|
if (dtStatusFailed(zone.navmesh->addTile(data, data_size, DT_TILE_FREE_DATA, 0, 0))) {
|
|
|
|
dtFree(data);
|
|
|
|
LUMIX_DELETE(scene.m_allocator, this);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!zone.crowd) scene.initCrowd(zone);
|
|
|
|
|
|
|
|
LUMIX_DELETE(scene.m_allocator, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
NavigationSceneImpl& scene;
|
|
|
|
EntityRef entity;
|
|
|
|
};
|
|
|
|
|
|
|
|
bool load(EntityRef zone_entity, const char* path) override {
|
|
|
|
RecastZone& zone = m_zones[zone_entity];
|
|
|
|
clearNavmesh(zone);
|
|
|
|
|
|
|
|
LoadCallback* lcb = LUMIX_NEW(m_allocator, LoadCallback)(*this, zone_entity);
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-04 20:23:03 +02:00
|
|
|
FileSystem::ContentCallback cb;
|
2020-01-07 19:17:48 +01:00
|
|
|
cb.bind<&LoadCallback::fileLoaded>(lcb);
|
2019-07-04 20:23:03 +02:00
|
|
|
FileSystem& fs = m_engine.getFileSystem();
|
2019-07-05 20:08:51 +02:00
|
|
|
return fs.getContent(Path(path), cb).isValid();
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
2019-07-05 20:08:51 +02:00
|
|
|
bool save(EntityRef zone_entity, const char* path) override {
|
|
|
|
RecastZone& zone = m_zones[zone_entity];
|
|
|
|
if (!zone.navmesh) return false;
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-04 20:23:03 +02:00
|
|
|
FileSystem& fs = m_engine.getFileSystem();
|
|
|
|
|
|
|
|
OS::OutputFile file;
|
2019-07-27 23:38:09 +02:00
|
|
|
if (!fs.open(path, Ref(file))) return false;
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-05 20:08:51 +02:00
|
|
|
bool success = file.write(&m_num_tiles_x, sizeof(m_num_tiles_x));
|
|
|
|
success = success && file.write(&m_num_tiles_z, sizeof(m_num_tiles_z));
|
|
|
|
const dtNavMeshParams* params = zone.navmesh->getParams();
|
|
|
|
success = success && file.write(params, sizeof(*params));
|
|
|
|
for (int j = 0; j < m_num_tiles_z; ++j) {
|
|
|
|
for (int i = 0; i < m_num_tiles_x; ++i) {
|
|
|
|
const auto* tile = zone.navmesh->getTileAt(i, j, 0);
|
|
|
|
success = success && file.write(&tile->dataSize, sizeof(tile->dataSize));
|
|
|
|
success = success && file.write(tile->data, tile->dataSize);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-05 20:08:51 +02:00
|
|
|
file.close();
|
|
|
|
return success;
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
void debugDrawHeightfield(EntityRef zone_entity) override {
|
2017-09-23 23:50:22 +02:00
|
|
|
auto render_scene = static_cast<RenderScene*>(m_universe.getScene(crc32("renderer")));
|
|
|
|
if (!render_scene) return;
|
2019-07-04 23:45:31 +02:00
|
|
|
|
|
|
|
const RecastZone& zone = m_zones[zone_entity];
|
|
|
|
if (!zone.debug_heightfield) return;
|
|
|
|
|
|
|
|
const Transform tr = m_universe.getTransform(zone_entity);
|
2017-09-23 23:50:22 +02:00
|
|
|
|
|
|
|
Vec3 orig = m_debug_tile_origin;
|
2019-07-04 23:45:31 +02:00
|
|
|
int width = zone.debug_heightfield->width;
|
2017-09-23 23:50:22 +02:00
|
|
|
float cell_height = 0.1f;
|
2019-07-04 23:45:31 +02:00
|
|
|
for(int z = 0; z < zone.debug_heightfield->height; ++z) {
|
|
|
|
for(int x = 0; x < width; ++x) {
|
2017-09-23 23:50:22 +02:00
|
|
|
float fx = orig.x + x * CELL_SIZE;
|
|
|
|
float fz = orig.z + z * CELL_SIZE;
|
2019-07-04 23:45:31 +02:00
|
|
|
const rcSpan* span = zone.debug_heightfield->spans[x + z * width];
|
|
|
|
while(span) {
|
2017-09-23 23:50:22 +02:00
|
|
|
Vec3 mins(fx, orig.y + span->smin * cell_height, fz);
|
|
|
|
Vec3 maxs(fx + CELL_SIZE, orig.y + span->smax * cell_height, fz + CELL_SIZE);
|
|
|
|
u32 color = span->area == 0 ? 0xffff0000 : 0xff00aaff;
|
2019-07-04 23:45:31 +02:00
|
|
|
render_scene->addDebugCubeSolid(tr.transform(mins), tr.transform(maxs), color);
|
|
|
|
render_scene->addDebugCube(tr.transform(mins), tr.transform(maxs), 0xffffFFFF);
|
2017-09-23 23:50:22 +02:00
|
|
|
span = span->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
void debugDrawCompactHeightfield(EntityRef zone_entity) override {
|
2017-09-23 23:50:22 +02:00
|
|
|
static const int MAX_CUBES = 0xffFF;
|
|
|
|
|
|
|
|
auto render_scene = static_cast<RenderScene*>(m_universe.getScene(crc32("renderer")));
|
|
|
|
if (!render_scene) return;
|
2019-07-04 23:45:31 +02:00
|
|
|
|
|
|
|
const RecastZone& zone = m_zones[zone_entity];
|
|
|
|
if (!zone.debug_compact_heightfield) return;
|
|
|
|
|
|
|
|
const Transform tr = m_universe.getTransform(zone_entity);
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
auto& chf = *zone.debug_compact_heightfield;
|
2017-09-23 23:50:22 +02:00
|
|
|
const float cs = chf.cs;
|
|
|
|
const float ch = chf.ch;
|
|
|
|
|
|
|
|
Vec3 orig = m_debug_tile_origin;
|
|
|
|
|
|
|
|
int rendered_cubes = 0;
|
2019-07-04 23:45:31 +02:00
|
|
|
for (int y = 0; y < chf.height; ++y) {
|
|
|
|
for (int x = 0; x < chf.width; ++x) {
|
2017-09-23 23:50:22 +02:00
|
|
|
float vx = orig.x + (float)x * cs;
|
|
|
|
float vz = orig.z + (float)y * cs;
|
|
|
|
|
|
|
|
const rcCompactCell& c = chf.cells[x + y * chf.width];
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
for (u32 i = c.index, ni = c.index + c.count; i < ni; ++i) {
|
2017-09-23 23:50:22 +02:00
|
|
|
float vy = orig.y + float(chf.spans[i].y) * ch;
|
2019-07-04 23:45:31 +02:00
|
|
|
render_scene->addDebugTriangle(tr.transform(Vec3(vx, vy, vz))
|
|
|
|
, tr.transform(Vec3(vx + cs, vy, vz + cs))
|
|
|
|
, tr.transform(Vec3(vx + cs, vy, vz))
|
|
|
|
, 0xffff00FF);
|
|
|
|
render_scene->addDebugTriangle(tr.transform(Vec3(vx, vy, vz))
|
|
|
|
, tr.transform(Vec3(vx, vy, vz + cs))
|
|
|
|
, tr.transform(Vec3(vx + cs, vy, vz + cs))
|
|
|
|
, 0xffff00FF);
|
2017-09-23 23:50:22 +02:00
|
|
|
++rendered_cubes;
|
|
|
|
if (rendered_cubes > MAX_CUBES) return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void drawPolyBoundaries(RenderScene* render_scene,
|
2019-07-04 23:45:31 +02:00
|
|
|
const Transform& tr,
|
2017-09-23 23:50:22 +02:00
|
|
|
const dtMeshTile& tile,
|
|
|
|
const unsigned int col,
|
|
|
|
bool inner)
|
|
|
|
{
|
|
|
|
static const float thr = 0.01f * 0.01f;
|
|
|
|
|
|
|
|
for (int i = 0; i < tile.header->polyCount; ++i)
|
|
|
|
{
|
|
|
|
const dtPoly* p = &tile.polys[i];
|
|
|
|
|
|
|
|
if (p->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) continue;
|
|
|
|
|
|
|
|
const dtPolyDetail* pd = &tile.detailMeshes[i];
|
|
|
|
|
|
|
|
for (int j = 0, nj = (int)p->vertCount; j < nj; ++j)
|
|
|
|
{
|
|
|
|
unsigned int c = col;
|
|
|
|
if (inner)
|
|
|
|
{
|
|
|
|
if (p->neis[j] == 0) continue;
|
|
|
|
if (p->neis[j] & DT_EXT_LINK)
|
|
|
|
{
|
|
|
|
bool con = false;
|
|
|
|
for (unsigned int k = p->firstLink; k != DT_NULL_LINK; k = tile.links[k].next)
|
|
|
|
{
|
|
|
|
if (tile.links[k].edge == j)
|
|
|
|
{
|
|
|
|
con = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (con)
|
|
|
|
c = 0xffffffff;
|
|
|
|
else
|
|
|
|
c = 0xff000000;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
c = 0xff004466;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (p->neis[j] != 0) continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const float* v0 = &tile.verts[p->verts[j] * 3];
|
|
|
|
const float* v1 = &tile.verts[p->verts[(j + 1) % nj] * 3];
|
|
|
|
|
|
|
|
// Draw detail mesh edges which align with the actual poly edge.
|
|
|
|
// This is really slow.
|
|
|
|
for (int k = 0; k < pd->triCount; ++k)
|
|
|
|
{
|
|
|
|
const unsigned char* t = &tile.detailTris[(pd->triBase + k) * 4];
|
|
|
|
const float* tv[3];
|
|
|
|
for (int m = 0; m < 3; ++m)
|
|
|
|
{
|
|
|
|
if (t[m] < p->vertCount)
|
|
|
|
tv[m] = &tile.verts[p->verts[t[m]] * 3];
|
|
|
|
else
|
|
|
|
tv[m] = &tile.detailVerts[(pd->vertBase + (t[m] - p->vertCount)) * 3];
|
|
|
|
}
|
|
|
|
for (int m = 0, n = 2; m < 3; n = m++)
|
|
|
|
{
|
|
|
|
if (((t[3] >> (n * 2)) & 0x3) == 0) continue; // Skip inner detail edges.
|
|
|
|
if (distancePtLine2d(tv[n], v0, v1) < thr && distancePtLine2d(tv[m], v0, v1) < thr)
|
|
|
|
{
|
2019-07-04 23:45:31 +02:00
|
|
|
render_scene->addDebugLine(tr.transform(*(Vec3*)tv[n] + Vec3(0, 0.5f, 0))
|
|
|
|
, tr.transform(*(Vec3*)tv[m] + Vec3(0, 0.5f, 0))
|
|
|
|
, c);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-05 11:32:37 +02:00
|
|
|
static void drawTilePortal(RenderScene* render_scene, const Transform& zone_tr, const dtMeshTile& tile) {
|
2017-09-23 23:50:22 +02:00
|
|
|
const float padx = 0.04f;
|
|
|
|
const float pady = tile.header->walkableClimb;
|
|
|
|
|
2019-07-05 11:32:37 +02:00
|
|
|
for (int side = 0; side < 8; ++side) {
|
2017-09-23 23:50:22 +02:00
|
|
|
unsigned short m = DT_EXT_LINK | (unsigned short)side;
|
|
|
|
|
2019-07-05 11:32:37 +02:00
|
|
|
for (int i = 0; i < tile.header->polyCount; ++i) {
|
2017-09-23 23:50:22 +02:00
|
|
|
dtPoly* poly = &tile.polys[i];
|
|
|
|
|
|
|
|
const int nv = poly->vertCount;
|
2019-07-05 11:32:37 +02:00
|
|
|
for (int j = 0; j < nv; ++j) {
|
2017-09-23 23:50:22 +02:00
|
|
|
if (poly->neis[j] != m) continue;
|
|
|
|
|
|
|
|
const float* va = &tile.verts[poly->verts[j] * 3];
|
|
|
|
const float* vb = &tile.verts[poly->verts[(j + 1) % nv] * 3];
|
|
|
|
|
2019-07-05 11:32:37 +02:00
|
|
|
if (side == 0 || side == 4) {
|
2017-09-23 23:50:22 +02:00
|
|
|
unsigned int col = side == 0 ? 0xff0000aa : 0xff00aaaa;
|
|
|
|
|
|
|
|
const float x = va[0] + ((side == 0) ? -padx : padx);
|
|
|
|
|
2019-07-05 11:32:37 +02:00
|
|
|
render_scene->addDebugLine(zone_tr.transform(Vec3(x, va[1] - pady, va[2])), zone_tr.transform(Vec3(x, va[1] + pady, va[2])), col);
|
|
|
|
render_scene->addDebugLine(zone_tr.transform(Vec3(x, va[1] + pady, va[2])), zone_tr.transform(Vec3(x, vb[1] + pady, vb[2])), col);
|
|
|
|
render_scene->addDebugLine(zone_tr.transform(Vec3(x, vb[1] + pady, vb[2])), zone_tr.transform(Vec3(x, vb[1] - pady, vb[2])), col);
|
|
|
|
render_scene->addDebugLine(zone_tr.transform(Vec3(x, vb[1] - pady, vb[2])), zone_tr.transform(Vec3(x, va[1] - pady, va[2])), col);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
2019-07-05 11:32:37 +02:00
|
|
|
else if (side == 2 || side == 6) {
|
2017-09-23 23:50:22 +02:00
|
|
|
unsigned int col = side == 2 ? 0xff00aa00 : 0xffaaaa00;
|
|
|
|
|
|
|
|
const float z = va[2] + ((side == 2) ? -padx : padx);
|
|
|
|
|
2019-07-05 11:32:37 +02:00
|
|
|
render_scene->addDebugLine(zone_tr.transform(Vec3(va[0], va[1] - pady, z)), zone_tr.transform(Vec3(va[0], va[1] + pady, z)), col);
|
|
|
|
render_scene->addDebugLine(zone_tr.transform(Vec3(va[0], va[1] + pady, z)), zone_tr.transform(Vec3(vb[0], vb[1] + pady, z)), col);
|
|
|
|
render_scene->addDebugLine(zone_tr.transform(Vec3(vb[0], vb[1] + pady, z)), zone_tr.transform(Vec3(vb[0], vb[1] - pady, z)), col);
|
|
|
|
render_scene->addDebugLine(zone_tr.transform(Vec3(vb[0], vb[1] - pady, z)), zone_tr.transform(Vec3(va[0], va[1] - pady, z)), col);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-07-05 11:32:37 +02:00
|
|
|
}
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
void debugDrawNavmesh(EntityRef zone_entity, const DVec3& world_pos, bool inner_boundaries, bool outer_boundaries, bool portals) override
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
2019-07-04 23:45:31 +02:00
|
|
|
const RecastZone& zone = m_zones[zone_entity];
|
2019-07-05 11:04:03 +02:00
|
|
|
if (!zone.navmesh) return;
|
2019-07-04 23:45:31 +02:00
|
|
|
|
|
|
|
const Transform tr = m_universe.getTransform(zone_entity);
|
|
|
|
const Vec3 pos = tr.inverted().transform(world_pos).toFloat();
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
const Vec3 min = -zone.zone.extents;
|
|
|
|
const Vec3 max = zone.zone.extents;
|
|
|
|
if (pos.x > max.x || pos.x < min.x || pos.z > max.z || pos.z < min.z) return;
|
|
|
|
|
|
|
|
int x = int((pos.x - min.x + (1 + m_config.borderSize) * m_config.cs) / (CELLS_PER_TILE_SIDE * CELL_SIZE));
|
|
|
|
int z = int((pos.z - min.z + (1 + m_config.borderSize) * m_config.cs) / (CELLS_PER_TILE_SIDE * CELL_SIZE));
|
|
|
|
const dtMeshTile* tile = zone.navmesh->getTileAt(x, z, 0);
|
2019-07-05 11:32:37 +02:00
|
|
|
if (!tile) return;
|
|
|
|
|
2017-09-23 23:50:22 +02:00
|
|
|
auto render_scene = static_cast<RenderScene*>(m_universe.getScene(crc32("renderer")));
|
|
|
|
if (!render_scene) return;
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
for (int i = 0; i < tile->header->polyCount; ++i) {
|
2017-09-23 23:50:22 +02:00
|
|
|
const dtPoly* p = &tile->polys[i];
|
|
|
|
if (p->getType() == DT_POLYTYPE_OFFMESH_CONNECTION) continue;
|
2019-07-04 23:45:31 +02:00
|
|
|
drawPoly(render_scene, tr, *tile, *p);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
if (outer_boundaries) drawPolyBoundaries(render_scene, tr, *tile, 0xffff0000, false);
|
|
|
|
if (inner_boundaries) drawPolyBoundaries(render_scene, tr, *tile, 0xffff0000, true);
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
if (portals) drawTilePortal(render_scene, tr, *tile);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void stopGame() override
|
|
|
|
{
|
2019-07-05 13:16:39 +02:00
|
|
|
for (RecastZone& zone : m_zones) {
|
|
|
|
if (zone.crowd) {
|
|
|
|
for (Agent& agent : m_agents) {
|
|
|
|
if (agent.zone == zone.entity) {
|
|
|
|
zone.crowd->removeAgent(agent.agent);
|
|
|
|
agent.agent = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dtFreeCrowd(zone.crowd);
|
|
|
|
zone.crowd = nullptr;
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
2019-07-05 13:16:39 +02:00
|
|
|
}
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void startGame() override
|
|
|
|
{
|
|
|
|
auto* scene = m_universe.getScene(crc32("lua_script"));
|
|
|
|
m_script_scene = static_cast<LuaScriptScene*>(scene);
|
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
for (RecastZone& zone : m_zones) {
|
|
|
|
if (zone.navmesh && !zone.crowd) initCrowd(zone);
|
|
|
|
}
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
bool initCrowd(RecastZone& zone) {
|
|
|
|
ASSERT(!zone.crowd);
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
zone.crowd = dtAllocCrowd();
|
|
|
|
if (!zone.crowd->init(1000, 4.0f, zone.navmesh)) {
|
|
|
|
dtFreeCrowd(zone.crowd);
|
|
|
|
zone.crowd = nullptr;
|
2017-09-23 23:50:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
2019-07-05 13:16:39 +02:00
|
|
|
|
|
|
|
const Transform inv_zone_tr = m_universe.getTransform(zone.entity).inverted();
|
|
|
|
const Vec3 min = -zone.zone.extents;
|
|
|
|
const Vec3 max = zone.zone.extents;
|
|
|
|
|
|
|
|
for (auto iter = m_agents.begin(), end = m_agents.end(); iter != end; ++iter) {
|
2017-09-23 23:50:22 +02:00
|
|
|
Agent& agent = iter.value();
|
2019-07-05 13:16:39 +02:00
|
|
|
if (agent.zone.isValid()) continue;
|
|
|
|
|
|
|
|
const Vec3 pos = inv_zone_tr.transform(m_universe.getPosition(agent.entity)).toFloat();
|
|
|
|
if (pos.x > min.x && pos.y > min.y && pos.z > min.z
|
|
|
|
&& pos.x < max.x && pos.y < max.y && pos.z < max.z)
|
|
|
|
{
|
|
|
|
agent.zone = zone.entity;
|
|
|
|
addCrowdAgent(agent, zone);
|
|
|
|
}
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
RecastZone* getZone(const Agent& agent) {
|
|
|
|
if (!agent.zone.isValid()) return nullptr;
|
|
|
|
return &m_zones[(EntityRef)agent.zone];
|
|
|
|
}
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
void cancelNavigation(EntityRef entity) override {
|
|
|
|
auto iter = m_agents.find(entity);
|
2017-09-23 23:50:22 +02:00
|
|
|
if (iter == m_agents.end()) return;
|
|
|
|
|
|
|
|
Agent& agent = iter.value();
|
|
|
|
if (agent.agent < 0) return;
|
2019-07-05 13:16:39 +02:00
|
|
|
|
|
|
|
RecastZone* zone = getZone(agent);
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
if (zone) {
|
|
|
|
zone->crowd->resetMoveTarget(agent.agent);
|
|
|
|
}
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-19 17:35:37 +02:00
|
|
|
void setActorActive(EntityRef entity, bool active) override
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
2019-07-04 23:45:31 +02:00
|
|
|
/*if (!m_crowd) return;
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2018-01-12 17:01:26 +01:00
|
|
|
auto iter = m_agents.find(entity);
|
2017-09-23 23:50:22 +02:00
|
|
|
if (iter == m_agents.end()) return;
|
|
|
|
|
|
|
|
Agent& agent = iter.value();
|
|
|
|
if (agent.agent < 0) return;
|
|
|
|
|
|
|
|
dtCrowdAgent* dt_agent = m_crowd->getEditableAgent(agent.agent);
|
2019-07-04 23:45:31 +02:00
|
|
|
if (dt_agent) dt_agent->paused = !active;*/
|
|
|
|
// TOOD
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
bool navigate(EntityRef entity, const DVec3& world_dest, float speed, float stop_distance) override
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
|
|
|
auto iter = m_agents.find(entity);
|
|
|
|
if (iter == m_agents.end()) return false;
|
2019-07-05 13:16:39 +02:00
|
|
|
|
2017-09-23 23:50:22 +02:00
|
|
|
Agent& agent = iter.value();
|
|
|
|
if (agent.agent < 0) return false;
|
2019-07-05 13:16:39 +02:00
|
|
|
if (!agent.zone.isValid()) return false;
|
|
|
|
|
|
|
|
RecastZone& zone = m_zones[(EntityRef)agent.zone];
|
|
|
|
|
|
|
|
if (!zone.navquery) return false;
|
|
|
|
if (!zone.crowd) return false;
|
|
|
|
|
2017-09-23 23:50:22 +02:00
|
|
|
dtPolyRef end_poly_ref;
|
|
|
|
dtQueryFilter filter;
|
|
|
|
static const float ext[] = { 1.0f, 20.0f, 1.0f };
|
2019-07-05 13:16:39 +02:00
|
|
|
|
|
|
|
const Transform zone_tr = m_universe.getTransform(zone.entity);
|
|
|
|
const Vec3 dest = zone_tr.inverted().transform(world_dest).toFloat();
|
|
|
|
|
|
|
|
zone.navquery->findNearestPoly(&dest.x, ext, &filter, &end_poly_ref, 0);
|
|
|
|
dtCrowdAgentParams params = zone.crowd->getAgent(agent.agent)->params;
|
2017-09-23 23:50:22 +02:00
|
|
|
params.maxSpeed = speed;
|
2019-07-05 13:16:39 +02:00
|
|
|
zone.crowd->updateAgentParameters(agent.agent, ¶ms);
|
|
|
|
if (zone.crowd->requestMoveTarget(agent.agent, end_poly_ref, &dest.x)) {
|
2017-09-23 23:50:22 +02:00
|
|
|
agent.stop_distance = stop_distance;
|
|
|
|
agent.is_finished = false;
|
|
|
|
}
|
2019-07-05 13:16:39 +02:00
|
|
|
else {
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("requestMoveTarget failed");
|
2017-09-23 23:50:22 +02:00
|
|
|
agent.is_finished = true;
|
|
|
|
}
|
2019-07-05 13:16:39 +02:00
|
|
|
return !agent.is_finished;
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void setGeneratorParams(float cell_size,
|
|
|
|
float cell_height,
|
|
|
|
float agent_radius,
|
|
|
|
float agent_height,
|
|
|
|
float walkable_angle,
|
|
|
|
float max_climb) override
|
|
|
|
{
|
|
|
|
static const float DETAIL_SAMPLE_DIST = 6;
|
|
|
|
static const float DETAIL_SAMPLE_MAX_ERROR = 1;
|
|
|
|
|
|
|
|
m_config.cs = cell_size;
|
|
|
|
m_config.ch = cell_height;
|
|
|
|
m_config.walkableSlopeAngle = walkable_angle;
|
|
|
|
m_config.walkableHeight = (int)(agent_height / m_config.ch + 0.99f);
|
|
|
|
m_config.walkableClimb = (int)(max_climb / m_config.ch);
|
|
|
|
m_config.walkableRadius = (int)(agent_radius / m_config.cs + 0.99f);
|
|
|
|
m_config.maxEdgeLen = (int)(12 / m_config.cs);
|
|
|
|
m_config.maxSimplificationError = 1.3f;
|
|
|
|
m_config.minRegionArea = 8 * 8;
|
|
|
|
m_config.mergeRegionArea = 20 * 20;
|
|
|
|
m_config.maxVertsPerPoly = 6;
|
|
|
|
m_config.detailSampleDist = DETAIL_SAMPLE_DIST < 0.9f ? 0 : CELL_SIZE * DETAIL_SAMPLE_DIST;
|
|
|
|
m_config.detailSampleMaxError = m_config.ch * DETAIL_SAMPLE_MAX_ERROR;
|
|
|
|
m_config.borderSize = m_config.walkableRadius + 3;
|
|
|
|
m_config.tileSize = CELLS_PER_TILE_SIDE;
|
|
|
|
m_config.width = m_config.tileSize + m_config.borderSize * 2;
|
|
|
|
m_config.height = m_config.tileSize + m_config.borderSize * 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
bool generateTileAt(EntityRef zone_entity, const DVec3& world_pos, bool keep_data) override {
|
|
|
|
RecastZone& zone = m_zones[zone_entity];
|
|
|
|
const Transform tr = m_universe.getTransform(zone_entity);
|
|
|
|
const Vec3 pos = tr.inverted().transform(world_pos).toFloat();
|
|
|
|
const Vec3 min = -zone.zone.extents;
|
|
|
|
const int x = int((pos.x - min.x + (1 + m_config.borderSize) * m_config.cs) / (CELLS_PER_TILE_SIDE * CELL_SIZE));
|
|
|
|
const int z = int((pos.z - min.z + (1 + m_config.borderSize) * m_config.cs) / (CELLS_PER_TILE_SIDE * CELL_SIZE));
|
|
|
|
return generateTile(zone, zone_entity, x, z, keep_data);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
bool generateTile(RecastZone& zone, EntityRef zone_entity, int x, int z, bool keep_data) {
|
2017-09-23 23:50:22 +02:00
|
|
|
PROFILE_FUNCTION();
|
2019-07-04 23:45:31 +02:00
|
|
|
if (!zone.navmesh) return false;
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
zone.navmesh->removeTile(zone.navmesh->getTileRefAt(x, z, 0), 0, 0);
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
rcContext ctx;
|
|
|
|
const Vec3 min = -zone.zone.extents;
|
|
|
|
const Vec3 max = zone.zone.extents;
|
|
|
|
Vec3 bmin(min.x + x * CELLS_PER_TILE_SIDE * CELL_SIZE - (1 + m_config.borderSize) * m_config.cs,
|
|
|
|
min.y,
|
|
|
|
min.z + z * CELLS_PER_TILE_SIDE * CELL_SIZE - (1 + m_config.borderSize) * m_config.cs);
|
2017-09-23 23:50:22 +02:00
|
|
|
Vec3 bmax(bmin.x + CELLS_PER_TILE_SIDE * CELL_SIZE + (1 + m_config.borderSize) * m_config.cs,
|
2019-07-04 23:45:31 +02:00
|
|
|
max.y,
|
2017-09-23 23:50:22 +02:00
|
|
|
bmin.z + CELLS_PER_TILE_SIDE * CELL_SIZE + (1 + m_config.borderSize) * m_config.cs);
|
|
|
|
if (keep_data) m_debug_tile_origin = bmin;
|
|
|
|
rcVcopy(m_config.bmin, &bmin.x);
|
|
|
|
rcVcopy(m_config.bmax, &bmax.x);
|
|
|
|
rcHeightfield* solid = rcAllocHeightfield();
|
2019-07-04 23:45:31 +02:00
|
|
|
zone.debug_heightfield = keep_data ? solid : nullptr;
|
|
|
|
if (!solid) {
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("Could not generate navmesh: Out of memory 'solid'.");
|
2017-09-23 23:50:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
2019-07-04 23:45:31 +02:00
|
|
|
|
2017-09-23 23:50:22 +02:00
|
|
|
if (!rcCreateHeightfield(
|
|
|
|
&ctx, *solid, m_config.width, m_config.height, m_config.bmin, m_config.bmax, m_config.cs, m_config.ch))
|
|
|
|
{
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("Could not generate navmesh: Could not create solid heightfield.");
|
2017-09-23 23:50:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
2019-07-04 23:45:31 +02:00
|
|
|
|
|
|
|
const Transform tr = m_universe.getTransform(zone_entity);
|
|
|
|
rasterizeGeometry(tr, AABB(bmin, bmax), ctx, m_config, *solid);
|
2017-09-23 23:50:22 +02:00
|
|
|
|
|
|
|
rcFilterLowHangingWalkableObstacles(&ctx, m_config.walkableClimb, *solid);
|
|
|
|
rcFilterLedgeSpans(&ctx, m_config.walkableHeight, m_config.walkableClimb, *solid);
|
|
|
|
rcFilterWalkableLowHeightSpans(&ctx, m_config.walkableHeight, *solid);
|
|
|
|
|
|
|
|
rcCompactHeightfield* chf = rcAllocCompactHeightfield();
|
2019-07-04 23:45:31 +02:00
|
|
|
zone.debug_compact_heightfield = keep_data ? chf : nullptr;
|
|
|
|
if (!chf) {
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("Could not generate navmesh: Out of memory 'chf'.");
|
2017-09-23 23:50:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
if (!rcBuildCompactHeightfield(&ctx, m_config.walkableHeight, m_config.walkableClimb, *solid, *chf)) {
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("Could not generate navmesh: Could not build compact data.");
|
2017-09-23 23:50:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
if (!zone.debug_heightfield) rcFreeHeightField(solid);
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
if (!rcErodeWalkableArea(&ctx, m_config.walkableRadius, *chf)) {
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("Could not generate navmesh: Could not erode.");
|
2017-09-23 23:50:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
if (!rcBuildDistanceField(&ctx, *chf)) {
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("Could not generate navmesh: Could not build distance field.");
|
2017-09-23 23:50:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
if (!rcBuildRegions(&ctx, *chf, m_config.borderSize, m_config.minRegionArea, m_config.mergeRegionArea)) {
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("Could not generate navmesh: Could not build regions.");
|
2017-09-23 23:50:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
rcContourSet* cset = rcAllocContourSet();
|
2019-07-04 23:45:31 +02:00
|
|
|
zone.debug_contours = keep_data ? cset : nullptr;
|
|
|
|
if (!cset) {
|
2017-09-23 23:50:22 +02:00
|
|
|
ctx.log(RC_LOG_ERROR, "Could not generate navmesh: Out of memory 'cset'.");
|
|
|
|
return false;
|
|
|
|
}
|
2019-07-04 23:45:31 +02:00
|
|
|
|
|
|
|
if (!rcBuildContours(&ctx, *chf, m_config.maxSimplificationError, m_config.maxEdgeLen, *cset)) {
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("Could not generate navmesh: Could not create contours.");
|
2017-09-23 23:50:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
zone.polymesh = rcAllocPolyMesh();
|
|
|
|
if (!zone.polymesh) {
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("Could not generate navmesh: Out of memory 'm_polymesh'.");
|
2017-09-23 23:50:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
2019-07-04 23:45:31 +02:00
|
|
|
if (!rcBuildPolyMesh(&ctx, *cset, m_config.maxVertsPerPoly, *zone.polymesh)) {
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("Could not generate navmesh: Could not triangulate contours.");
|
2017-09-23 23:50:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
zone.detail_mesh = rcAllocPolyMeshDetail();
|
|
|
|
if (!zone.detail_mesh) {
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("Could not generate navmesh: Out of memory 'pmdtl'.");
|
2017-09-23 23:50:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!rcBuildPolyMeshDetail(
|
2019-07-04 23:45:31 +02:00
|
|
|
&ctx, *zone.polymesh, *chf, m_config.detailSampleDist, m_config.detailSampleMaxError, *zone.detail_mesh))
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("Could not generate navmesh: Could not build detail mesh.");
|
2017-09-23 23:50:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
if (!zone.debug_compact_heightfield) rcFreeCompactHeightfield(chf);
|
|
|
|
if (!zone.debug_contours) rcFreeContourSet(cset);
|
2017-09-23 23:50:22 +02:00
|
|
|
|
|
|
|
unsigned char* nav_data = 0;
|
|
|
|
int nav_data_size = 0;
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
for (int i = 0; i < zone.polymesh->npolys; ++i) {
|
|
|
|
zone.polymesh->flags[i] = zone.polymesh->areas[i] == RC_WALKABLE_AREA ? 1 : 0;
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
dtNavMeshCreateParams params = {};
|
2019-07-04 23:45:31 +02:00
|
|
|
params.verts = zone.polymesh->verts;
|
|
|
|
params.vertCount = zone.polymesh->nverts;
|
|
|
|
params.polys = zone.polymesh->polys;
|
|
|
|
params.polyAreas = zone.polymesh->areas;
|
|
|
|
params.polyFlags = zone.polymesh->flags;
|
|
|
|
params.polyCount = zone.polymesh->npolys;
|
|
|
|
params.nvp = zone.polymesh->nvp;
|
|
|
|
params.detailMeshes = zone.detail_mesh->meshes;
|
|
|
|
params.detailVerts = zone.detail_mesh->verts;
|
|
|
|
params.detailVertsCount = zone.detail_mesh->nverts;
|
|
|
|
params.detailTris = zone.detail_mesh->tris;
|
|
|
|
params.detailTriCount = zone.detail_mesh->ntris;
|
2017-09-23 23:50:22 +02:00
|
|
|
params.walkableHeight = m_config.walkableHeight * m_config.ch;
|
|
|
|
params.walkableRadius = m_config.walkableRadius * m_config.cs;
|
|
|
|
params.walkableClimb = m_config.walkableClimb * m_config.ch;
|
|
|
|
params.tileX = x;
|
|
|
|
params.tileY = z;
|
2019-07-04 23:45:31 +02:00
|
|
|
rcVcopy(params.bmin, zone.polymesh->bmin);
|
|
|
|
rcVcopy(params.bmax, zone.polymesh->bmax);
|
2017-09-23 23:50:22 +02:00
|
|
|
params.cs = m_config.cs;
|
|
|
|
params.ch = m_config.ch;
|
|
|
|
params.buildBvTree = false;
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
if (!dtCreateNavMeshData(¶ms, &nav_data, &nav_data_size)) {
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("Could not build Detour navmesh.");
|
2017-09-23 23:50:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
2019-07-04 23:45:31 +02:00
|
|
|
|
|
|
|
if (dtStatusFailed(zone.navmesh->addTile(nav_data, nav_data_size, DT_TILE_FREE_DATA, 0, nullptr))) {
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("Could not add Detour tile.");
|
2017-09-23 23:50:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
bool initNavmesh(RecastZone& zone) {
|
2019-07-05 20:08:51 +02:00
|
|
|
ASSERT(!zone.navmesh);
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
zone.navmesh = dtAllocNavMesh();
|
|
|
|
if (!zone.navmesh) {
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("Could not create Detour navmesh");
|
2017-09-23 23:50:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
zone.navquery = dtAllocNavMeshQuery();
|
|
|
|
if (!zone.navquery) {
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("Could not create Detour navmesh query");
|
2017-09-23 23:50:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
2019-07-04 23:45:31 +02:00
|
|
|
|
|
|
|
if (dtStatusFailed(zone.navquery->init(zone.navmesh, 2048))) {
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("Could not init Detour navmesh query");
|
2017-09-23 23:50:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
bool generateNavmesh(EntityRef zone_entity) override {
|
2017-09-23 23:50:22 +02:00
|
|
|
PROFILE_FUNCTION();
|
2019-07-04 23:45:31 +02:00
|
|
|
RecastZone& zone = m_zones[zone_entity];
|
|
|
|
clearNavmesh(zone);
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
if (!initNavmesh(zone)) return false;
|
2017-09-23 23:50:22 +02:00
|
|
|
|
|
|
|
dtNavMeshParams params;
|
2019-07-04 23:45:31 +02:00
|
|
|
const Vec3 min = -zone.zone.extents;
|
|
|
|
const Vec3 max = zone.zone.extents;
|
|
|
|
|
|
|
|
rcVcopy(params.orig, &min.x);
|
2017-09-23 23:50:22 +02:00
|
|
|
params.tileWidth = float(CELLS_PER_TILE_SIDE * CELL_SIZE);
|
|
|
|
params.tileHeight = float(CELLS_PER_TILE_SIDE * CELL_SIZE);
|
|
|
|
int grid_width, grid_height;
|
2019-07-04 23:45:31 +02:00
|
|
|
rcCalcGridSize(&min.x, &max.x, CELL_SIZE, &grid_width, &grid_height);
|
2017-09-23 23:50:22 +02:00
|
|
|
m_num_tiles_x = (grid_width + CELLS_PER_TILE_SIDE - 1) / CELLS_PER_TILE_SIDE;
|
|
|
|
m_num_tiles_z = (grid_height + CELLS_PER_TILE_SIDE - 1) / CELLS_PER_TILE_SIDE;
|
|
|
|
params.maxTiles = m_num_tiles_x * m_num_tiles_z;
|
2019-07-04 20:23:03 +02:00
|
|
|
int tiles_bits = log2(nextPow2(params.maxTiles));
|
2017-09-23 23:50:22 +02:00
|
|
|
params.maxPolys = 1 << (22 - tiles_bits); // keep 10 bits for salt
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
if (dtStatusFailed(zone.navmesh->init(¶ms))) {
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("Could not init Detour navmesh");
|
2017-09-23 23:50:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
for (int j = 0; j < m_num_tiles_z; ++j) {
|
|
|
|
for (int i = 0; i < m_num_tiles_x; ++i) {
|
|
|
|
if (!generateTile(zone, zone_entity, i, j, false)) {
|
2017-09-23 23:50:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
void addCrowdAgent(Agent& agent, RecastZone& zone) {
|
|
|
|
ASSERT(zone.crowd);
|
2017-09-23 23:50:22 +02:00
|
|
|
|
2019-07-05 13:16:39 +02:00
|
|
|
const Transform zone_tr = m_universe.getTransform(zone.entity);
|
|
|
|
const Vec3 pos = zone_tr.inverted().transform(m_universe.getPosition(agent.entity)).toFloat();
|
2017-09-23 23:50:22 +02:00
|
|
|
dtCrowdAgentParams params = {};
|
|
|
|
params.radius = agent.radius;
|
|
|
|
params.height = agent.height;
|
|
|
|
params.maxAcceleration = 10.0f;
|
|
|
|
params.maxSpeed = 10.0f;
|
|
|
|
params.collisionQueryRange = params.radius * 12.0f;
|
|
|
|
params.pathOptimizationRange = params.radius * 30.0f;
|
|
|
|
params.updateFlags = DT_CROWD_ANTICIPATE_TURNS | DT_CROWD_SEPARATION | DT_CROWD_OBSTACLE_AVOIDANCE | DT_CROWD_OPTIMIZE_TOPO | DT_CROWD_OPTIMIZE_VIS;
|
2019-07-05 13:16:39 +02:00
|
|
|
agent.agent = zone.crowd->addAgent(&pos.x, ¶ms);
|
|
|
|
if (agent.agent < 0) {
|
2020-11-16 22:27:05 +01:00
|
|
|
logError("Failed to create navigation actor");
|
2019-07-05 13:16:39 +02:00
|
|
|
}
|
2019-07-04 23:45:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void createZone(EntityRef entity) {
|
|
|
|
RecastZone zone;
|
|
|
|
zone.zone.extents = Vec3(1);
|
2019-07-05 13:16:39 +02:00
|
|
|
zone.entity = entity;
|
2019-07-04 23:45:31 +02:00
|
|
|
m_zones.insert(entity, zone);
|
|
|
|
m_universe.onComponentCreated(entity, NAVMESH_ZONE_TYPE, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void destroyZone(EntityRef entity) {
|
|
|
|
auto iter = m_zones.find(entity);
|
2019-07-07 11:53:48 +02:00
|
|
|
const RecastZone& zone = iter.value();
|
|
|
|
if (zone.crowd) {
|
|
|
|
for (Agent& agent : m_agents) {
|
|
|
|
if (agent.zone == zone.entity) {
|
|
|
|
zone.crowd->removeAgent(agent.agent);
|
|
|
|
agent.agent = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dtFreeCrowd(zone.crowd);
|
|
|
|
}
|
|
|
|
|
2019-07-04 23:45:31 +02:00
|
|
|
m_zones.erase(iter);
|
2019-07-05 13:16:39 +02:00
|
|
|
m_universe.onComponentDestroyed(entity, NAVMESH_ZONE_TYPE, this);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
2019-07-07 11:53:48 +02:00
|
|
|
void assignZone(Agent& agent) {
|
2019-12-20 19:25:33 +01:00
|
|
|
const DVec3 agent_pos = m_universe.getPosition(agent.entity);
|
2019-07-07 11:53:48 +02:00
|
|
|
for (RecastZone& zone : m_zones) {
|
|
|
|
const Transform inv_zone_tr = m_universe.getTransform(zone.entity).inverted();
|
|
|
|
const Vec3 min = -zone.zone.extents;
|
|
|
|
const Vec3 max = zone.zone.extents;
|
2019-12-20 19:25:33 +01:00
|
|
|
const Vec3 pos = inv_zone_tr.transform(agent_pos).toFloat();
|
2019-07-07 11:53:48 +02:00
|
|
|
if (pos.x > min.x && pos.y > min.y && pos.z > min.z
|
|
|
|
&& pos.x < max.x && pos.y < max.y && pos.z < max.z)
|
|
|
|
{
|
|
|
|
agent.zone = zone.entity;
|
|
|
|
if (zone.crowd) addCrowdAgent(agent, zone);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void createAgent(EntityRef entity) {
|
2018-01-12 00:35:18 +01:00
|
|
|
Agent agent;
|
2019-07-04 23:45:31 +02:00
|
|
|
agent.zone = INVALID_ENTITY;
|
2018-01-12 00:35:18 +01:00
|
|
|
agent.entity = entity;
|
|
|
|
agent.radius = 0.5f;
|
|
|
|
agent.height = 2.0f;
|
|
|
|
agent.agent = -1;
|
|
|
|
agent.flags = Agent::USE_ROOT_MOTION;
|
|
|
|
agent.is_finished = true;
|
|
|
|
m_agents.insert(entity, agent);
|
2019-07-07 11:53:48 +02:00
|
|
|
assignZone(agent);
|
2018-01-12 17:01:26 +01:00
|
|
|
m_universe.onComponentCreated(entity, NAVMESH_AGENT_TYPE, this);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
2019-07-07 11:53:48 +02:00
|
|
|
void destroyAgent(EntityRef entity) {
|
2018-01-12 00:35:18 +01:00
|
|
|
auto iter = m_agents.find(entity);
|
|
|
|
const Agent& agent = iter.value();
|
2019-07-05 13:16:39 +02:00
|
|
|
if (agent.zone.isValid()) {
|
|
|
|
RecastZone& zone = m_zones[(EntityRef)agent.zone];
|
|
|
|
if (zone.crowd && agent.agent >= 0) zone.crowd->removeAgent(agent.agent);
|
|
|
|
m_agents.erase(iter);
|
|
|
|
}
|
2018-01-12 17:01:26 +01:00
|
|
|
m_universe.onComponentDestroyed(entity, NAVMESH_AGENT_TYPE, this);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int getVersion() const override { return (int)NavigationSceneVersion::LATEST; }
|
|
|
|
|
|
|
|
|
2019-07-04 20:23:03 +02:00
|
|
|
void serialize(OutputMemoryStream& serializer) override
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
2019-07-04 23:45:31 +02:00
|
|
|
int count = m_zones.size();
|
2017-09-23 23:50:22 +02:00
|
|
|
serializer.write(count);
|
2019-07-04 23:45:31 +02:00
|
|
|
for (auto iter = m_zones.begin(); iter.isValid(); ++iter) {
|
|
|
|
serializer.write(iter.key());
|
2019-07-05 13:16:39 +02:00
|
|
|
serializer.write(iter.value().zone);
|
2019-07-04 23:45:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
count = m_agents.size();
|
|
|
|
serializer.write(count);
|
|
|
|
for (auto iter = m_agents.begin(), end = m_agents.end(); iter != end; ++iter) {
|
2017-09-23 23:50:22 +02:00
|
|
|
serializer.write(iter.key());
|
|
|
|
serializer.write(iter.value().radius);
|
|
|
|
serializer.write(iter.value().height);
|
|
|
|
serializer.write(iter.value().flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-28 20:31:29 +01:00
|
|
|
void deserialize(InputMemoryStream& serializer, const EntityMap& entity_map, i32 version) override
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
2020-01-28 19:28:25 +01:00
|
|
|
u32 count = 0;
|
2019-07-04 23:45:31 +02:00
|
|
|
serializer.read(count);
|
2020-01-28 19:28:25 +01:00
|
|
|
m_zones.reserve(count + m_zones.size());
|
|
|
|
for (u32 i = 0; i < count; ++i) {
|
2019-07-05 13:16:39 +02:00
|
|
|
RecastZone zone;
|
2019-07-04 23:45:31 +02:00
|
|
|
EntityRef e;
|
|
|
|
serializer.read(e);
|
2020-01-28 19:28:25 +01:00
|
|
|
e = entity_map.get(e);
|
2019-07-05 13:16:39 +02:00
|
|
|
serializer.read(zone.zone);
|
|
|
|
m_zones.insert(e, zone);
|
2019-07-04 23:45:31 +02:00
|
|
|
m_universe.onComponentCreated(e, NAVMESH_ZONE_TYPE, this);
|
|
|
|
}
|
|
|
|
|
2017-09-23 23:50:22 +02:00
|
|
|
serializer.read(count);
|
2020-01-28 19:28:25 +01:00
|
|
|
m_agents.reserve(count + m_agents.size());
|
|
|
|
for (u32 i = 0; i < count; ++i) {
|
2017-09-23 23:50:22 +02:00
|
|
|
Agent agent;
|
|
|
|
serializer.read(agent.entity);
|
2020-01-28 19:28:25 +01:00
|
|
|
agent.entity = entity_map.get(agent.entity);
|
2017-09-23 23:50:22 +02:00
|
|
|
serializer.read(agent.radius);
|
|
|
|
serializer.read(agent.height);
|
|
|
|
serializer.read(agent.flags);
|
|
|
|
agent.is_finished = true;
|
|
|
|
agent.agent = -1;
|
|
|
|
m_agents.insert(agent.entity, agent);
|
2018-01-12 17:01:26 +01:00
|
|
|
m_universe.onComponentCreated(agent.entity, NAVMESH_AGENT_TYPE, this);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-19 17:35:37 +02:00
|
|
|
bool isGettingRootMotionFromAnim(EntityRef entity) override
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
|
|
|
return (m_agents[entity].flags & Agent::GET_ROOT_MOTION_FROM_ANIM_CONTROLLER) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-19 17:35:37 +02:00
|
|
|
void setIsGettingRootMotionFromAnim(EntityRef entity, bool is) override
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
|
|
|
if (is)
|
|
|
|
m_agents[entity].flags |= Agent::GET_ROOT_MOTION_FROM_ANIM_CONTROLLER;
|
|
|
|
else
|
|
|
|
m_agents[entity].flags &= ~Agent::GET_ROOT_MOTION_FROM_ANIM_CONTROLLER;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-19 17:35:37 +02:00
|
|
|
bool useAgentRootMotion(EntityRef entity) override
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
|
|
|
return (m_agents[entity].flags & Agent::USE_ROOT_MOTION) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-19 17:35:37 +02:00
|
|
|
void setUseAgentRootMotion(EntityRef entity, bool use_root_motion) override
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
|
|
|
if (use_root_motion)
|
|
|
|
m_agents[entity].flags |= Agent::USE_ROOT_MOTION;
|
|
|
|
else
|
|
|
|
m_agents[entity].flags &= ~Agent::USE_ROOT_MOTION;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-19 17:35:37 +02:00
|
|
|
void setAgentRadius(EntityRef entity, float radius) override
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
|
|
|
m_agents[entity].radius = radius;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-19 17:35:37 +02:00
|
|
|
float getAgentRadius(EntityRef entity) override
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
|
|
|
return m_agents[entity].radius;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-19 17:35:37 +02:00
|
|
|
void setAgentHeight(EntityRef entity, float height) override
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
|
|
|
m_agents[entity].height = height;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-08-19 17:35:37 +02:00
|
|
|
float getAgentHeight(EntityRef entity) override
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
|
|
|
return m_agents[entity].height;
|
|
|
|
}
|
2019-07-04 23:45:31 +02:00
|
|
|
|
|
|
|
NavmeshZone& getZone(EntityRef entity) override {
|
|
|
|
return m_zones[entity].zone;
|
|
|
|
}
|
2017-09-23 23:50:22 +02:00
|
|
|
|
|
|
|
IPlugin& getPlugin() const override { return m_system; }
|
|
|
|
Universe& getUniverse() override { return m_universe; }
|
|
|
|
|
|
|
|
IAllocator& m_allocator;
|
|
|
|
Universe& m_universe;
|
|
|
|
IPlugin& m_system;
|
|
|
|
Engine& m_engine;
|
2019-07-04 23:45:31 +02:00
|
|
|
HashMap<EntityRef, RecastZone> m_zones;
|
2018-08-19 17:35:37 +02:00
|
|
|
HashMap<EntityRef, Agent> m_agents;
|
2019-07-07 11:53:48 +02:00
|
|
|
EntityPtr m_moving_agent = INVALID_ENTITY;
|
2019-07-04 23:45:31 +02:00
|
|
|
|
2017-09-23 23:50:22 +02:00
|
|
|
Vec3 m_debug_tile_origin;
|
|
|
|
rcConfig m_config;
|
|
|
|
int m_num_tiles_x;
|
|
|
|
int m_num_tiles_z;
|
|
|
|
LuaScriptScene* m_script_scene;
|
|
|
|
DelegateList<void(float)> m_on_update;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2020-10-09 20:16:55 +02:00
|
|
|
UniquePtr<NavigationScene> NavigationScene::create(Engine& engine, IPlugin& system, Universe& universe, IAllocator& allocator)
|
2017-09-23 23:50:22 +02:00
|
|
|
{
|
2020-10-09 20:16:55 +02:00
|
|
|
return UniquePtr<NavigationSceneImpl>::create(allocator, engine, system, universe, allocator);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
2020-12-01 00:17:00 +01:00
|
|
|
void NavigationScene::reflect() {
|
2020-12-01 19:20:47 +01:00
|
|
|
LUMIX_SCENE(NavigationSceneImpl, "navigation",
|
|
|
|
LUMIX_FUNC(NavigationScene::setGeneratorParams),
|
|
|
|
LUMIX_CMP(Zone, "navmesh_zone", "Navigation / Zone",
|
|
|
|
icon(ICON_FA_MAP_MARKED_ALT),
|
|
|
|
LUMIX_FUNC_EX(NavigationScene::debugDrawContours, "drawContours"),
|
|
|
|
LUMIX_FUNC_EX(NavigationScene::debugDrawNavmesh, "drawNavmesh"),
|
|
|
|
LUMIX_FUNC_EX(NavigationScene::debugDrawCompactHeightfield, "drawCompactHeightfield"),
|
|
|
|
LUMIX_FUNC_EX(NavigationScene::debugDrawHeightfield, "drawHeightfield"),
|
|
|
|
LUMIX_FUNC(NavigationScene::save),
|
|
|
|
LUMIX_FUNC(NavigationScene::load),
|
|
|
|
LUMIX_FUNC(NavigationScene::generateNavmesh),
|
2020-12-01 00:17:00 +01:00
|
|
|
var_property("Extents", &NavigationScene::getZone, &NavmeshZone::extents)
|
|
|
|
),
|
2020-12-01 19:20:47 +01:00
|
|
|
LUMIX_CMP(Agent, "navmesh_agent", "Navigation / Agent",
|
|
|
|
icon(ICON_FA_STREET_VIEW),
|
|
|
|
LUMIX_FUNC_EX(NavigationScene::setActorActive, "setActive"),
|
|
|
|
LUMIX_FUNC_EX(NavigationScene::navigate, "navigate"),
|
|
|
|
LUMIX_FUNC_EX(NavigationScene::cancelNavigation, "cancelNavigation"),
|
|
|
|
LUMIX_FUNC_EX(NavigationScene::getAgentSpeed, "getSpeed"),
|
|
|
|
LUMIX_FUNC_EX(NavigationScene::debugDrawPath, "drawPath"),
|
|
|
|
LUMIX_PROP(AgentRadius, "Radius", MinAttribute(0)),
|
|
|
|
LUMIX_PROP(AgentHeight, "Height", MinAttribute(0)),
|
2020-12-01 00:17:00 +01:00
|
|
|
property("Use root motion", &NavigationScene::useAgentRootMotion, &NavigationScene::setUseAgentRootMotion)
|
|
|
|
//property("Get root motion from animation", LUMIX_PROP_FULL(NavigationScene, isGettingRootMotionFromAnim, setIsGettingRootMotionFromAnim))
|
|
|
|
)
|
|
|
|
);
|
2017-09-23 23:50:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Lumix
|