Add MultiNodeEdit support for NavigationRegion3D
Adds MultiNodeEdit support for NavigationRegion3D.
This commit is contained in:
parent
cc9761c3f0
commit
7ae2c0af9d
@ -1219,6 +1219,14 @@ bool GodotNavigationServer3D::is_baking_navigation_mesh(Ref<NavigationMesh> p_na
|
|||||||
return NavMeshGenerator3D::get_singleton()->is_baking(p_navigation_mesh);
|
return NavMeshGenerator3D::get_singleton()->is_baking(p_navigation_mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String GodotNavigationServer3D::get_baking_navigation_mesh_state_msg(Ref<NavigationMesh> p_navigation_mesh) const {
|
||||||
|
#ifdef _3D_DISABLED
|
||||||
|
return "";
|
||||||
|
#else
|
||||||
|
return NavMeshGenerator3D::get_singleton()->get_baking_state_msg(p_navigation_mesh);
|
||||||
|
#endif // _3D_DISABLED
|
||||||
|
}
|
||||||
|
|
||||||
COMMAND_1(free, RID, p_object) {
|
COMMAND_1(free, RID, p_object) {
|
||||||
if (map_owner.owns(p_object)) {
|
if (map_owner.owns(p_object)) {
|
||||||
NavMap3D *map = map_owner.get_or_null(p_object);
|
NavMap3D *map = map_owner.get_or_null(p_object);
|
||||||
|
@ -272,6 +272,7 @@ public:
|
|||||||
virtual void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
|
virtual void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
|
||||||
virtual void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
|
virtual void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override;
|
||||||
virtual bool is_baking_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh) const override;
|
virtual bool is_baking_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh) const override;
|
||||||
|
virtual String get_baking_navigation_mesh_state_msg(Ref<NavigationMesh> p_navigation_mesh) const override;
|
||||||
|
|
||||||
virtual RID source_geometry_parser_create() override;
|
virtual RID source_geometry_parser_create() override;
|
||||||
virtual void source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) override;
|
virtual void source_geometry_parser_set_callback(RID p_parser, const Callable &p_callback) override;
|
||||||
|
@ -45,10 +45,26 @@ RWLock NavMeshGenerator3D::generator_parsers_rwlock;
|
|||||||
bool NavMeshGenerator3D::use_threads = true;
|
bool NavMeshGenerator3D::use_threads = true;
|
||||||
bool NavMeshGenerator3D::baking_use_multiple_threads = true;
|
bool NavMeshGenerator3D::baking_use_multiple_threads = true;
|
||||||
bool NavMeshGenerator3D::baking_use_high_priority_threads = true;
|
bool NavMeshGenerator3D::baking_use_high_priority_threads = true;
|
||||||
HashSet<Ref<NavigationMesh>> NavMeshGenerator3D::baking_navmeshes;
|
HashMap<Ref<NavigationMesh>, NavMeshGenerator3D::NavMeshGeneratorTask3D *> NavMeshGenerator3D::baking_navmeshes;
|
||||||
HashMap<WorkerThreadPool::TaskID, NavMeshGenerator3D::NavMeshGeneratorTask3D *> NavMeshGenerator3D::generator_tasks;
|
HashMap<WorkerThreadPool::TaskID, NavMeshGenerator3D::NavMeshGeneratorTask3D *> NavMeshGenerator3D::generator_tasks;
|
||||||
LocalVector<NavMeshGeometryParser3D *> NavMeshGenerator3D::generator_parsers;
|
LocalVector<NavMeshGeometryParser3D *> NavMeshGenerator3D::generator_parsers;
|
||||||
|
|
||||||
|
static const char *_navmesh_bake_state_msgs[(size_t)NavMeshGenerator3D::NavMeshBakeState::BAKE_STATE_MAX] = {
|
||||||
|
"",
|
||||||
|
"Setting up configuration...",
|
||||||
|
"Calculating grid size...",
|
||||||
|
"Creating heightfield...",
|
||||||
|
"Marking walkable triangles...",
|
||||||
|
"Constructing compact heightfield...", // step 5
|
||||||
|
"Eroding walkable area...",
|
||||||
|
"Sample partitioning...",
|
||||||
|
"Creating contours...",
|
||||||
|
"Creating polymesh...",
|
||||||
|
"Converting to native navigation mesh...", // step 10
|
||||||
|
"Baking cleanup...",
|
||||||
|
"Baking finished.",
|
||||||
|
};
|
||||||
|
|
||||||
NavMeshGenerator3D *NavMeshGenerator3D::get_singleton() {
|
NavMeshGenerator3D *NavMeshGenerator3D::get_singleton() {
|
||||||
return singleton;
|
return singleton;
|
||||||
}
|
}
|
||||||
@ -158,10 +174,15 @@ void NavMeshGenerator3D::bake_from_source_geometry_data(Ref<NavigationMesh> p_na
|
|||||||
ERR_FAIL_MSG("NavigationMesh is already baking. Wait for current bake to finish.");
|
ERR_FAIL_MSG("NavigationMesh is already baking. Wait for current bake to finish.");
|
||||||
}
|
}
|
||||||
baking_navmesh_mutex.lock();
|
baking_navmesh_mutex.lock();
|
||||||
baking_navmeshes.insert(p_navigation_mesh);
|
NavMeshGeneratorTask3D generator_task;
|
||||||
|
baking_navmeshes.insert(p_navigation_mesh, &generator_task);
|
||||||
baking_navmesh_mutex.unlock();
|
baking_navmesh_mutex.unlock();
|
||||||
|
|
||||||
generator_bake_from_source_geometry_data(p_navigation_mesh, p_source_geometry_data);
|
generator_task.navigation_mesh = p_navigation_mesh;
|
||||||
|
generator_task.source_geometry_data = p_source_geometry_data;
|
||||||
|
generator_task.status = NavMeshGeneratorTask3D::TaskStatus::BAKING_STARTED;
|
||||||
|
|
||||||
|
generator_bake_from_source_geometry_data(&generator_task);
|
||||||
|
|
||||||
baking_navmesh_mutex.lock();
|
baking_navmesh_mutex.lock();
|
||||||
baking_navmeshes.erase(p_navigation_mesh);
|
baking_navmeshes.erase(p_navigation_mesh);
|
||||||
@ -197,16 +218,16 @@ void NavMeshGenerator3D::bake_from_source_geometry_data_async(Ref<NavigationMesh
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
baking_navmesh_mutex.lock();
|
baking_navmesh_mutex.lock();
|
||||||
baking_navmeshes.insert(p_navigation_mesh);
|
NavMeshGeneratorTask3D *generator_task = memnew(NavMeshGeneratorTask3D);
|
||||||
|
baking_navmeshes.insert(p_navigation_mesh, generator_task);
|
||||||
baking_navmesh_mutex.unlock();
|
baking_navmesh_mutex.unlock();
|
||||||
|
|
||||||
MutexLock generator_task_lock(generator_task_mutex);
|
|
||||||
NavMeshGeneratorTask3D *generator_task = memnew(NavMeshGeneratorTask3D);
|
|
||||||
generator_task->navigation_mesh = p_navigation_mesh;
|
generator_task->navigation_mesh = p_navigation_mesh;
|
||||||
generator_task->source_geometry_data = p_source_geometry_data;
|
generator_task->source_geometry_data = p_source_geometry_data;
|
||||||
generator_task->callback = p_callback;
|
generator_task->callback = p_callback;
|
||||||
generator_task->status = NavMeshGeneratorTask3D::TaskStatus::BAKING_STARTED;
|
generator_task->status = NavMeshGeneratorTask3D::TaskStatus::BAKING_STARTED;
|
||||||
generator_task->thread_task_id = WorkerThreadPool::get_singleton()->add_native_task(&NavMeshGenerator3D::generator_thread_bake, generator_task, NavMeshGenerator3D::baking_use_high_priority_threads, SNAME("NavMeshGeneratorBake3D"));
|
generator_task->thread_task_id = WorkerThreadPool::get_singleton()->add_native_task(&NavMeshGenerator3D::generator_thread_bake, generator_task, NavMeshGenerator3D::baking_use_high_priority_threads, SNAME("NavMeshGeneratorBake3D"));
|
||||||
|
MutexLock generator_task_lock(generator_task_mutex);
|
||||||
generator_tasks.insert(generator_task->thread_task_id, generator_task);
|
generator_tasks.insert(generator_task->thread_task_id, generator_task);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,10 +236,21 @@ bool NavMeshGenerator3D::is_baking(Ref<NavigationMesh> p_navigation_mesh) {
|
|||||||
return baking_navmeshes.has(p_navigation_mesh);
|
return baking_navmeshes.has(p_navigation_mesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String NavMeshGenerator3D::get_baking_state_msg(Ref<NavigationMesh> p_navigation_mesh) {
|
||||||
|
String bake_state_msg;
|
||||||
|
MutexLock baking_navmesh_lock(baking_navmesh_mutex);
|
||||||
|
if (baking_navmeshes.has(p_navigation_mesh)) {
|
||||||
|
bake_state_msg = _navmesh_bake_state_msgs[baking_navmeshes[p_navigation_mesh]->bake_state];
|
||||||
|
} else {
|
||||||
|
bake_state_msg = _navmesh_bake_state_msgs[NavMeshBakeState::BAKE_STATE_NONE];
|
||||||
|
}
|
||||||
|
return bake_state_msg;
|
||||||
|
}
|
||||||
|
|
||||||
void NavMeshGenerator3D::generator_thread_bake(void *p_arg) {
|
void NavMeshGenerator3D::generator_thread_bake(void *p_arg) {
|
||||||
NavMeshGeneratorTask3D *generator_task = static_cast<NavMeshGeneratorTask3D *>(p_arg);
|
NavMeshGeneratorTask3D *generator_task = static_cast<NavMeshGeneratorTask3D *>(p_arg);
|
||||||
|
|
||||||
generator_bake_from_source_geometry_data(generator_task->navigation_mesh, generator_task->source_geometry_data);
|
generator_bake_from_source_geometry_data(generator_task);
|
||||||
|
|
||||||
generator_task->status = NavMeshGeneratorTask3D::TaskStatus::BAKING_FINISHED;
|
generator_task->status = NavMeshGeneratorTask3D::TaskStatus::BAKING_FINISHED;
|
||||||
}
|
}
|
||||||
@ -269,7 +301,10 @@ void NavMeshGenerator3D::generator_parse_source_geometry_data(const Ref<Navigati
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data) {
|
void NavMeshGenerator3D::generator_bake_from_source_geometry_data(NavMeshGeneratorTask3D *p_generator_task) {
|
||||||
|
Ref<NavigationMesh> p_navigation_mesh = p_generator_task->navigation_mesh;
|
||||||
|
const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data = p_generator_task->source_geometry_data;
|
||||||
|
|
||||||
if (p_navigation_mesh.is_null() || p_source_geometry_data.is_null()) {
|
if (p_navigation_mesh.is_null() || p_source_geometry_data.is_null()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -294,10 +329,7 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation
|
|||||||
rcPolyMeshDetail *detail_mesh = nullptr;
|
rcPolyMeshDetail *detail_mesh = nullptr;
|
||||||
rcContext ctx;
|
rcContext ctx;
|
||||||
|
|
||||||
// added to keep track of steps, no functionality right now
|
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_CONFIGURATION; // step #1
|
||||||
String bake_state = "";
|
|
||||||
|
|
||||||
bake_state = "Setting up Configuration..."; // step #1
|
|
||||||
|
|
||||||
const float *verts = source_geometry_vertices.ptr();
|
const float *verts = source_geometry_vertices.ptr();
|
||||||
const int nverts = source_geometry_vertices.size() / 3;
|
const int nverts = source_geometry_vertices.size() / 3;
|
||||||
@ -373,7 +405,7 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation
|
|||||||
cfg.bmax[2] = cfg.bmin[2] + baking_aabb.size[2];
|
cfg.bmax[2] = cfg.bmin[2] + baking_aabb.size[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
bake_state = "Calculating grid size..."; // step #2
|
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_CALC_GRID_SIZE; // step #2
|
||||||
rcCalcGridSize(cfg.bmin, cfg.bmax, cfg.cs, &cfg.width, &cfg.height);
|
rcCalcGridSize(cfg.bmin, cfg.bmax, cfg.cs, &cfg.width, &cfg.height);
|
||||||
|
|
||||||
// ~30000000 seems to be around sweetspot where Editor baking breaks
|
// ~30000000 seems to be around sweetspot where Editor baking breaks
|
||||||
@ -387,13 +419,13 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bake_state = "Creating heightfield..."; // step #3
|
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_CREATE_HEIGHTFIELD; // step #3
|
||||||
hf = rcAllocHeightfield();
|
hf = rcAllocHeightfield();
|
||||||
|
|
||||||
ERR_FAIL_NULL(hf);
|
ERR_FAIL_NULL(hf);
|
||||||
ERR_FAIL_COND(!rcCreateHeightfield(&ctx, *hf, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs, cfg.ch));
|
ERR_FAIL_COND(!rcCreateHeightfield(&ctx, *hf, cfg.width, cfg.height, cfg.bmin, cfg.bmax, cfg.cs, cfg.ch));
|
||||||
|
|
||||||
bake_state = "Marking walkable triangles..."; // step #4
|
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_MARK_WALKABLE_TRIANGLES; // step #4
|
||||||
{
|
{
|
||||||
Vector<unsigned char> tri_areas;
|
Vector<unsigned char> tri_areas;
|
||||||
tri_areas.resize(ntris);
|
tri_areas.resize(ntris);
|
||||||
@ -416,7 +448,7 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation
|
|||||||
rcFilterWalkableLowHeightSpans(&ctx, cfg.walkableHeight, *hf);
|
rcFilterWalkableLowHeightSpans(&ctx, cfg.walkableHeight, *hf);
|
||||||
}
|
}
|
||||||
|
|
||||||
bake_state = "Constructing compact heightfield..."; // step #5
|
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_CONSTRUCT_COMPACT_HEIGHTFIELD; // step #5
|
||||||
|
|
||||||
chf = rcAllocCompactHeightfield();
|
chf = rcAllocCompactHeightfield();
|
||||||
|
|
||||||
@ -443,7 +475,7 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bake_state = "Eroding walkable area..."; // step #6
|
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_ERODE_WALKABLE_AREA; // step #6
|
||||||
|
|
||||||
ERR_FAIL_COND(!rcErodeWalkableArea(&ctx, cfg.walkableRadius, *chf));
|
ERR_FAIL_COND(!rcErodeWalkableArea(&ctx, cfg.walkableRadius, *chf));
|
||||||
|
|
||||||
@ -464,7 +496,7 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bake_state = "Partitioning..."; // step #7
|
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_SAMPLE_PARTITIONING; // step #7
|
||||||
|
|
||||||
if (p_navigation_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_WATERSHED) {
|
if (p_navigation_mesh->get_sample_partition_type() == NavigationMesh::SAMPLE_PARTITION_WATERSHED) {
|
||||||
ERR_FAIL_COND(!rcBuildDistanceField(&ctx, *chf));
|
ERR_FAIL_COND(!rcBuildDistanceField(&ctx, *chf));
|
||||||
@ -475,14 +507,14 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation
|
|||||||
ERR_FAIL_COND(!rcBuildLayerRegions(&ctx, *chf, cfg.borderSize, cfg.minRegionArea));
|
ERR_FAIL_COND(!rcBuildLayerRegions(&ctx, *chf, cfg.borderSize, cfg.minRegionArea));
|
||||||
}
|
}
|
||||||
|
|
||||||
bake_state = "Creating contours..."; // step #8
|
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_CREATING_CONTOURS; // step #8
|
||||||
|
|
||||||
cset = rcAllocContourSet();
|
cset = rcAllocContourSet();
|
||||||
|
|
||||||
ERR_FAIL_NULL(cset);
|
ERR_FAIL_NULL(cset);
|
||||||
ERR_FAIL_COND(!rcBuildContours(&ctx, *chf, cfg.maxSimplificationError, cfg.maxEdgeLen, *cset));
|
ERR_FAIL_COND(!rcBuildContours(&ctx, *chf, cfg.maxSimplificationError, cfg.maxEdgeLen, *cset));
|
||||||
|
|
||||||
bake_state = "Creating polymesh..."; // step #9
|
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_CREATING_POLYMESH; // step #9
|
||||||
|
|
||||||
poly_mesh = rcAllocPolyMesh();
|
poly_mesh = rcAllocPolyMesh();
|
||||||
ERR_FAIL_NULL(poly_mesh);
|
ERR_FAIL_NULL(poly_mesh);
|
||||||
@ -497,7 +529,7 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation
|
|||||||
rcFreeContourSet(cset);
|
rcFreeContourSet(cset);
|
||||||
cset = nullptr;
|
cset = nullptr;
|
||||||
|
|
||||||
bake_state = "Converting to native navigation mesh..."; // step #10
|
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_CONVERTING_NATIVE_NAVMESH; // step #10
|
||||||
|
|
||||||
Vector<Vector3> nav_vertices;
|
Vector<Vector3> nav_vertices;
|
||||||
Vector<Vector<int>> nav_polygons;
|
Vector<Vector<int>> nav_polygons;
|
||||||
@ -544,14 +576,14 @@ void NavMeshGenerator3D::generator_bake_from_source_geometry_data(Ref<Navigation
|
|||||||
|
|
||||||
p_navigation_mesh->set_data(nav_vertices, nav_polygons);
|
p_navigation_mesh->set_data(nav_vertices, nav_polygons);
|
||||||
|
|
||||||
bake_state = "Cleanup..."; // step #11
|
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_BAKE_CLEANUP; // step #11
|
||||||
|
|
||||||
rcFreePolyMesh(poly_mesh);
|
rcFreePolyMesh(poly_mesh);
|
||||||
poly_mesh = nullptr;
|
poly_mesh = nullptr;
|
||||||
rcFreePolyMeshDetail(detail_mesh);
|
rcFreePolyMeshDetail(detail_mesh);
|
||||||
detail_mesh = nullptr;
|
detail_mesh = nullptr;
|
||||||
|
|
||||||
bake_state = "Baking finished."; // step #12
|
p_generator_task->bake_state = NavMeshBakeState::BAKE_STATE_BAKE_FINISHED; // step #12
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NavMeshGenerator3D::generator_emit_callback(const Callable &p_callback) {
|
bool NavMeshGenerator3D::generator_emit_callback(const Callable &p_callback) {
|
||||||
|
@ -52,6 +52,25 @@ class NavMeshGenerator3D : public Object {
|
|||||||
static bool baking_use_multiple_threads;
|
static bool baking_use_multiple_threads;
|
||||||
static bool baking_use_high_priority_threads;
|
static bool baking_use_high_priority_threads;
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum NavMeshBakeState {
|
||||||
|
BAKE_STATE_NONE,
|
||||||
|
BAKE_STATE_CONFIGURATION,
|
||||||
|
BAKE_STATE_CALC_GRID_SIZE,
|
||||||
|
BAKE_STATE_CREATE_HEIGHTFIELD,
|
||||||
|
BAKE_STATE_MARK_WALKABLE_TRIANGLES,
|
||||||
|
BAKE_STATE_CONSTRUCT_COMPACT_HEIGHTFIELD,
|
||||||
|
BAKE_STATE_ERODE_WALKABLE_AREA,
|
||||||
|
BAKE_STATE_SAMPLE_PARTITIONING,
|
||||||
|
BAKE_STATE_CREATING_CONTOURS,
|
||||||
|
BAKE_STATE_CREATING_POLYMESH,
|
||||||
|
BAKE_STATE_CONVERTING_NATIVE_NAVMESH,
|
||||||
|
BAKE_STATE_BAKE_CLEANUP,
|
||||||
|
BAKE_STATE_BAKE_FINISHED,
|
||||||
|
BAKE_STATE_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
struct NavMeshGeneratorTask3D {
|
struct NavMeshGeneratorTask3D {
|
||||||
enum TaskStatus {
|
enum TaskStatus {
|
||||||
BAKING_STARTED,
|
BAKING_STARTED,
|
||||||
@ -66,17 +85,19 @@ class NavMeshGenerator3D : public Object {
|
|||||||
Callable callback;
|
Callable callback;
|
||||||
WorkerThreadPool::TaskID thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
|
WorkerThreadPool::TaskID thread_task_id = WorkerThreadPool::INVALID_TASK_ID;
|
||||||
NavMeshGeneratorTask3D::TaskStatus status = NavMeshGeneratorTask3D::TaskStatus::BAKING_STARTED;
|
NavMeshGeneratorTask3D::TaskStatus status = NavMeshGeneratorTask3D::TaskStatus::BAKING_STARTED;
|
||||||
|
|
||||||
|
NavMeshBakeState bake_state = NavMeshBakeState::BAKE_STATE_NONE;
|
||||||
};
|
};
|
||||||
|
|
||||||
static HashMap<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> generator_tasks;
|
static HashMap<WorkerThreadPool::TaskID, NavMeshGeneratorTask3D *> generator_tasks;
|
||||||
|
|
||||||
static void generator_thread_bake(void *p_arg);
|
static void generator_thread_bake(void *p_arg);
|
||||||
|
|
||||||
static HashSet<Ref<NavigationMesh>> baking_navmeshes;
|
static HashMap<Ref<NavigationMesh>, NavMeshGeneratorTask3D *> baking_navmeshes;
|
||||||
|
|
||||||
static void generator_parse_geometry_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node, bool p_recurse_children);
|
static void generator_parse_geometry_node(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node, bool p_recurse_children);
|
||||||
static void generator_parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node);
|
static void generator_parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node);
|
||||||
static void generator_bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data);
|
static void generator_bake_from_source_geometry_data(NavMeshGeneratorTask3D *p_generator_task);
|
||||||
|
|
||||||
static bool generator_emit_callback(const Callable &p_callback);
|
static bool generator_emit_callback(const Callable &p_callback);
|
||||||
|
|
||||||
@ -93,6 +114,7 @@ public:
|
|||||||
static void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback = Callable());
|
static void bake_from_source_geometry_data(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback = Callable());
|
||||||
static void bake_from_source_geometry_data_async(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback = Callable());
|
static void bake_from_source_geometry_data_async(Ref<NavigationMesh> p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, const Callable &p_callback = Callable());
|
||||||
static bool is_baking(Ref<NavigationMesh> p_navigation_mesh);
|
static bool is_baking(Ref<NavigationMesh> p_navigation_mesh);
|
||||||
|
static String get_baking_state_msg(Ref<NavigationMesh> p_navigation_mesh);
|
||||||
|
|
||||||
NavMeshGenerator3D();
|
NavMeshGenerator3D();
|
||||||
~NavMeshGenerator3D();
|
~NavMeshGenerator3D();
|
||||||
|
@ -32,18 +32,27 @@
|
|||||||
|
|
||||||
#include "editor/editor_node.h"
|
#include "editor/editor_node.h"
|
||||||
#include "editor/editor_string_names.h"
|
#include "editor/editor_string_names.h"
|
||||||
|
#include "editor/multi_node_edit.h"
|
||||||
#include "editor/plugins/node_3d_editor_plugin.h"
|
#include "editor/plugins/node_3d_editor_plugin.h"
|
||||||
#include "scene/3d/navigation/navigation_region_3d.h"
|
#include "scene/3d/navigation/navigation_region_3d.h"
|
||||||
#include "scene/gui/box_container.h"
|
#include "scene/gui/box_container.h"
|
||||||
#include "scene/gui/button.h"
|
#include "scene/gui/button.h"
|
||||||
#include "scene/gui/dialogs.h"
|
#include "scene/gui/dialogs.h"
|
||||||
#include "scene/gui/label.h"
|
#include "scene/gui/label.h"
|
||||||
|
#include "servers/navigation_server_3d.h"
|
||||||
|
|
||||||
void NavigationRegion3DEditor::_node_removed(Node *p_node) {
|
void NavigationRegion3DEditor::_node_removed(Node *p_node) {
|
||||||
if (p_node == node) {
|
if (selected_regions.is_empty()) {
|
||||||
node = nullptr;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
hide();
|
NavigationRegion3D *region = Object::cast_to<NavigationRegion3D>(p_node);
|
||||||
|
|
||||||
|
if (region && selected_regions.has(region)) {
|
||||||
|
selected_regions.erase(region);
|
||||||
|
if (selected_regions.is_empty()) {
|
||||||
|
hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,73 +62,167 @@ void NavigationRegion3DEditor::_notification(int p_what) {
|
|||||||
button_bake->set_button_icon(get_theme_icon(SNAME("Bake"), EditorStringName(EditorIcons)));
|
button_bake->set_button_icon(get_theme_icon(SNAME("Bake"), EditorStringName(EditorIcons)));
|
||||||
button_reset->set_button_icon(get_theme_icon(SNAME("Reload"), EditorStringName(EditorIcons)));
|
button_reset->set_button_icon(get_theme_icon(SNAME("Reload"), EditorStringName(EditorIcons)));
|
||||||
} break;
|
} break;
|
||||||
|
case NOTIFICATION_PROCESS: {
|
||||||
|
if (currently_baking_region) {
|
||||||
|
const String bake_state_msg = NavigationServer3D::get_singleton()->get_baking_navigation_mesh_state_msg(currently_baking_region->get_navigation_mesh());
|
||||||
|
multibake_dialog->set_text(itos(processed_regions_to_bake_count) + " / " + itos(processed_regions_to_bake_count_max) + " - Baking navmesh from region '" + currently_baking_region->get_name() + "'.\n\nBake state: " + bake_state_msg + "\n\nDo NOT change nodes by any means while the baking is parsing the SceneTree.");
|
||||||
|
} else {
|
||||||
|
multibake_dialog->set_text("");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavigationRegion3DEditor::_bake_pressed() {
|
void NavigationRegion3DEditor::_bake_pressed() {
|
||||||
button_bake->set_pressed(false);
|
button_bake->set_pressed(false);
|
||||||
|
|
||||||
ERR_FAIL_NULL(node);
|
if (selected_regions.is_empty()) {
|
||||||
Ref<NavigationMesh> navmesh = node->get_navigation_mesh();
|
|
||||||
if (navmesh.is_null()) {
|
|
||||||
err_dialog->set_text(TTR("A NavigationMesh resource must be set or created for this node to work."));
|
|
||||||
err_dialog->popup_centered();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String path = navmesh->get_path();
|
if (bake_in_process) {
|
||||||
if (!path.is_resource_file()) {
|
return;
|
||||||
int srpos = path.find("::");
|
}
|
||||||
if (srpos != -1) {
|
|
||||||
String base = path.substr(0, srpos);
|
HashSet<Ref<NavigationMesh>> unique_navmeshes;
|
||||||
if (ResourceLoader::get_resource_type(base) == "PackedScene") {
|
regions_to_bake.clear();
|
||||||
if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
|
regions_with_navmesh_to_bake.clear();
|
||||||
err_dialog->set_text(TTR("Cannot generate navigation mesh because it does not belong to the edited scene. Make it unique first."));
|
|
||||||
err_dialog->popup_centered();
|
for (NavigationRegion3D *region : selected_regions) {
|
||||||
return;
|
ERR_CONTINUE(region == nullptr);
|
||||||
}
|
Ref<NavigationMesh> navmesh = region->get_navigation_mesh();
|
||||||
} else {
|
if (navmesh.is_null()) {
|
||||||
if (FileAccess::exists(base + ".import")) {
|
err_dialog->set_text(TTR("A NavigationMesh resource must be set or created for this node to work."));
|
||||||
err_dialog->set_text(TTR("Cannot generate navigation mesh because it belongs to a resource which was imported."));
|
|
||||||
err_dialog->popup_centered();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (FileAccess::exists(path + ".import")) {
|
|
||||||
err_dialog->set_text(TTR("Cannot generate navigation mesh because the resource was imported from another type."));
|
|
||||||
err_dialog->popup_centered();
|
err_dialog->popup_centered();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String path = navmesh->get_path();
|
||||||
|
if (!path.is_resource_file()) {
|
||||||
|
int srpos = path.find("::");
|
||||||
|
if (srpos != -1) {
|
||||||
|
String base = path.substr(0, srpos);
|
||||||
|
if (ResourceLoader::get_resource_type(base) == "PackedScene") {
|
||||||
|
if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
|
||||||
|
err_dialog->set_text(TTR("Cannot generate navigation mesh because it does not belong to the edited scene. Make it unique first."));
|
||||||
|
err_dialog->popup_centered();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (FileAccess::exists(base + ".import")) {
|
||||||
|
err_dialog->set_text(TTR("Cannot generate navigation mesh because it belongs to a resource which was imported."));
|
||||||
|
err_dialog->popup_centered();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (FileAccess::exists(path + ".import")) {
|
||||||
|
err_dialog->set_text(TTR("Cannot generate navigation mesh because the resource was imported from another type."));
|
||||||
|
err_dialog->popup_centered();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
regions_to_bake.push_back(region);
|
||||||
|
|
||||||
|
if (unique_navmeshes.has(navmesh)) {
|
||||||
|
// No point (re)baking the same resource in case of multi select.
|
||||||
|
// Trying to bake the same navmesh twice would trigger an error.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
unique_navmeshes.insert(navmesh);
|
||||||
|
regions_with_navmesh_to_bake.push_back(region);
|
||||||
}
|
}
|
||||||
|
|
||||||
node->bake_navigation_mesh(true);
|
if (!regions_with_navmesh_to_bake.is_empty()) {
|
||||||
|
multibake_dialog->set_ok_button_text(TTR("Bake"));
|
||||||
node->update_gizmos();
|
multibake_dialog->get_ok_button()->set_disabled(false);
|
||||||
}
|
multibake_dialog->set_text("Attempting to bake " + itos(regions_with_navmesh_to_bake.size()) + " unique navmesh(es) from " + itos(regions_to_bake.size()) + " selected NavigationRegion3D node(s).\n\nThis can take some time and freeze the Editor temporarily.\n\nDo NOT change nodes by any means while the baking is parsing the SceneTree.");
|
||||||
|
multibake_dialog->popup_centered();
|
||||||
void NavigationRegion3DEditor::_clear_pressed() {
|
if (regions_with_navmesh_to_bake.size() == 1) {
|
||||||
if (node) {
|
// If we only have a single region start bake immediately.
|
||||||
if (node->get_navigation_mesh().is_valid()) {
|
_on_navmesh_multibake_confirmed();
|
||||||
node->get_navigation_mesh()->clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
button_bake->set_pressed(false);
|
|
||||||
bake_info->set_text("");
|
|
||||||
|
|
||||||
if (node) {
|
|
||||||
node->update_gizmos();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavigationRegion3DEditor::edit(NavigationRegion3D *p_nav_region) {
|
void NavigationRegion3DEditor::_on_navmesh_multibake_confirmed() {
|
||||||
if (p_nav_region == nullptr || node == p_nav_region) {
|
multibake_dialog->get_ok_button()->set_disabled(true);
|
||||||
|
|
||||||
|
bake_in_process = true;
|
||||||
|
region_baking_canceled = false;
|
||||||
|
processed_regions_to_bake_count = 0;
|
||||||
|
processed_regions_to_bake_count_max = regions_with_navmesh_to_bake.size();
|
||||||
|
|
||||||
|
set_process(true);
|
||||||
|
_process_regions_to_bake();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavigationRegion3DEditor::_process_regions_to_bake() {
|
||||||
|
if (region_baking_canceled) {
|
||||||
|
region_baking_canceled = false;
|
||||||
|
regions_with_navmesh_to_bake.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (regions_with_navmesh_to_bake.is_empty()) {
|
||||||
|
regions_to_bake.clear();
|
||||||
|
multibake_dialog->set_visible(false);
|
||||||
|
set_process(false);
|
||||||
|
currently_baking_region = nullptr;
|
||||||
|
bake_in_process = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = p_nav_region;
|
NavigationRegion3D *region_to_bake = regions_with_navmesh_to_bake[0];
|
||||||
|
regions_with_navmesh_to_bake.remove_at_unordered(0);
|
||||||
|
processed_regions_to_bake_count += 1;
|
||||||
|
if (region_to_bake && region_to_bake->get_navigation_mesh().is_valid()) {
|
||||||
|
currently_baking_region = region_to_bake;
|
||||||
|
region_to_bake->connect(SNAME("bake_finished"), callable_mp(this, &NavigationRegion3DEditor::_process_regions_to_bake), CONNECT_ONE_SHOT);
|
||||||
|
region_to_bake->bake_navigation_mesh(true);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
_process_regions_to_bake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavigationRegion3DEditor::_on_navmesh_multibake_canceled() {
|
||||||
|
if (bake_in_process) {
|
||||||
|
region_baking_canceled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
multibake_dialog->set_visible(false);
|
||||||
|
regions_to_bake.clear();
|
||||||
|
regions_with_navmesh_to_bake.clear();
|
||||||
|
processed_regions_to_bake_count = 0;
|
||||||
|
processed_regions_to_bake_count_max = 0;
|
||||||
|
region_baking_canceled = false;
|
||||||
|
currently_baking_region = nullptr;
|
||||||
|
bake_in_process = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavigationRegion3DEditor::_clear_pressed() {
|
||||||
|
button_bake->set_pressed(false);
|
||||||
|
bake_info->set_text("");
|
||||||
|
|
||||||
|
if (!selected_regions.is_empty()) {
|
||||||
|
for (NavigationRegion3D *region : selected_regions) {
|
||||||
|
if (region->get_navigation_mesh().is_valid()) {
|
||||||
|
region->get_navigation_mesh()->clear();
|
||||||
|
region->update_gizmos();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavigationRegion3DEditor::edit(LocalVector<NavigationRegion3D *> p_regions) {
|
||||||
|
if (p_regions.is_empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selected_regions = p_regions;
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationRegion3DEditor::NavigationRegion3DEditor() {
|
NavigationRegion3DEditor::NavigationRegion3DEditor() {
|
||||||
@ -146,15 +249,56 @@ NavigationRegion3DEditor::NavigationRegion3DEditor() {
|
|||||||
|
|
||||||
err_dialog = memnew(AcceptDialog);
|
err_dialog = memnew(AcceptDialog);
|
||||||
add_child(err_dialog);
|
add_child(err_dialog);
|
||||||
node = nullptr;
|
|
||||||
|
multibake_dialog = memnew(ConfirmationDialog);
|
||||||
|
add_child(multibake_dialog);
|
||||||
|
multibake_dialog->connect(SceneStringName(confirmed), callable_mp(this, &NavigationRegion3DEditor::_on_navmesh_multibake_confirmed));
|
||||||
|
multibake_dialog->connect(SNAME("canceled"), callable_mp(this, &NavigationRegion3DEditor::_on_navmesh_multibake_canceled));
|
||||||
|
multibake_dialog->set_hide_on_ok(false);
|
||||||
|
multibake_dialog->set_title(TTR("Baking NavigationMesh ..."));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavigationRegion3DEditorPlugin::edit(Object *p_object) {
|
void NavigationRegion3DEditorPlugin::edit(Object *p_object) {
|
||||||
navigation_region_editor->edit(Object::cast_to<NavigationRegion3D>(p_object));
|
LocalVector<NavigationRegion3D *> regions;
|
||||||
|
|
||||||
|
{
|
||||||
|
NavigationRegion3D *region = Object::cast_to<NavigationRegion3D>(p_object);
|
||||||
|
if (region) {
|
||||||
|
regions.push_back(region);
|
||||||
|
navigation_region_editor->edit(regions);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<MultiNodeEdit> mne = Ref<MultiNodeEdit>(p_object);
|
||||||
|
Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
|
||||||
|
if (mne.is_valid() && edited_scene) {
|
||||||
|
for (int i = 0; i < mne->get_node_count(); i++) {
|
||||||
|
NavigationRegion3D *region = Object::cast_to<NavigationRegion3D>(edited_scene->get_node(mne->get_node(i)));
|
||||||
|
if (region) {
|
||||||
|
regions.push_back(region);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
navigation_region_editor->edit(regions);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NavigationRegion3DEditorPlugin::handles(Object *p_object) const {
|
bool NavigationRegion3DEditorPlugin::handles(Object *p_object) const {
|
||||||
return p_object->is_class("NavigationRegion3D");
|
if (Object::cast_to<NavigationRegion3D>(p_object)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<MultiNodeEdit> mne = Ref<MultiNodeEdit>(p_object);
|
||||||
|
Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
|
||||||
|
if (mne.is_valid() && edited_scene) {
|
||||||
|
for (int i = 0; i < mne->get_node_count(); i++) {
|
||||||
|
if (Object::cast_to<NavigationRegion3D>(edited_scene->get_node(mne->get_node(i)))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavigationRegion3DEditorPlugin::make_visible(bool p_visible) {
|
void NavigationRegion3DEditorPlugin::make_visible(bool p_visible) {
|
||||||
@ -164,7 +308,7 @@ void NavigationRegion3DEditorPlugin::make_visible(bool p_visible) {
|
|||||||
} else {
|
} else {
|
||||||
navigation_region_editor->hide();
|
navigation_region_editor->hide();
|
||||||
navigation_region_editor->bake_hbox->hide();
|
navigation_region_editor->bake_hbox->hide();
|
||||||
navigation_region_editor->edit(nullptr);
|
navigation_region_editor->edit(LocalVector<NavigationRegion3D *>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
|
|
||||||
class AcceptDialog;
|
class AcceptDialog;
|
||||||
class Button;
|
class Button;
|
||||||
|
class ConfirmationDialog;
|
||||||
class HBoxContainer;
|
class HBoxContainer;
|
||||||
class Label;
|
class Label;
|
||||||
class NavigationRegion3D;
|
class NavigationRegion3D;
|
||||||
@ -46,23 +47,38 @@ class NavigationRegion3DEditor : public Control {
|
|||||||
GDCLASS(NavigationRegion3DEditor, Control);
|
GDCLASS(NavigationRegion3DEditor, Control);
|
||||||
|
|
||||||
AcceptDialog *err_dialog = nullptr;
|
AcceptDialog *err_dialog = nullptr;
|
||||||
|
ConfirmationDialog *multibake_dialog = nullptr;
|
||||||
|
|
||||||
HBoxContainer *bake_hbox = nullptr;
|
HBoxContainer *bake_hbox = nullptr;
|
||||||
Button *button_bake = nullptr;
|
Button *button_bake = nullptr;
|
||||||
Button *button_reset = nullptr;
|
Button *button_reset = nullptr;
|
||||||
Label *bake_info = nullptr;
|
Label *bake_info = nullptr;
|
||||||
|
|
||||||
NavigationRegion3D *node = nullptr;
|
LocalVector<NavigationRegion3D *> selected_regions;
|
||||||
|
|
||||||
|
LocalVector<NavigationRegion3D *> regions_to_bake;
|
||||||
|
LocalVector<NavigationRegion3D *> regions_with_navmesh_to_bake;
|
||||||
|
|
||||||
|
int processed_regions_to_bake_count = 0;
|
||||||
|
int processed_regions_to_bake_count_max = 0;
|
||||||
|
bool region_baking_canceled = false;
|
||||||
|
NavigationRegion3D *currently_baking_region = nullptr;
|
||||||
|
|
||||||
|
bool bake_in_process = false;
|
||||||
|
|
||||||
void _bake_pressed();
|
void _bake_pressed();
|
||||||
void _clear_pressed();
|
void _clear_pressed();
|
||||||
|
|
||||||
|
void _on_navmesh_multibake_confirmed();
|
||||||
|
void _on_navmesh_multibake_canceled();
|
||||||
|
void _process_regions_to_bake();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void _node_removed(Node *p_node);
|
void _node_removed(Node *p_node);
|
||||||
void _notification(int p_what);
|
void _notification(int p_what);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void edit(NavigationRegion3D *p_nav_region);
|
void edit(LocalVector<NavigationRegion3D *> p_regions);
|
||||||
NavigationRegion3DEditor();
|
NavigationRegion3DEditor();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -286,6 +286,7 @@ public:
|
|||||||
virtual void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
|
virtual void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
|
||||||
virtual void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
|
virtual void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) = 0;
|
||||||
virtual bool is_baking_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh) const = 0;
|
virtual bool is_baking_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh) const = 0;
|
||||||
|
virtual String get_baking_navigation_mesh_state_msg(Ref<NavigationMesh> p_navigation_mesh) const = 0;
|
||||||
#endif // _3D_DISABLED
|
#endif // _3D_DISABLED
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -191,6 +191,7 @@ public:
|
|||||||
void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
|
void bake_from_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
|
||||||
void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
|
void bake_from_source_geometry_data_async(const Ref<NavigationMesh> &p_navigation_mesh, const Ref<NavigationMeshSourceGeometryData3D> &p_source_geometry_data, const Callable &p_callback = Callable()) override {}
|
||||||
bool is_baking_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh) const override { return false; }
|
bool is_baking_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh) const override { return false; }
|
||||||
|
String get_baking_navigation_mesh_state_msg(Ref<NavigationMesh> p_navigation_mesh) const override { return ""; }
|
||||||
#endif // _3D_DISABLED
|
#endif // _3D_DISABLED
|
||||||
|
|
||||||
RID source_geometry_parser_create() override { return RID(); }
|
RID source_geometry_parser_create() override { return RID(); }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user