1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-08 12:40:44 +00:00

Portal occlusion culling

Adds support for occlusion culling via rooms and portals.
This commit is contained in:
lawnjelly
2021-02-04 10:43:08 +00:00
parent b0b2b7df31
commit eb6f98ec55
78 changed files with 10865 additions and 45 deletions

View File

@@ -101,7 +101,7 @@ VisualServerScene::SpatialPartitionID VisualServerScene::SpatialPartitioningScen
#if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED)
// we are relying on this instance to be valid in order to pass
// the visible flag to the bvh.
CRASH_COND(!p_userdata);
DEV_ASSERT(p_userdata);
#endif
return _bvh.create(p_userdata, p_userdata->visible, p_aabb, p_subindex, p_pairable, p_pairable_type, p_pairable_mask) + 1;
}
@@ -601,6 +601,11 @@ void VisualServerScene::instance_set_scenario(RID p_instance, RID p_scenario) {
instance->spatial_partition_id = 0;
}
// handle occlusion changes
if (instance->occlusion_handle) {
_instance_destroy_occlusion_rep(instance);
}
switch (instance->base_type) {
case VS::INSTANCE_LIGHT: {
InstanceLightData *light = static_cast<InstanceLightData *>(instance->base_data);
@@ -653,6 +658,9 @@ void VisualServerScene::instance_set_scenario(RID p_instance, RID p_scenario) {
}
}
// handle occlusion changes if necessary
_instance_create_occlusion_rep(instance);
_instance_queue_update(instance, true, true);
}
}
@@ -889,6 +897,412 @@ void VisualServerScene::instance_set_extra_visibility_margin(RID p_instance, rea
_instance_queue_update(instance, true, false);
}
// Portals
void VisualServerScene::instance_set_portal_mode(RID p_instance, VisualServer::InstancePortalMode p_mode) {
Instance *instance = instance_owner.get(p_instance);
ERR_FAIL_COND(!instance);
// no change?
if (instance->portal_mode == p_mode) {
return;
}
// should this happen?
if (!instance->scenario) {
instance->portal_mode = p_mode;
return;
}
// destroy previous occlusion instance?
_instance_destroy_occlusion_rep(instance);
instance->portal_mode = p_mode;
_instance_create_occlusion_rep(instance);
}
void VisualServerScene::_instance_create_occlusion_rep(Instance *p_instance) {
ERR_FAIL_COND(!p_instance);
ERR_FAIL_COND(!p_instance->scenario);
switch (p_instance->portal_mode) {
default: {
p_instance->occlusion_handle = 0;
} break;
case VisualServer::InstancePortalMode::INSTANCE_PORTAL_MODE_ROAMING: {
p_instance->occlusion_handle = p_instance->scenario->_portal_renderer.instance_moving_create(p_instance, p_instance->self, false, p_instance->transformed_aabb);
} break;
case VisualServer::InstancePortalMode::INSTANCE_PORTAL_MODE_GLOBAL: {
p_instance->occlusion_handle = p_instance->scenario->_portal_renderer.instance_moving_create(p_instance, p_instance->self, true, p_instance->transformed_aabb);
} break;
}
}
void VisualServerScene::_instance_destroy_occlusion_rep(Instance *p_instance) {
ERR_FAIL_COND(!p_instance);
ERR_FAIL_COND(!p_instance->scenario);
// not an error, can occur
if (!p_instance->occlusion_handle) {
return;
}
p_instance->scenario->_portal_renderer.instance_moving_destroy(p_instance->occlusion_handle);
// unset
p_instance->occlusion_handle = 0;
}
void *VisualServerScene::_instance_get_from_rid(RID p_instance) {
Instance *instance = instance_owner.get(p_instance);
return instance;
}
bool VisualServerScene::_instance_get_transformed_aabb(RID p_instance, AABB &r_aabb) {
Instance *instance = instance_owner.get(p_instance);
ERR_FAIL_NULL_V(instance, false);
r_aabb = instance->transformed_aabb;
return true;
}
// the portal has to be associated with a scenario, this is assumed to be
// the same scenario as the portal node
RID VisualServerScene::portal_create() {
Portal *portal = memnew(Portal);
ERR_FAIL_COND_V(!portal, RID());
RID portal_rid = portal_owner.make_rid(portal);
return portal_rid;
}
// should not be called multiple times, different scenarios etc, but just in case, we will support this
void VisualServerScene::portal_set_scenario(RID p_portal, RID p_scenario) {
Portal *portal = portal_owner.getornull(p_portal);
ERR_FAIL_COND(!portal);
Scenario *scenario = scenario_owner.getornull(p_scenario);
// noop?
if (portal->scenario == scenario) {
return;
}
// if the portal is in a scenario already, remove it
if (portal->scenario) {
portal->scenario->_portal_renderer.portal_destroy(portal->scenario_portal_id);
portal->scenario = nullptr;
portal->scenario_portal_id = 0;
}
// create when entering the world
if (scenario) {
portal->scenario = scenario;
// defer the actual creation to here
portal->scenario_portal_id = scenario->_portal_renderer.portal_create();
}
}
void VisualServerScene::portal_set_geometry(RID p_portal, const Vector<Vector3> &p_points, float p_margin) {
Portal *portal = portal_owner.getornull(p_portal);
ERR_FAIL_COND(!portal);
ERR_FAIL_COND(!portal->scenario);
portal->scenario->_portal_renderer.portal_set_geometry(portal->scenario_portal_id, p_points);
}
void VisualServerScene::portal_link(RID p_portal, RID p_room_from, RID p_room_to, bool p_two_way) {
Portal *portal = portal_owner.getornull(p_portal);
ERR_FAIL_COND(!portal);
ERR_FAIL_COND(!portal->scenario);
Room *room_from = room_owner.getornull(p_room_from);
ERR_FAIL_COND(!room_from);
Room *room_to = room_owner.getornull(p_room_to);
ERR_FAIL_COND(!room_to);
portal->scenario->_portal_renderer.portal_link(portal->scenario_portal_id, room_from->scenario_room_id, room_to->scenario_room_id, p_two_way);
}
void VisualServerScene::portal_set_active(RID p_portal, bool p_active) {
Portal *portal = portal_owner.getornull(p_portal);
ERR_FAIL_COND(!portal);
ERR_FAIL_COND(!portal->scenario);
portal->scenario->_portal_renderer.portal_set_active(portal->scenario_portal_id, p_active);
}
RID VisualServerScene::ghost_create() {
Ghost *ci = memnew(Ghost);
ERR_FAIL_COND_V(!ci, RID());
RID ci_rid = ghost_owner.make_rid(ci);
return ci_rid;
}
void VisualServerScene::ghost_set_scenario(RID p_ghost, RID p_scenario, ObjectID p_id, const AABB &p_aabb) {
Ghost *ci = ghost_owner.getornull(p_ghost);
ERR_FAIL_COND(!ci);
ci->aabb = p_aabb;
ci->object_id = p_id;
Scenario *scenario = scenario_owner.getornull(p_scenario);
// noop?
if (ci->scenario == scenario) {
return;
}
// if the portal is in a scenario already, remove it
if (ci->scenario) {
_ghost_destroy_occlusion_rep(ci);
ci->scenario = nullptr;
}
// create when entering the world
if (scenario) {
ci->scenario = scenario;
// defer the actual creation to here
_ghost_create_occlusion_rep(ci);
}
}
void VisualServerScene::ghost_update(RID p_ghost, const AABB &p_aabb) {
Ghost *ci = ghost_owner.getornull(p_ghost);
ERR_FAIL_COND(!ci);
ERR_FAIL_COND(!ci->scenario);
ci->aabb = p_aabb;
if (ci->rghost_handle) {
ci->scenario->_portal_renderer.rghost_update(ci->rghost_handle, p_aabb);
}
}
void VisualServerScene::_ghost_create_occlusion_rep(Ghost *p_ghost) {
ERR_FAIL_COND(!p_ghost);
ERR_FAIL_COND(!p_ghost->scenario);
if (!p_ghost->rghost_handle) {
p_ghost->rghost_handle = p_ghost->scenario->_portal_renderer.rghost_create(p_ghost->object_id, p_ghost->aabb);
}
}
void VisualServerScene::_ghost_destroy_occlusion_rep(Ghost *p_ghost) {
ERR_FAIL_COND(!p_ghost);
ERR_FAIL_COND(!p_ghost->scenario);
// not an error, can occur
if (!p_ghost->rghost_handle) {
return;
}
p_ghost->scenario->_portal_renderer.rghost_destroy(p_ghost->rghost_handle);
p_ghost->rghost_handle = 0;
}
RID VisualServerScene::roomgroup_create() {
RoomGroup *rg = memnew(RoomGroup);
ERR_FAIL_COND_V(!rg, RID());
RID roomgroup_rid = roomgroup_owner.make_rid(rg);
return roomgroup_rid;
}
void VisualServerScene::roomgroup_prepare(RID p_roomgroup, ObjectID p_roomgroup_object_id) {
RoomGroup *roomgroup = roomgroup_owner.getornull(p_roomgroup);
ERR_FAIL_COND(!roomgroup);
ERR_FAIL_COND(!roomgroup->scenario);
roomgroup->scenario->_portal_renderer.roomgroup_prepare(roomgroup->scenario_roomgroup_id, p_roomgroup_object_id);
}
void VisualServerScene::roomgroup_set_scenario(RID p_roomgroup, RID p_scenario) {
RoomGroup *rg = roomgroup_owner.getornull(p_roomgroup);
ERR_FAIL_COND(!rg);
Scenario *scenario = scenario_owner.getornull(p_scenario);
// noop?
if (rg->scenario == scenario) {
return;
}
// if the portal is in a scenario already, remove it
if (rg->scenario) {
rg->scenario->_portal_renderer.roomgroup_destroy(rg->scenario_roomgroup_id);
rg->scenario = nullptr;
rg->scenario_roomgroup_id = 0;
}
// create when entering the world
if (scenario) {
rg->scenario = scenario;
// defer the actual creation to here
rg->scenario_roomgroup_id = scenario->_portal_renderer.roomgroup_create();
}
}
void VisualServerScene::roomgroup_add_room(RID p_roomgroup, RID p_room) {
RoomGroup *roomgroup = roomgroup_owner.getornull(p_roomgroup);
ERR_FAIL_COND(!roomgroup);
ERR_FAIL_COND(!roomgroup->scenario);
Room *room = room_owner.getornull(p_room);
ERR_FAIL_COND(!room);
ERR_FAIL_COND(!room->scenario);
ERR_FAIL_COND(roomgroup->scenario != room->scenario);
roomgroup->scenario->_portal_renderer.roomgroup_add_room(roomgroup->scenario_roomgroup_id, room->scenario_room_id);
}
// Rooms
void VisualServerScene::callbacks_register(VisualServerCallbacks *p_callbacks) {
_visual_server_callbacks = p_callbacks;
}
// the room has to be associated with a scenario, this is assumed to be
// the same scenario as the room node
RID VisualServerScene::room_create() {
Room *room = memnew(Room);
ERR_FAIL_COND_V(!room, RID());
RID room_rid = room_owner.make_rid(room);
return room_rid;
}
// should not be called multiple times, different scenarios etc, but just in case, we will support this
void VisualServerScene::room_set_scenario(RID p_room, RID p_scenario) {
Room *room = room_owner.getornull(p_room);
ERR_FAIL_COND(!room);
Scenario *scenario = scenario_owner.getornull(p_scenario);
// no change?
if (room->scenario == scenario) {
return;
}
// if the room has an existing scenario, remove from it
if (room->scenario) {
room->scenario->_portal_renderer.room_destroy(room->scenario_room_id);
room->scenario = nullptr;
room->scenario_room_id = 0;
}
// create when entering the world
if (scenario) {
room->scenario = scenario;
// defer the actual creation to here
room->scenario_room_id = scenario->_portal_renderer.room_create();
}
}
void VisualServerScene::room_add_ghost(RID p_room, ObjectID p_object_id, const AABB &p_aabb) {
Room *room = room_owner.getornull(p_room);
ERR_FAIL_COND(!room);
ERR_FAIL_COND(!room->scenario);
room->scenario->_portal_renderer.room_add_ghost(room->scenario_room_id, p_object_id, p_aabb);
}
void VisualServerScene::room_add_instance(RID p_room, RID p_instance, const AABB &p_aabb, const Vector<Vector3> &p_object_pts) {
Room *room = room_owner.getornull(p_room);
ERR_FAIL_COND(!room);
ERR_FAIL_COND(!room->scenario);
Instance *instance = instance_owner.getornull(p_instance);
ERR_FAIL_COND(!instance);
AABB bb = p_aabb;
// the aabb passed from the client takes no account of the extra cull margin,
// so we need to add this manually.
// It is assumed it is in world space.
if (instance->extra_margin != 0.0) {
bb.grow_by(instance->extra_margin);
}
bool dynamic = false;
// don't add if portal mode is not static or dynamic
switch (instance->portal_mode) {
default: {
return; // this should be taken care of by the calling function, but just in case
} break;
case VisualServer::InstancePortalMode::INSTANCE_PORTAL_MODE_DYNAMIC: {
dynamic = true;
} break;
case VisualServer::InstancePortalMode::INSTANCE_PORTAL_MODE_STATIC: {
dynamic = false;
} break;
}
instance->occlusion_handle = room->scenario->_portal_renderer.room_add_instance(room->scenario_room_id, p_instance, bb, dynamic, p_object_pts);
}
void VisualServerScene::room_prepare(RID p_room, int32_t p_priority) {
Room *room = room_owner.getornull(p_room);
ERR_FAIL_COND(!room);
ERR_FAIL_COND(!room->scenario);
room->scenario->_portal_renderer.room_prepare(room->scenario_room_id, p_priority);
}
void VisualServerScene::room_set_bound(RID p_room, ObjectID p_room_object_id, const Vector<Plane> &p_convex, const AABB &p_aabb, const Vector<Vector3> &p_verts) {
Room *room = room_owner.getornull(p_room);
ERR_FAIL_COND(!room);
ERR_FAIL_COND(!room->scenario);
room->scenario->_portal_renderer.room_set_bound(room->scenario_room_id, p_room_object_id, p_convex, p_aabb, p_verts);
}
void VisualServerScene::rooms_unload(RID p_scenario) {
Scenario *scenario = scenario_owner.getornull(p_scenario);
ERR_FAIL_COND(!scenario);
scenario->_portal_renderer.rooms_unload();
}
void VisualServerScene::rooms_and_portals_clear(RID p_scenario) {
Scenario *scenario = scenario_owner.getornull(p_scenario);
ERR_FAIL_COND(!scenario);
scenario->_portal_renderer.rooms_and_portals_clear();
}
void VisualServerScene::rooms_finalize(RID p_scenario, bool p_generate_pvs, bool p_cull_using_pvs, bool p_use_secondary_pvs, bool p_use_signals, String p_pvs_filename) {
Scenario *scenario = scenario_owner.getornull(p_scenario);
ERR_FAIL_COND(!scenario);
scenario->_portal_renderer.rooms_finalize(p_generate_pvs, p_cull_using_pvs, p_use_secondary_pvs, p_use_signals, p_pvs_filename);
}
void VisualServerScene::rooms_override_camera(RID p_scenario, bool p_override, const Vector3 &p_point, const Vector<Plane> *p_convex) {
Scenario *scenario = scenario_owner.getornull(p_scenario);
ERR_FAIL_COND(!scenario);
scenario->_portal_renderer.rooms_override_camera(p_override, p_point, p_convex);
}
void VisualServerScene::rooms_set_active(RID p_scenario, bool p_active) {
Scenario *scenario = scenario_owner.getornull(p_scenario);
ERR_FAIL_COND(!scenario);
scenario->_portal_renderer.rooms_set_active(p_active);
}
void VisualServerScene::rooms_set_params(RID p_scenario, int p_portal_depth_limit) {
Scenario *scenario = scenario_owner.getornull(p_scenario);
ERR_FAIL_COND(!scenario);
scenario->_portal_renderer.rooms_set_params(p_portal_depth_limit);
}
void VisualServerScene::rooms_set_debug_feature(RID p_scenario, VisualServer::RoomsDebugFeature p_feature, bool p_active) {
Scenario *scenario = scenario_owner.getornull(p_scenario);
ERR_FAIL_COND(!scenario);
switch (p_feature) {
default: {
} break;
case VisualServer::ROOMS_DEBUG_SPRAWL: {
scenario->_portal_renderer.set_debug_sprawl(p_active);
} break;
}
}
void VisualServerScene::rooms_update_gameplay_monitor(RID p_scenario, const Vector<Vector3> &p_camera_positions) {
Scenario *scenario = scenario_owner.getornull(p_scenario);
ERR_FAIL_COND(!scenario);
scenario->_portal_renderer.rooms_update_gameplay_monitor(p_camera_positions);
}
Vector<ObjectID> VisualServerScene::instances_cull_aabb(const AABB &p_aabb, RID p_scenario) const {
Vector<ObjectID> instances;
Scenario *scenario = scenario_owner.get(p_scenario);
@@ -958,6 +1372,40 @@ Vector<ObjectID> VisualServerScene::instances_cull_convex(const Vector<Plane> &p
return instances;
}
// thin wrapper to allow rooms / portals to take over culling if active
int VisualServerScene::_cull_convex_from_point(Scenario *p_scenario, const Vector3 &p_point, const Vector<Plane> &p_convex, Instance **p_result_array, int p_result_max, int32_t &r_previous_room_id_hint, uint32_t p_mask) {
int res = -1;
if (p_scenario->_portal_renderer.is_active()) {
// Note that the portal renderer ASSUMES that the planes exactly match the convention in
// CameraMatrix of enum Planes (6 planes, in order, near, far etc)
// If this is not the case, it should not be used.
res = p_scenario->_portal_renderer.cull_convex(p_point, p_convex, (VSInstance **)p_result_array, p_result_max, p_mask, r_previous_room_id_hint);
}
// fallback to BVH / octree if portals not active
if (res == -1) {
res = p_scenario->sps->cull_convex(p_convex, p_result_array, p_result_max, p_mask);
}
return res;
}
void VisualServerScene::_rooms_instance_update(Instance *p_instance, const AABB &p_aabb) {
// magic number for instances in the room / portal system, but not requiring an update
// (due to being a STATIC or DYNAMIC object within a room)
// Must match the value in PortalRenderer in VisualServer
const uint32_t OCCLUSION_HANDLE_ROOM_BIT = 1 << 31;
// if the instance is a moving object in the room / portal system, update it
// Note that if rooms and portals is not in use, occlusion_handle should be zero in all cases unless the portal_mode
// has been set to global or roaming. (which is unlikely as the default is static).
// The exception is editor user interface elements.
// These are always set to global and will always keep their aabb up to date in the portal renderer unnecessarily.
// There is no easy way around this, but it should be very cheap, and have no impact outside the editor.
if (p_instance->occlusion_handle && (p_instance->occlusion_handle != OCCLUSION_HANDLE_ROOM_BIT)) {
p_instance->scenario->_portal_renderer.instance_moving_update(p_instance->occlusion_handle, p_aabb);
}
}
void VisualServerScene::instance_geometry_set_flag(RID p_instance, VS::InstanceFlags p_flags, bool p_enabled) {
Instance *instance = instance_owner.get(p_instance);
ERR_FAIL_COND(!instance);
@@ -1094,6 +1542,9 @@ void VisualServerScene::_update_instance(Instance *p_instance) {
p_instance->scenario->sps->move(p_instance->spatial_partition_id, new_aabb);
}
// keep rooms and portals instance up to date if present
_rooms_instance_update(p_instance, new_aabb);
}
void VisualServerScene::_update_instance_aabb(Instance *p_instance) {
@@ -1746,7 +2197,7 @@ bool VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
Vector<Plane> planes = cm.get_projection_planes(xform);
int cull_count = p_scenario->sps->cull_convex(planes, instance_shadow_cull_result, MAX_INSTANCE_CULL, VS::INSTANCE_GEOMETRY_MASK);
int cull_count = _cull_convex_from_point(p_scenario, light_transform.origin, planes, instance_shadow_cull_result, MAX_INSTANCE_CULL, light->previous_room_id_hint, VS::INSTANCE_GEOMETRY_MASK);
Plane near_plane(xform.origin, -xform.basis.get_axis(2));
for (int j = 0; j < cull_count; j++) {
@@ -1781,7 +2232,7 @@ bool VisualServerScene::_light_instance_update_shadow(Instance *p_instance, cons
cm.set_perspective(angle * 2.0, 1.0, 0.01, radius);
Vector<Plane> planes = cm.get_projection_planes(light_transform);
int cull_count = p_scenario->sps->cull_convex(planes, instance_shadow_cull_result, MAX_INSTANCE_CULL, VS::INSTANCE_GEOMETRY_MASK);
int cull_count = _cull_convex_from_point(p_scenario, light_transform.origin, planes, instance_shadow_cull_result, MAX_INSTANCE_CULL, light->previous_room_id_hint, VS::INSTANCE_GEOMETRY_MASK);
Plane near_plane(light_transform.origin, -light_transform.basis.get_axis(2));
for (int j = 0; j < cull_count; j++) {
@@ -1851,7 +2302,7 @@ void VisualServerScene::render_camera(RID p_camera, RID p_scenario, Size2 p_view
} break;
}
_prepare_scene(camera->transform, camera_matrix, ortho, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID());
_prepare_scene(camera->transform, camera_matrix, ortho, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), camera->previous_room_id_hint);
_render_scene(camera->transform, camera_matrix, 0, ortho, camera->env, p_scenario, p_shadow_atlas, RID(), -1);
#endif
}
@@ -1930,17 +2381,17 @@ void VisualServerScene::render_camera(Ref<ARVRInterface> &p_interface, ARVRInter
mono_transform *= apply_z_shift;
// now prepare our scene with our adjusted transform projection matrix
_prepare_scene(mono_transform, combined_matrix, false, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID());
_prepare_scene(mono_transform, combined_matrix, false, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), camera->previous_room_id_hint);
} else if (p_eye == ARVRInterface::EYE_MONO) {
// For mono render, prepare as per usual
_prepare_scene(cam_transform, camera_matrix, false, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID());
_prepare_scene(cam_transform, camera_matrix, false, camera->env, camera->visible_layers, p_scenario, p_shadow_atlas, RID(), camera->previous_room_id_hint);
}
// And render our scene...
_render_scene(cam_transform, camera_matrix, p_eye, false, camera->env, p_scenario, p_shadow_atlas, RID(), -1);
};
void VisualServerScene::_prepare_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe) {
void VisualServerScene::_prepare_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int32_t &r_previous_room_id_hint) {
// Note, in stereo rendering:
// - p_cam_transform will be a transform in the middle of our two eyes
// - p_cam_projection is a wider frustrum that encompasses both eyes
@@ -1960,7 +2411,7 @@ void VisualServerScene::_prepare_scene(const Transform p_cam_transform, const Ca
float z_far = p_cam_projection.get_z_far();
/* STEP 2 - CULL */
instance_cull_count = scenario->sps->cull_convex(planes, instance_cull_result, MAX_INSTANCE_CULL);
instance_cull_count = _cull_convex_from_point(scenario, p_cam_transform.origin, planes, instance_cull_result, MAX_INSTANCE_CULL, r_previous_room_id_hint);
light_cull_count = 0;
reflection_probe_cull_count = 0;
@@ -2339,7 +2790,7 @@ bool VisualServerScene::_render_reflection_probe_step(Instance *p_instance, int
shadow_atlas = scenario->reflection_probe_shadow_atlas;
}
_prepare_scene(xform, cm, false, RID(), VSG::storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, shadow_atlas, reflection_probe->instance);
_prepare_scene(xform, cm, false, RID(), VSG::storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, shadow_atlas, reflection_probe->instance, reflection_probe->previous_room_id_hint);
_render_scene(xform, cm, 0, false, RID(), p_instance->scenario->self, shadow_atlas, reflection_probe->instance, p_step);
} else {
@@ -3524,6 +3975,22 @@ bool VisualServerScene::free(RID p_rid) {
instance_owner.free(p_rid);
memdelete(instance);
} else if (room_owner.owns(p_rid)) {
Room *room = room_owner.get(p_rid);
room_owner.free(p_rid);
memdelete(room);
} else if (portal_owner.owns(p_rid)) {
Portal *portal = portal_owner.get(p_rid);
portal_owner.free(p_rid);
memdelete(portal);
} else if (ghost_owner.owns(p_rid)) {
Ghost *ghost = ghost_owner.get(p_rid);
ghost_owner.free(p_rid);
memdelete(ghost);
} else if (roomgroup_owner.owns(p_rid)) {
RoomGroup *roomgroup = roomgroup_owner.get(p_rid);
roomgroup_owner.free(p_rid);
memdelete(roomgroup);
} else {
return false;
}
@@ -3540,6 +4007,7 @@ VisualServerScene::VisualServerScene() {
render_pass = 1;
singleton = this;
_use_bvh = GLOBAL_DEF("rendering/quality/spatial_partitioning/use_bvh", true);
_visual_server_callbacks = nullptr;
}
VisualServerScene::~VisualServerScene() {