From 3c2df498321773a26f80d33d95438fb12f19d477 Mon Sep 17 00:00:00 2001 From: lawnjelly Date: Fri, 11 Feb 2022 14:36:02 +0000 Subject: [PATCH] Fix Occluder to properly share resources In order to properly support the resource sharing paradigm, Occluders are split into Instances and Resources in the VisualServer. Instances are owned by a Scenario, and Resources are global. OccluderShape resources can now correctly be shared by multiple OccluderInstances. --- doc/classes/OccluderShapeSphere.xml | 2 +- scene/3d/occluder.cpp | 47 ++-- scene/3d/occluder.h | 1 + scene/resources/occluder_shape.cpp | 38 ++- scene/resources/occluder_shape.h | 10 +- scene/resources/occluder_shape_polygon.cpp | 18 +- scene/resources/occluder_shape_polygon.h | 3 +- .../portals/portal_occlusion_culler.cpp | 36 +-- .../visual/portals/portal_occlusion_culler.h | 6 +- servers/visual/portals/portal_renderer.cpp | 227 ++++++------------ servers/visual/portals/portal_renderer.h | 181 ++++++++++---- servers/visual/portals/portal_resources.cpp | 216 +++++++++++++++++ servers/visual/portals/portal_resources.h | 68 ++++++ servers/visual/portals/portal_types.h | 64 ++--- servers/visual/visual_server_raster.h | 16 +- servers/visual/visual_server_scene.cpp | 106 +++++--- servers/visual/visual_server_scene.h | 39 ++- servers/visual/visual_server_wrap_mt.cpp | 3 +- servers/visual/visual_server_wrap_mt.h | 16 +- servers/visual_server.h | 17 +- 20 files changed, 728 insertions(+), 386 deletions(-) create mode 100644 servers/visual/portals/portal_resources.cpp create mode 100644 servers/visual/portals/portal_resources.h diff --git a/doc/classes/OccluderShapeSphere.xml b/doc/classes/OccluderShapeSphere.xml index 9bfb9e71c18..e1cc926ae0b 100644 --- a/doc/classes/OccluderShapeSphere.xml +++ b/doc/classes/OccluderShapeSphere.xml @@ -28,7 +28,7 @@ - + The sphere data can be accessed as an array of [Plane]s. The position of each sphere is stored in the [code]normal[/code], and the radius is stored in the [code]d[/code] value of the plane. diff --git a/scene/3d/occluder.cpp b/scene/3d/occluder.cpp index 148033ea4ec..fea2a4d3c4b 100644 --- a/scene/3d/occluder.cpp +++ b/scene/3d/occluder.cpp @@ -35,10 +35,6 @@ void Occluder::resource_changed(RES res) { update_gizmo(); - - if (_shape.is_valid()) { - _shape->update_shape_to_visual_server(); - } } void Occluder::set_shape(const Ref &p_shape) { @@ -52,16 +48,9 @@ void Occluder::set_shape(const Ref &p_shape) { if (_shape.is_valid()) { _shape->register_owner(this); - // Especially in the editor, the shape can be added AFTER the occluder - // is added to the world. This means the shape scenario will not be set. - // To remedy this we make sure the scenario etc is refreshed as soon as - // a shape is set on the occluder. if (is_inside_world() && get_world().is_valid()) { - _shape->notification_enter_world(get_world()->get_scenario()); - _shape->update_shape_to_visual_server(); - if (is_inside_tree()) { - _shape->update_active_to_visual_server(is_visible_in_tree()); - _shape->update_transform_to_visual_server(get_global_transform()); + if (_occluder_instance.is_valid()) { + VisualServer::get_singleton()->occluder_instance_link_resource(_occluder_instance, p_shape->get_rid()); } } } @@ -115,12 +104,16 @@ void Occluder::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_WORLD: { ERR_FAIL_COND(get_world().is_null()); - if (_shape.is_valid()) { - _shape->notification_enter_world(get_world()->get_scenario()); - _shape->update_active_to_visual_server(is_visible_in_tree()); - _shape->update_shape_to_visual_server(); - _shape->update_transform_to_visual_server(get_global_transform()); + + if (_occluder_instance.is_valid()) { + VisualServer::get_singleton()->occluder_instance_set_scenario(_occluder_instance, get_world()->get_scenario()); + if (get_shape().is_valid()) { + VisualServer::get_singleton()->occluder_instance_link_resource(_occluder_instance, get_shape()->get_rid()); + } + VisualServer::get_singleton()->occluder_instance_set_active(_occluder_instance, is_visible_in_tree()); + VisualServer::get_singleton()->occluder_instance_set_transform(_occluder_instance, get_global_transform()); } + #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { set_process_internal(true); @@ -128,8 +121,8 @@ void Occluder::_notification(int p_what) { #endif } break; case NOTIFICATION_EXIT_WORLD: { - if (_shape.is_valid()) { - _shape->notification_exit_world(); + if (_occluder_instance.is_valid()) { + VisualServer::get_singleton()->occluder_instance_set_scenario(_occluder_instance, RID()); } #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { @@ -138,14 +131,13 @@ void Occluder::_notification(int p_what) { #endif } break; case NOTIFICATION_VISIBILITY_CHANGED: { - if (_shape.is_valid() && is_inside_tree()) { - _shape->update_active_to_visual_server(is_visible_in_tree()); + if (_occluder_instance.is_valid() && is_inside_tree()) { + VisualServer::get_singleton()->occluder_instance_set_active(_occluder_instance, is_visible_in_tree()); } } break; case NOTIFICATION_TRANSFORM_CHANGED: { - if (_shape.is_valid()) { - _shape->update_transform_to_visual_server(get_global_transform()); - + if (_occluder_instance.is_valid()) { + VisualServer::get_singleton()->occluder_instance_set_transform(_occluder_instance, get_global_transform()); #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { update_configuration_warning(); @@ -171,10 +163,15 @@ void Occluder::_bind_methods() { } Occluder::Occluder() { + _occluder_instance = RID_PRIME(VisualServer::get_singleton()->occluder_instance_create()); set_notify_transform(true); } Occluder::~Occluder() { + if (_occluder_instance != RID()) { + VisualServer::get_singleton()->free(_occluder_instance); + } + if (!_shape.is_null()) { _shape->unregister_owner(this); } diff --git a/scene/3d/occluder.h b/scene/3d/occluder.h index 6b0d06522d7..22e5b8b6a7e 100644 --- a/scene/3d/occluder.h +++ b/scene/3d/occluder.h @@ -40,6 +40,7 @@ class Occluder : public Spatial { friend class OccluderSpatialGizmo; friend class OccluderEditorPlugin; + RID _occluder_instance; Ref _shape; void resource_changed(RES res); diff --git a/scene/resources/occluder_shape.cpp b/scene/resources/occluder_shape.cpp index 60f8474bd81..88723b75594 100644 --- a/scene/resources/occluder_shape.cpp +++ b/scene/resources/occluder_shape.cpp @@ -41,28 +41,16 @@ void OccluderShape::_bind_methods() { } -OccluderShape::OccluderShape(RID p_shape) { - _shape = p_shape; +OccluderShape::OccluderShape() { + _shape = RID_PRIME(VisualServer::get_singleton()->occluder_resource_create()); } OccluderShape::~OccluderShape() { - if (_shape != RID()) { + if (_shape.is_valid()) { VisualServer::get_singleton()->free(_shape); } } -void OccluderShape::update_transform_to_visual_server(const Transform &p_global_xform) { - VisualServer::get_singleton()->occluder_set_transform(get_shape(), p_global_xform); -} - -void OccluderShape::update_active_to_visual_server(bool p_active) { - VisualServer::get_singleton()->occluder_set_active(get_shape(), p_active); -} - -void OccluderShape::notification_exit_world() { - VisualServer::get_singleton()->occluder_set_scenario(_shape, RID(), VisualServer::OCCLUDER_TYPE_UNDEFINED); -} - #ifdef TOOLS_ENABLED AABB OccluderShape::get_fallback_gizmo_aabb() const { return AABB(Vector3(-0.5, -0.5, -0.5), Vector3(1, 1, 1)); @@ -105,7 +93,7 @@ AABB OccluderShapeSphere::get_fallback_gizmo_aabb() const { #endif void OccluderShapeSphere::update_shape_to_visual_server() { - VisualServer::get_singleton()->occluder_spheres_update(get_shape(), _spheres); + VisualServer::get_singleton()->occluder_resource_spheres_update(get_shape(), _spheres); } Transform OccluderShapeSphere::center_node(const Transform &p_global_xform, const Transform &p_parent_xform, real_t p_snap) { @@ -189,10 +177,6 @@ Transform OccluderShapeSphere::center_node(const Transform &p_global_xform, cons return new_local_xform; } -void OccluderShapeSphere::notification_enter_world(RID p_scenario) { - VisualServer::get_singleton()->occluder_set_scenario(get_shape(), p_scenario, VisualServer::OCCLUDER_TYPE_SPHERE); -} - void OccluderShapeSphere::set_spheres(const Vector &p_spheres) { #ifdef TOOLS_ENABLED // try and detect special circumstance of adding a new sphere in the editor @@ -221,6 +205,7 @@ void OccluderShapeSphere::set_spheres(const Vector &p_spheres) { _update_aabb(); #endif + update_shape_to_visual_server(); notify_change_to_owners(); } @@ -232,6 +217,7 @@ void OccluderShapeSphere::set_sphere_position(int p_idx, const Vector3 &p_positi #ifdef TOOLS_ENABLED _update_aabb(); #endif + update_shape_to_visual_server(); notify_change_to_owners(); } } @@ -244,10 +230,18 @@ void OccluderShapeSphere::set_sphere_radius(int p_idx, real_t p_radius) { #ifdef TOOLS_ENABLED _update_aabb(); #endif + update_shape_to_visual_server(); notify_change_to_owners(); } } -OccluderShapeSphere::OccluderShapeSphere() : - OccluderShape(RID_PRIME(VisualServer::get_singleton()->occluder_create())) { +OccluderShapeSphere::OccluderShapeSphere() { + if (get_shape().is_valid()) { + VisualServer::get_singleton()->occluder_resource_prepare(get_shape(), VisualServer::OCCLUDER_TYPE_SPHERE); + } + + // Create a default sphere + Vector planes; + planes.push_back(Plane(Vector3(0, 0, 0), 1)); + set_spheres(planes); } diff --git a/scene/resources/occluder_shape.h b/scene/resources/occluder_shape.h index e98fb1f3eff..17117b46d1c 100644 --- a/scene/resources/occluder_shape.h +++ b/scene/resources/occluder_shape.h @@ -45,17 +45,12 @@ protected: static void _bind_methods(); RID get_shape() const { return _shape; } - OccluderShape(RID p_shape); + OccluderShape(); public: virtual RID get_rid() const { return _shape; } ~OccluderShape(); - virtual void notification_enter_world(RID p_scenario) = 0; - virtual void update_shape_to_visual_server() = 0; - void update_transform_to_visual_server(const Transform &p_global_xform); - void update_active_to_visual_server(bool p_active); - void notification_exit_world(); virtual Transform center_node(const Transform &p_global_xform, const Transform &p_parent_xform, real_t p_snap) = 0; #ifdef TOOLS_ENABLED @@ -87,8 +82,7 @@ public: void set_sphere_position(int p_idx, const Vector3 &p_position); void set_sphere_radius(int p_idx, real_t p_radius); - virtual void notification_enter_world(RID p_scenario); - virtual void update_shape_to_visual_server(); + void update_shape_to_visual_server(); virtual Transform center_node(const Transform &p_global_xform, const Transform &p_parent_xform, real_t p_snap); #ifdef TOOLS_ENABLED diff --git a/scene/resources/occluder_shape_polygon.cpp b/scene/resources/occluder_shape_polygon.cpp index 4533c9d811d..05cdb2ae893 100644 --- a/scene/resources/occluder_shape_polygon.cpp +++ b/scene/resources/occluder_shape_polygon.cpp @@ -108,6 +108,7 @@ void OccluderShapePolygon::set_polygon_point(int p_idx, const Vector2 &p_point) _poly_pts_local_raw.set(p_idx, p_point); _sanitize_points(); + update_shape_to_visual_server(); notify_change_to_owners(); } @@ -118,18 +119,21 @@ void OccluderShapePolygon::set_hole_point(int p_idx, const Vector2 &p_point) { _hole_pts_local_raw.set(p_idx, p_point); _sanitize_points(); + update_shape_to_visual_server(); notify_change_to_owners(); } void OccluderShapePolygon::set_polygon_points(const PoolVector &p_points) { _poly_pts_local_raw = p_points; _sanitize_points(); + update_shape_to_visual_server(); notify_change_to_owners(); } void OccluderShapePolygon::set_hole_points(const PoolVector &p_points) { _hole_pts_local_raw = p_points; _sanitize_points(); + update_shape_to_visual_server(); notify_change_to_owners(); } @@ -141,10 +145,6 @@ PoolVector OccluderShapePolygon::get_hole_points() const { return _hole_pts_local_raw; } -void OccluderShapePolygon::notification_enter_world(RID p_scenario) { - VisualServer::get_singleton()->occluder_set_scenario(get_shape(), p_scenario, VisualServer::OCCLUDER_TYPE_MESH); -} - void OccluderShapePolygon::update_shape_to_visual_server() { if (_poly_pts_local.size() < 3) return; @@ -179,11 +179,12 @@ void OccluderShapePolygon::update_shape_to_visual_server() { face.plane = Plane(Vector3(0, 0, 0), Vector3(0, 0, -1)); - VisualServer::get_singleton()->occluder_mesh_update(get_shape(), md); + VisualServer::get_singleton()->occluder_resource_mesh_update(get_shape(), md); } void OccluderShapePolygon::set_two_way(bool p_two_way) { _settings_two_way = p_two_way; + update_shape_to_visual_server(); notify_change_to_owners(); } @@ -221,8 +222,11 @@ void OccluderShapePolygon::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "hole_points"), "set_hole_points", "get_hole_points"); } -OccluderShapePolygon::OccluderShapePolygon() : - OccluderShape(RID_PRIME(VisualServer::get_singleton()->occluder_create())) { +OccluderShapePolygon::OccluderShapePolygon() { + if (get_shape().is_valid()) { + VisualServer::get_singleton()->occluder_resource_prepare(get_shape(), VisualServer::OCCLUDER_TYPE_MESH); + } + clear(); PoolVector points; diff --git a/scene/resources/occluder_shape_polygon.h b/scene/resources/occluder_shape_polygon.h index 6d04b09c6f2..17d9d242530 100644 --- a/scene/resources/occluder_shape_polygon.h +++ b/scene/resources/occluder_shape_polygon.h @@ -81,8 +81,7 @@ public: void clear(); - virtual void notification_enter_world(RID p_scenario); - virtual void update_shape_to_visual_server(); + void update_shape_to_visual_server(); virtual Transform center_node(const Transform &p_global_xform, const Transform &p_parent_xform, real_t p_snap); #ifdef TOOLS_ENABLED diff --git a/servers/visual/portals/portal_occlusion_culler.cpp b/servers/visual/portals/portal_occlusion_culler.cpp index 16f61ffd822..d89ac379438 100644 --- a/servers/visual/portals/portal_occlusion_culler.cpp +++ b/servers/visual/portals/portal_occlusion_culler.cpp @@ -34,6 +34,8 @@ #include "core/math/aabb.h" #include "core/project_settings.h" #include "portal_renderer.h" +#include "servers/visual/visual_server_globals.h" +#include "servers/visual/visual_server_scene.h" #define _log(a, b) ; //#define _log_prepare(a) log(a, 0) @@ -275,10 +277,12 @@ void PortalOcclusionCuller::prepare_generic(PortalRenderer &p_portal_renderer, c uint32_t polycount = 0; #endif + const PortalResources &resources = VSG::scene->get_portal_resources(); + // find occluders for (unsigned int o = 0; o < p_occluder_pool_ids.size(); o++) { int id = p_occluder_pool_ids[o]; - VSOccluder &occ = p_portal_renderer.get_pool_occluder(id); + VSOccluder_Instance &occ = p_portal_renderer.get_pool_occluder_instance(id); // is it active? // in the case of rooms, they will always be active, as inactive @@ -290,9 +294,9 @@ void PortalOcclusionCuller::prepare_generic(PortalRenderer &p_portal_renderer, c // TODO : occlusion cull spheres AGAINST themselves. // i.e. a sphere that is occluded by another occluder is no // use as an occluder... - if (occ.type == VSOccluder::OT_SPHERE) { + if (occ.type == VSOccluder_Instance::OT_SPHERE) { // make sure world space spheres are up to date - p_portal_renderer.occluder_ensure_up_to_date_sphere(occ); + p_portal_renderer.occluder_ensure_up_to_date_sphere(resources, occ); // cull entire AABB if (is_aabb_culled(occ.aabb, p_planes)) { @@ -301,7 +305,7 @@ void PortalOcclusionCuller::prepare_generic(PortalRenderer &p_portal_renderer, c // multiple spheres for (int n = 0; n < occ.list_ids.size(); n++) { - const Occlusion::Sphere &occluder_sphere = p_portal_renderer.get_pool_occluder_sphere(occ.list_ids[n]).world; + const Occlusion::Sphere &occluder_sphere = p_portal_renderer.get_pool_occluder_world_sphere(occ.list_ids[n]); // is the occluder sphere culled? if (is_sphere_culled(occluder_sphere.pos, occluder_sphere.radius, p_planes)) { @@ -358,14 +362,14 @@ void PortalOcclusionCuller::prepare_generic(PortalRenderer &p_portal_renderer, c } } // sphere - if (occ.type == VSOccluder::OT_MESH) { + if (occ.type == VSOccluder_Instance::OT_MESH) { // make sure world space spheres are up to date - p_portal_renderer.occluder_ensure_up_to_date_polys(occ); + p_portal_renderer.occluder_ensure_up_to_date_polys(resources, occ); // multiple polys for (int n = 0; n < occ.list_ids.size(); n++) { - const VSOccluder_Mesh &opoly = p_portal_renderer.get_pool_occluder_mesh(occ.list_ids[n]); - const Occlusion::PolyPlane &poly = opoly.poly_world; + const VSOccluder_Poly &opoly = p_portal_renderer.get_pool_occluder_world_poly(occ.list_ids[n]); + const Occlusion::PolyPlane &poly = opoly.poly; // backface cull bool faces_camera = poly.plane.is_point_over(pt_camera); @@ -507,21 +511,21 @@ void PortalOcclusionCuller::precalc_poly_edge_planes(const Vector3 &p_pt_camera) // holes if (sortpoly.flags & SortPoly::SPF_HAS_HOLES) { // get the mesh poly and the holes - const VSOccluder_Mesh &mesh = _portal_renderer->get_pool_occluder_mesh(sortpoly.mesh_source_id); + const VSOccluder_Poly &mesh = _portal_renderer->get_pool_occluder_world_poly(sortpoly.mesh_source_id); dpoly.num_holes = mesh.num_holes; for (int h = 0; h < mesh.num_holes; h++) { uint32_t hid = mesh.hole_pool_ids[h]; - const VSOccluder_Hole &hole = _portal_renderer->get_pool_occluder_hole(hid); + const VSOccluder_Hole &hole = _portal_renderer->get_pool_occluder_world_hole(hid); // copy the verts to the precalced poly, // we will need these later for whittling polys. // We could alternatively link back to the original verts, but that gets messy. - dpoly.hole_polys[h] = hole.poly_world; + dpoly.hole_polys[h] = hole; - int hole_num_verts = hole.poly_world.num_verts; - const Vector3 *hverts = hole.poly_world.verts; + int hole_num_verts = hole.num_verts; + const Vector3 *hverts = hole.verts; // number of planes equals number of verts forming edges dpoly.hole_edge_planes[h].num_planes = hole_num_verts; @@ -671,7 +675,7 @@ void PortalOcclusionCuller::whittle_polys() { // order polys by distance to camera / area? NYI } -bool PortalOcclusionCuller::calculate_poly_goodness_of_fit(const VSOccluder_Mesh &p_opoly, real_t &r_fit) { +bool PortalOcclusionCuller::calculate_poly_goodness_of_fit(const VSOccluder_Poly &p_opoly, real_t &r_fit) { // transform each of the poly points, find the area in screen space // The points must be homogeneous coordinates, i.e. BEFORE @@ -679,11 +683,11 @@ bool PortalOcclusionCuller::calculate_poly_goodness_of_fit(const VSOccluder_Mesh // divide applied after clipping, to calculate the area. // We therefore store them as planes to store the w coordinate as d. Plane xpoints[Occlusion::PolyPlane::MAX_POLY_VERTS]; - int num_verts = p_opoly.poly_world.num_verts; + int num_verts = p_opoly.poly.num_verts; for (int n = 0; n < num_verts; n++) { // source and dest in homogeneous coords - Plane source(p_opoly.poly_world.verts[n], 1.0f); + Plane source(p_opoly.poly.verts[n], 1.0f); Plane &dest = xpoints[n]; dest = _matrix_camera.xform4(source); diff --git a/servers/visual/portals/portal_occlusion_culler.h b/servers/visual/portals/portal_occlusion_culler.h index 40e13f2342a..53459503e39 100644 --- a/servers/visual/portals/portal_occlusion_culler.h +++ b/servers/visual/portals/portal_occlusion_culler.h @@ -161,14 +161,10 @@ private: return false; } - bool calculate_poly_goodness_of_fit(const VSOccluder_Mesh &p_opoly, real_t &r_fit); + bool calculate_poly_goodness_of_fit(const VSOccluder_Poly &p_opoly, real_t &r_fit); void whittle_polys(); void precalc_poly_edge_planes(const Vector3 &p_pt_camera); - bool is_vso_poly_culled(const VSOccluder_Mesh &p_opoly, const LocalVector &p_planes) const { - return is_poly_culled(p_opoly.poly_world, p_planes); - } - // If all the points of the poly are beyond one of the planes (e.g. frustum), it is completely culled. bool is_poly_culled(const Occlusion::PolyPlane &p_opoly, const LocalVector &p_planes) const { for (unsigned int p = 0; p < p_planes.size(); p++) { diff --git a/servers/visual/portals/portal_renderer.cpp b/servers/visual/portals/portal_renderer.cpp index 031f3571951..289acae9425 100644 --- a/servers/visual/portals/portal_renderer.cpp +++ b/servers/visual/portals/portal_renderer.cpp @@ -117,10 +117,13 @@ void PortalRenderer::_rghost_remove_from_rooms(uint32_t p_pool_id) { } void PortalRenderer::_occluder_remove_from_rooms(uint32_t p_pool_id) { - VSOccluder &occ = _occluder_pool[p_pool_id]; + VSOccluder_Instance &occ = _occluder_instance_pool[p_pool_id]; if (_loaded && (occ.room_id != -1)) { VSRoom &room = get_room(occ.room_id); - room.remove_occluder(p_pool_id); + bool res = room.remove_occluder(p_pool_id); + if (!res) { + WARN_PRINT_ONCE("OccluderInstance was not present in Room"); + } } } @@ -467,22 +470,38 @@ void PortalRenderer::rghost_destroy(RGhostHandle p_handle) { _rghost_pool.free(p_handle); } -OccluderHandle PortalRenderer::occluder_create(VSOccluder::Type p_type) { +OccluderInstanceHandle PortalRenderer::occluder_instance_create() { uint32_t pool_id = 0; - VSOccluder *occ = _occluder_pool.request(pool_id); + VSOccluder_Instance *occ = _occluder_instance_pool.request(pool_id); occ->create(); - // specific type - occ->type = p_type; - CRASH_COND(p_type == VSOccluder::OT_UNDEFINED); - - OccluderHandle handle = pool_id + 1; + OccluderInstanceHandle handle = pool_id + 1; return handle; } -void PortalRenderer::occluder_set_active(OccluderHandle p_handle, bool p_active) { +void PortalRenderer::occluder_instance_link(OccluderInstanceHandle p_handle, OccluderResourceHandle p_resource_handle) { p_handle--; - VSOccluder &occ = _occluder_pool[p_handle]; + VSOccluder_Instance &occ = _occluder_instance_pool[p_handle]; + + // Unlink with any already linked, and destroy world resources + if (occ.resource_pool_id != UINT32_MAX) { + // Watch for bugs in future with the room within, this is not changed here, + // but could potentially be removed and re-added in future if we use sprawling. + occluder_instance_destroy(p_handle + 1, false); + occ.resource_pool_id = UINT32_MAX; + } + + p_resource_handle--; + VSOccluder_Resource &res = VSG::scene->get_portal_resources().get_pool_occluder_resource(p_resource_handle); + + occ.resource_pool_id = p_resource_handle; + occ.type = res.type; + occ.revision = 0; +} + +void PortalRenderer::occluder_instance_set_active(OccluderInstanceHandle p_handle, bool p_active) { + p_handle--; + VSOccluder_Instance &occ = _occluder_instance_pool[p_handle]; if (occ.active == p_active) { return; @@ -493,18 +512,23 @@ void PortalRenderer::occluder_set_active(OccluderHandle p_handle, bool p_active) occluder_refresh_room_within(p_handle); } -void PortalRenderer::occluder_set_transform(OccluderHandle p_handle, const Transform &p_xform) { +void PortalRenderer::occluder_instance_set_transform(OccluderInstanceHandle p_handle, const Transform &p_xform) { p_handle--; - VSOccluder &occ = _occluder_pool[p_handle]; + VSOccluder_Instance &occ = _occluder_instance_pool[p_handle]; occ.xform = p_xform; // mark as dirty as the world space spheres will be out of date - occ.dirty = true; + occ.revision = 0; + + // The room within is based on the xform, rather than the AABB so this + // should still work even though the world space transform is deferred. + // N.B. Occluders are a single room based on the center of the Occluder transform, + // this may need to be improved at a later date. occluder_refresh_room_within(p_handle); } void PortalRenderer::occluder_refresh_room_within(uint32_t p_occluder_pool_id) { - VSOccluder &occ = _occluder_pool[p_occluder_pool_id]; + VSOccluder_Instance &occ = _occluder_instance_pool[p_occluder_pool_id]; // if we aren't loaded, the room within can't be valid if (!_loaded) { @@ -547,156 +571,47 @@ void PortalRenderer::occluder_refresh_room_within(uint32_t p_occluder_pool_id) { } } -void PortalRenderer::occluder_update_mesh(OccluderHandle p_handle, const Geometry::OccluderMeshData &p_mesh_data) { +void PortalRenderer::occluder_instance_destroy(OccluderInstanceHandle p_handle, bool p_free) { p_handle--; - VSOccluder &occ = _occluder_pool[p_handle]; - ERR_FAIL_COND(occ.type != VSOccluder::OT_MESH); - // needs world points updating next time - occ.dirty = true; - - const LocalVectori &faces = p_mesh_data.faces; - const LocalVectori &vertices = p_mesh_data.vertices; - - // first deal with the situation where the number of polys has changed (rare) - if (occ.list_ids.size() != faces.size()) { - // not the most efficient, but works... - // remove existing - for (int n = 0; n < occ.list_ids.size(); n++) { - uint32_t id = occ.list_ids[n]; - _occluder_mesh_pool.free(id); - } - - occ.list_ids.clear(); - // create new - for (int n = 0; n < faces.size(); n++) { - uint32_t id; - VSOccluder_Mesh *poly = _occluder_mesh_pool.request(id); - poly->create(); - occ.list_ids.push_back(id); - } + if (p_free) { + _occluder_remove_from_rooms(p_handle); } - // new data - for (int n = 0; n < occ.list_ids.size(); n++) { - uint32_t id = occ.list_ids[n]; - - VSOccluder_Mesh &opoly = _occluder_mesh_pool[id]; - Occlusion::PolyPlane &poly = opoly.poly_local; - - // source face - const Geometry::OccluderMeshData::Face &face = faces[n]; - opoly.two_way = face.two_way; - - // make sure the number of holes is correct - if (face.holes.size() != opoly.num_holes) { - // slow but hey ho - // delete existing holes - for (int i = 0; i < opoly.num_holes; i++) { - _occluder_hole_pool.free(opoly.hole_pool_ids[i]); - opoly.hole_pool_ids[i] = UINT32_MAX; - } - // create any new holes - opoly.num_holes = face.holes.size(); - for (int i = 0; i < opoly.num_holes; i++) { - uint32_t hole_id; - VSOccluder_Hole *hole = _occluder_hole_pool.request(hole_id); - opoly.hole_pool_ids[i] = hole_id; - hole->create(); - } - } - - poly.plane = face.plane; - - poly.num_verts = MIN(face.indices.size(), Occlusion::PolyPlane::MAX_POLY_VERTS); - - // make sure the world poly also has the correct num verts - opoly.poly_world.num_verts = poly.num_verts; - - for (int c = 0; c < poly.num_verts; c++) { - int vert_index = face.indices[c]; - - if (vert_index < vertices.size()) { - poly.verts[c] = vertices[vert_index]; - } else { - WARN_PRINT_ONCE("occluder_update_mesh : poly index out of range"); - } - } - - // holes - for (int h = 0; h < opoly.num_holes; h++) { - VSOccluder_Hole &dhole = get_pool_occluder_hole(opoly.hole_pool_ids[h]); - const Geometry::OccluderMeshData::Hole &shole = face.holes[h]; - - dhole.poly_local.num_verts = shole.indices.size(); - dhole.poly_local.num_verts = MIN(dhole.poly_local.num_verts, Occlusion::Poly::MAX_POLY_VERTS); - dhole.poly_world.num_verts = dhole.poly_local.num_verts; - - for (int c = 0; c < dhole.poly_local.num_verts; c++) { - int vert_index = shole.indices[c]; - if (vert_index < vertices.size()) { - dhole.poly_local.verts[c] = vertices[vert_index]; - } else { - WARN_PRINT_ONCE("occluder_update_mesh : hole index out of range"); - } - } - } - } -} - -void PortalRenderer::occluder_update_spheres(OccluderHandle p_handle, const Vector &p_spheres) { - p_handle--; - VSOccluder &occ = _occluder_pool[p_handle]; - ERR_FAIL_COND(occ.type != VSOccluder::OT_SPHERE); - - // first deal with the situation where the number of spheres has changed (rare) - if (occ.list_ids.size() != p_spheres.size()) { - // not the most efficient, but works... - // remove existing - for (int n = 0; n < occ.list_ids.size(); n++) { - uint32_t id = occ.list_ids[n]; - _occluder_sphere_pool.free(id); - } - - occ.list_ids.clear(); - // create new - for (int n = 0; n < p_spheres.size(); n++) { - uint32_t id; - VSOccluder_Sphere *sphere = _occluder_sphere_pool.request(id); - sphere->create(); - occ.list_ids.push_back(id); - } - } - - // new positions - for (int n = 0; n < occ.list_ids.size(); n++) { - uint32_t id = occ.list_ids[n]; - VSOccluder_Sphere &sphere = _occluder_sphere_pool[id]; - sphere.local.from_plane(p_spheres[n]); - } - - // mark as dirty as the world space spheres will be out of date - occ.dirty = true; -} - -void PortalRenderer::occluder_destroy(OccluderHandle p_handle) { - p_handle--; - // depending on the occluder type, remove the spheres etc - VSOccluder &occ = _occluder_pool[p_handle]; + VSOccluder_Instance &occ = _occluder_instance_pool[p_handle]; switch (occ.type) { - case VSOccluder::OT_SPHERE: { - occluder_update_spheres(p_handle + 1, Vector()); + case VSOccluder_Instance::OT_SPHERE: { + // free any spheres owned by the occluder + for (int n = 0; n < occ.list_ids.size(); n++) { + uint32_t id = occ.list_ids[n]; + _occluder_world_sphere_pool.free(id); + } + occ.list_ids.clear(); } break; - case VSOccluder::OT_MESH: { - occluder_update_mesh(p_handle + 1, Geometry::OccluderMeshData()); + case VSOccluder_Instance::OT_MESH: { + // free any polys owned by the occluder + for (int n = 0; n < occ.list_ids.size(); n++) { + uint32_t id = occ.list_ids[n]; + VSOccluder_Poly &poly = _occluder_world_poly_pool[id]; + + // free any holes owned by the poly + for (int h = 0; h < poly.num_holes; h++) { + _occluder_world_hole_pool.free(poly.hole_pool_ids[h]); + } + // blanks + poly.create(); + _occluder_world_poly_pool.free(id); + } + occ.list_ids.clear(); } break; default: { } break; } - _occluder_remove_from_rooms(p_handle); - _occluder_pool.free(p_handle); + if (p_free) { + _occluder_instance_pool.free(p_handle); + } } // Rooms @@ -1047,9 +962,9 @@ void PortalRenderer::_load_finalize_roaming() { rghost_update(_rghost_pool.get_active_id(n) + 1, aabb, true); } - for (unsigned int n = 0; n < _occluder_pool.active_size(); n++) { - VSOccluder &occ = _occluder_pool.get_active(n); - int occluder_id = _occluder_pool.get_active_id(n); + for (unsigned int n = 0; n < _occluder_instance_pool.active_size(); n++) { + VSOccluder_Instance &occ = _occluder_instance_pool.get_active(n); + int occluder_id = _occluder_instance_pool.get_active_id(n); // make sure occluder is in the correct room occ.room_id = find_room_within(occ.pt_center, -1); diff --git a/servers/visual/portals/portal_renderer.h b/servers/visual/portals/portal_renderer.h index 449227a286d..584b64560df 100644 --- a/servers/visual/portals/portal_renderer.h +++ b/servers/visual/portals/portal_renderer.h @@ -38,6 +38,7 @@ #include "core/vector.h" #include "portal_gameplay_monitor.h" #include "portal_pvs.h" +#include "portal_resources.h" #include "portal_rooms_bsp.h" #include "portal_tracer.h" #include "portal_types.h" @@ -187,12 +188,11 @@ public: void rghost_destroy(RGhostHandle p_handle); // occluders - OccluderHandle occluder_create(VSOccluder::Type p_type); - void occluder_update_spheres(OccluderHandle p_handle, const Vector &p_spheres); - void occluder_update_mesh(OccluderHandle p_handle, const Geometry::OccluderMeshData &p_mesh_data); - void occluder_set_transform(OccluderHandle p_handle, const Transform &p_xform); - void occluder_set_active(OccluderHandle p_handle, bool p_active); - void occluder_destroy(OccluderHandle p_handle); + OccluderInstanceHandle occluder_instance_create(); + void occluder_instance_link(OccluderInstanceHandle p_handle, OccluderResourceHandle p_resource_handle); + void occluder_instance_set_transform(OccluderInstanceHandle p_handle, const Transform &p_xform); + void occluder_instance_set_active(OccluderInstanceHandle p_handle, bool p_active); + void occluder_instance_destroy(OccluderInstanceHandle p_handle, bool p_free = true); // editor only .. slow Geometry::MeshData occlusion_debug_get_current_polys() const { return _tracer.get_occlusion_culler().debug_get_current_polys(); } @@ -216,13 +216,13 @@ public: int cull_convex_implementation(const Vector3 &p_point, const Vector3 &p_cam_dir, const CameraMatrix &p_cam_matrix, const Vector &p_convex, VSInstance **p_result_array, int p_result_max, uint32_t p_mask, int32_t &r_previous_room_id_hint); - bool occlusion_is_active() const { return _occluder_pool.active_size() && use_occlusion_culling; } + bool occlusion_is_active() const { return _occluder_instance_pool.active_size() && use_occlusion_culling; } // special function for occlusion culling only that does not use portals / rooms, // but allows using occluders with the main scene int occlusion_cull(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, const Vector &p_convex, VSInstance **p_result_array, int p_num_results) { // inactive? - if (!_occluder_pool.active_size() || !use_occlusion_culling) { + if (!_occluder_instance_pool.active_size() || !use_occlusion_culling) { return p_num_results; } @@ -257,14 +257,6 @@ public: RGhost &get_pool_rghost(uint32_t p_pool_id) { return _rghost_pool[p_pool_id]; } const RGhost &get_pool_rghost(uint32_t p_pool_id) const { return _rghost_pool[p_pool_id]; } - const LocalVector &get_occluders_active_list() const { return _occluder_pool.get_active_list(); } - const VSOccluder &get_pool_occluder(uint32_t p_pool_id) const { return _occluder_pool[p_pool_id]; } - VSOccluder &get_pool_occluder(uint32_t p_pool_id) { return _occluder_pool[p_pool_id]; } - const VSOccluder_Sphere &get_pool_occluder_sphere(uint32_t p_pool_id) const { return _occluder_sphere_pool[p_pool_id]; } - const VSOccluder_Mesh &get_pool_occluder_mesh(uint32_t p_pool_id) const { return _occluder_mesh_pool[p_pool_id]; } - const VSOccluder_Hole &get_pool_occluder_hole(uint32_t p_pool_id) const { return _occluder_hole_pool[p_pool_id]; } - VSOccluder_Hole &get_pool_occluder_hole(uint32_t p_pool_id) { return _occluder_hole_pool[p_pool_id]; } - VSStaticGhost &get_static_ghost(uint32_t p_id) { return _static_ghosts[p_id]; } VSRoomGroup &get_roomgroup(uint32_t p_pool_id) { return _roomgroup_pool[p_pool_id]; } @@ -274,6 +266,15 @@ public: bool get_cull_using_pvs() const { return _cull_using_pvs; } + // occluders + const LocalVector &get_occluders_active_list() const { return _occluder_instance_pool.get_active_list(); } + const VSOccluder_Instance &get_pool_occluder_instance(uint32_t p_pool_id) const { return _occluder_instance_pool[p_pool_id]; } + VSOccluder_Instance &get_pool_occluder_instance(uint32_t p_pool_id) { return _occluder_instance_pool[p_pool_id]; } + const VSOccluder_Sphere &get_pool_occluder_world_sphere(uint32_t p_pool_id) const { return _occluder_world_sphere_pool[p_pool_id]; } + const VSOccluder_Poly &get_pool_occluder_world_poly(uint32_t p_pool_id) const { return _occluder_world_poly_pool[p_pool_id]; } + const VSOccluder_Hole &get_pool_occluder_world_hole(uint32_t p_pool_id) const { return _occluder_world_hole_pool[p_pool_id]; } + VSOccluder_Hole &get_pool_occluder_world_hole(uint32_t p_pool_id) { return _occluder_world_hole_pool[p_pool_id]; } + private: int find_room_within(const Vector3 &p_pos, int p_previous_room_id = -1) { return _rooms_lookup_bsp.find_room_within(*this, p_pos, p_previous_room_id); @@ -318,10 +319,10 @@ private: LocalVector _moving_list_roaming; // occluders - TrackedPooledList _occluder_pool; - TrackedPooledList _occluder_sphere_pool; - TrackedPooledList _occluder_mesh_pool; - TrackedPooledList _occluder_hole_pool; + TrackedPooledList _occluder_instance_pool; + TrackedPooledList _occluder_world_sphere_pool; + TrackedPooledList _occluder_world_poly_pool; + TrackedPooledList _occluder_world_hole_pool; PVS _pvs; @@ -353,16 +354,47 @@ public: static String _rid_to_string(RID p_rid); static String _addr_to_string(const void *p_addr); - void occluder_ensure_up_to_date_sphere(VSOccluder &r_occluder); - void occluder_ensure_up_to_date_polys(VSOccluder &r_occluder); + void occluder_ensure_up_to_date_sphere(const PortalResources &p_resources, VSOccluder_Instance &r_occluder); + void occluder_ensure_up_to_date_polys(const PortalResources &p_resources, VSOccluder_Instance &r_occluder); void occluder_refresh_room_within(uint32_t p_occluder_pool_id); }; -inline void PortalRenderer::occluder_ensure_up_to_date_sphere(VSOccluder &r_occluder) { - if (!r_occluder.dirty) { +inline void PortalRenderer::occluder_ensure_up_to_date_sphere(const PortalResources &p_resources, VSOccluder_Instance &r_occluder) { + // occluder is not bound to a resource, cannot be used + if (r_occluder.resource_pool_id == UINT32_MAX) { return; } - r_occluder.dirty = false; + + // get the resource + const VSOccluder_Resource &res = p_resources.get_pool_occluder_resource(r_occluder.resource_pool_id); + + // dirty? + if (r_occluder.revision == res.revision) { + return; + } + r_occluder.revision = res.revision; + + // must be same type, if not an error has occurred + ERR_FAIL_COND(res.type != r_occluder.type); + + // first make sure the instance has the correct number of world space spheres + if (r_occluder.list_ids.size() != res.list_ids.size()) { + // not the most efficient, but works... + // remove existing + for (int n = 0; n < r_occluder.list_ids.size(); n++) { + uint32_t id = r_occluder.list_ids[n]; + _occluder_world_sphere_pool.free(id); + } + + r_occluder.list_ids.clear(); + // create new + for (int n = 0; n < res.list_ids.size(); n++) { + uint32_t id; + VSOccluder_Sphere *sphere = _occluder_world_sphere_pool.request(id); + sphere->create(); + r_occluder.list_ids.push_back(id); + } + } const Transform &tr = r_occluder.xform; @@ -375,15 +407,16 @@ inline void PortalRenderer::occluder_ensure_up_to_date_sphere(VSOccluder &r_occl // transform spheres for (int n = 0; n < r_occluder.list_ids.size(); n++) { - uint32_t pool_id = r_occluder.list_ids[n]; - VSOccluder_Sphere &osphere = _occluder_sphere_pool[pool_id]; + uint32_t world_pool_id = r_occluder.list_ids[n]; + VSOccluder_Sphere &world_osphere = _occluder_world_sphere_pool[world_pool_id]; + const VSOccluder_Sphere &local_osphere = p_resources.get_pool_occluder_local_sphere(res.list_ids[n]); - osphere.world.pos = tr.xform(osphere.local.pos); - osphere.world.radius = osphere.local.radius * scale; + world_osphere.pos = tr.xform(local_osphere.pos); + world_osphere.radius = local_osphere.radius * scale; - Vector3 bradius = Vector3(osphere.world.radius, osphere.world.radius, osphere.world.radius); - Vector3 bmin = osphere.world.pos - bradius; - Vector3 bmax = osphere.world.pos + bradius; + Vector3 bradius = Vector3(world_osphere.radius, world_osphere.radius, world_osphere.radius); + Vector3 bmin = world_osphere.pos - bradius; + Vector3 bmax = world_osphere.pos + bradius; bb_min.x = MIN(bb_min.x, bmin.x); bb_min.y = MIN(bb_min.y, bmin.y); @@ -397,33 +430,91 @@ inline void PortalRenderer::occluder_ensure_up_to_date_sphere(VSOccluder &r_occl r_occluder.aabb.size = bb_max - bb_min; } -inline void PortalRenderer::occluder_ensure_up_to_date_polys(VSOccluder &r_occluder) { - if (!r_occluder.dirty) { +inline void PortalRenderer::occluder_ensure_up_to_date_polys(const PortalResources &p_resources, VSOccluder_Instance &r_occluder) { + // occluder is not bound to a resource, cannot be used + if (r_occluder.resource_pool_id == UINT32_MAX) { return; } - r_occluder.dirty = false; + + // get the resource + const VSOccluder_Resource &res = p_resources.get_pool_occluder_resource(r_occluder.resource_pool_id); + + // dirty? + if (r_occluder.revision == res.revision) { + return; + } + r_occluder.revision = res.revision; + + // must be same type, if not an error has occurred + ERR_FAIL_COND(res.type != r_occluder.type); + + // first make sure the instance has the correct number of world space spheres + if (r_occluder.list_ids.size() != res.list_ids.size()) { + // not the most efficient, but works... + // remove existing + for (int n = 0; n < r_occluder.list_ids.size(); n++) { + uint32_t id = r_occluder.list_ids[n]; + _occluder_world_poly_pool.free(id); + } + + r_occluder.list_ids.clear(); + // create new + for (int n = 0; n < res.list_ids.size(); n++) { + uint32_t id; + VSOccluder_Poly *poly = _occluder_world_poly_pool.request(id); + poly->create(); + r_occluder.list_ids.push_back(id); + } + } const Transform &tr = r_occluder.xform; for (int n = 0; n < r_occluder.list_ids.size(); n++) { - uint32_t pool_id = r_occluder.list_ids[n]; + uint32_t world_pool_id = r_occluder.list_ids[n]; + uint32_t local_pool_id = res.list_ids[n]; - VSOccluder_Mesh &opoly = _occluder_mesh_pool[pool_id]; + VSOccluder_Poly &world_opoly = _occluder_world_poly_pool[world_pool_id]; + const VSOccluder_Poly &local_opoly = p_resources._occluder_local_poly_pool[local_pool_id]; - for (int i = 0; i < opoly.poly_local.num_verts; i++) { - opoly.poly_world.verts[i] = tr.xform(opoly.poly_local.verts[i]); + world_opoly.poly.num_verts = local_opoly.poly.num_verts; + world_opoly.two_way = local_opoly.two_way; + + for (int i = 0; i < local_opoly.poly.num_verts; i++) { + world_opoly.poly.verts[i] = tr.xform(local_opoly.poly.verts[i]); } - opoly.poly_world.plane = tr.xform(opoly.poly_local.plane); + world_opoly.poly.plane = tr.xform(local_opoly.poly.plane); + + // number of holes must be correct for each poly + if (world_opoly.num_holes != local_opoly.num_holes) { + // remove existing + for (int h = 0; h < world_opoly.num_holes; h++) { + uint32_t id = world_opoly.hole_pool_ids[h]; + _occluder_world_hole_pool.free(id); + // not strictly necessary + world_opoly.hole_pool_ids[h] = UINT32_MAX; + } + + world_opoly.num_holes = local_opoly.num_holes; + for (int h = 0; h < world_opoly.num_holes; h++) { + uint32_t id; + VSOccluder_Hole *hole = _occluder_world_hole_pool.request(id); + hole->create(); + world_opoly.hole_pool_ids[h] = id; + } + } // holes - for (int h = 0; h < opoly.num_holes; h++) { - uint32_t hid = opoly.hole_pool_ids[h]; + for (int h = 0; h < world_opoly.num_holes; h++) { + uint32_t world_hid = world_opoly.hole_pool_ids[h]; + uint32_t local_hid = local_opoly.hole_pool_ids[h]; + VSOccluder_Hole &world_hole = _occluder_world_hole_pool[world_hid]; + const VSOccluder_Hole &local_hole = p_resources._occluder_local_hole_pool[local_hid]; - VSOccluder_Hole &hole = _occluder_hole_pool[hid]; + world_hole.num_verts = local_hole.num_verts; - for (int i = 0; i < hole.poly_local.num_verts; i++) { - hole.poly_world.verts[i] = tr.xform(hole.poly_local.verts[i]); + for (int i = 0; i < world_hole.num_verts; i++) { + world_hole.verts[i] = tr.xform(local_hole.verts[i]); } } } diff --git a/servers/visual/portals/portal_resources.cpp b/servers/visual/portals/portal_resources.cpp new file mode 100644 index 00000000000..d9d9b93374f --- /dev/null +++ b/servers/visual/portals/portal_resources.cpp @@ -0,0 +1,216 @@ +/*************************************************************************/ +/* portal_resources.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "portal_resources.h" + +OccluderResourceHandle PortalResources::occluder_resource_create() { + uint32_t pool_id = 0; + VSOccluder_Resource *occ = _occluder_resource_pool.request(pool_id); + occ->create(); + + OccluderResourceHandle handle = pool_id + 1; + return handle; +} + +void PortalResources::occluder_resource_destroy(OccluderResourceHandle p_handle) { + p_handle--; + + // Depending on the occluder resource type, remove the spheres, polys, holes etc + // We can reuse the update methods for this. + VSOccluder_Resource &occ = _occluder_resource_pool[p_handle]; + switch (occ.type) { + case VSOccluder_Instance::OT_SPHERE: { + occluder_resource_update_spheres(p_handle + 1, Vector()); + } break; + case VSOccluder_Instance::OT_MESH: { + occluder_resource_update_mesh(p_handle + 1, Geometry::OccluderMeshData()); + } break; + default: { + } break; + } + + // This also clears the occluder + occ.create(); + + _occluder_resource_pool.free(p_handle); +} + +void PortalResources::occluder_resource_prepare(OccluderResourceHandle p_handle, VSOccluder_Instance::Type p_type) { + p_handle--; + + // depending on the occluder type, remove the spheres etc + VSOccluder_Resource &occ = _occluder_resource_pool[p_handle]; + + if (occ.type != VSOccluder_Instance::OT_UNDEFINED) { + ERR_PRINT_ONCE("occluder_resource_prepare should be called only once."); + } + + occ.type = p_type; + ERR_FAIL_COND(p_type == VSOccluder_Instance::OT_UNDEFINED); +} + +void PortalResources::occluder_resource_update_spheres(OccluderResourceHandle p_handle, const Vector &p_spheres) { + p_handle--; + VSOccluder_Resource &occ = _occluder_resource_pool[p_handle]; + ERR_FAIL_COND(occ.type != VSOccluder_Resource::OT_SPHERE); + + // first deal with the situation where the number of spheres has changed (rare) + if (occ.list_ids.size() != p_spheres.size()) { + // not the most efficient, but works... + // remove existing + for (int n = 0; n < occ.list_ids.size(); n++) { + uint32_t id = occ.list_ids[n]; + _occluder_local_sphere_pool.free(id); + } + + occ.list_ids.clear(); + // create new + for (int n = 0; n < p_spheres.size(); n++) { + uint32_t id; + VSOccluder_Sphere *sphere = _occluder_local_sphere_pool.request(id); + sphere->create(); + occ.list_ids.push_back(id); + } + } + + // new positions + for (int n = 0; n < occ.list_ids.size(); n++) { + uint32_t id = occ.list_ids[n]; + VSOccluder_Sphere &sphere = _occluder_local_sphere_pool[id]; + sphere.from_plane(p_spheres[n]); + } + + // mark as dirty as the world space spheres will be out of date next time this resource is used + occ.revision += 1; +} + +void PortalResources::occluder_resource_update_mesh(OccluderResourceHandle p_handle, const Geometry::OccluderMeshData &p_mesh_data) { + p_handle--; + VSOccluder_Resource &occ = _occluder_resource_pool[p_handle]; + ERR_FAIL_COND(occ.type != VSOccluder_Resource::OT_MESH); + + // mark as dirty, needs world points updating next time this resource is used + occ.revision += 1; + + const LocalVectori &faces = p_mesh_data.faces; + const LocalVectori &vertices = p_mesh_data.vertices; + + // first deal with the situation where the number of polys has changed (rare) + if (occ.list_ids.size() != faces.size()) { + // not the most efficient, but works... + // remove existing + for (int n = 0; n < occ.list_ids.size(); n++) { + uint32_t id = occ.list_ids[n]; + + // must also free the holes + VSOccluder_Poly &opoly = _occluder_local_poly_pool[id]; + for (int h = 0; h < opoly.num_holes; h++) { + _occluder_local_hole_pool.free(opoly.hole_pool_ids[h]); + + // perhaps debug only + opoly.hole_pool_ids[h] = UINT32_MAX; + } + + _occluder_local_poly_pool.free(id); + } + + occ.list_ids.clear(); + // create new + for (int n = 0; n < faces.size(); n++) { + uint32_t id; + VSOccluder_Poly *poly = _occluder_local_poly_pool.request(id); + poly->create(); + occ.list_ids.push_back(id); + } + } + + // new data + for (int n = 0; n < occ.list_ids.size(); n++) { + uint32_t id = occ.list_ids[n]; + + VSOccluder_Poly &opoly = _occluder_local_poly_pool[id]; + Occlusion::PolyPlane &poly = opoly.poly; + + // source face + const Geometry::OccluderMeshData::Face &face = faces[n]; + opoly.two_way = face.two_way; + + // make sure the number of holes is correct + if (face.holes.size() != opoly.num_holes) { + // slow but hey ho + // delete existing holes + for (int i = 0; i < opoly.num_holes; i++) { + _occluder_local_hole_pool.free(opoly.hole_pool_ids[i]); + opoly.hole_pool_ids[i] = UINT32_MAX; + } + // create any new holes + opoly.num_holes = face.holes.size(); + for (int i = 0; i < opoly.num_holes; i++) { + uint32_t hole_id; + VSOccluder_Hole *hole = _occluder_local_hole_pool.request(hole_id); + opoly.hole_pool_ids[i] = hole_id; + hole->create(); + } + } + + // set up the poly basics, plane and verts + poly.plane = face.plane; + poly.num_verts = MIN(face.indices.size(), Occlusion::PolyPlane::MAX_POLY_VERTS); + + for (int c = 0; c < poly.num_verts; c++) { + int vert_index = face.indices[c]; + + if (vert_index < vertices.size()) { + poly.verts[c] = vertices[vert_index]; + } else { + WARN_PRINT_ONCE("occluder_update_mesh : poly index out of range"); + } + } + + // set up any holes that are present + for (int h = 0; h < opoly.num_holes; h++) { + VSOccluder_Hole &dhole = get_pool_occluder_local_hole(opoly.hole_pool_ids[h]); + const Geometry::OccluderMeshData::Hole &shole = face.holes[h]; + + dhole.num_verts = shole.indices.size(); + dhole.num_verts = MIN(dhole.num_verts, Occlusion::Poly::MAX_POLY_VERTS); + + for (int c = 0; c < dhole.num_verts; c++) { + int vert_index = shole.indices[c]; + if (vert_index < vertices.size()) { + dhole.verts[c] = vertices[vert_index]; + } else { + WARN_PRINT_ONCE("occluder_update_mesh : hole index out of range"); + } + } // for c through hole verts + } // for h through holes + + } // for n through occluders +} diff --git a/servers/visual/portals/portal_resources.h b/servers/visual/portals/portal_resources.h new file mode 100644 index 00000000000..02a9dcd124a --- /dev/null +++ b/servers/visual/portals/portal_resources.h @@ -0,0 +1,68 @@ +/*************************************************************************/ +/* portal_resources.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef PORTAL_RESOURCES_H +#define PORTAL_RESOURCES_H + +#include "core/math/geometry.h" +#include "portal_types.h" + +// Although the portal renderer is owned by a scenario, +// resources are not associated with a scenario and can be shared +// potentially across multiple scenarios. They must therefore be held in +// some form of global. + +class PortalResources { + friend class PortalRenderer; + +public: + OccluderResourceHandle occluder_resource_create(); + void occluder_resource_prepare(OccluderResourceHandle p_handle, VSOccluder_Instance::Type p_type); + void occluder_resource_update_spheres(OccluderResourceHandle p_handle, const Vector &p_spheres); + void occluder_resource_update_mesh(OccluderResourceHandle p_handle, const Geometry::OccluderMeshData &p_mesh_data); + void occluder_resource_destroy(OccluderResourceHandle p_handle); + + const VSOccluder_Resource &get_pool_occluder_resource(uint32_t p_pool_id) const { return _occluder_resource_pool[p_pool_id]; } + VSOccluder_Resource &get_pool_occluder_resource(uint32_t p_pool_id) { return _occluder_resource_pool[p_pool_id]; } + + // Local space is shared resources + const VSOccluder_Sphere &get_pool_occluder_local_sphere(uint32_t p_pool_id) const { return _occluder_local_sphere_pool[p_pool_id]; } + const VSOccluder_Poly &get_pool_occluder_local_poly(uint32_t p_pool_id) const { return _occluder_local_poly_pool[p_pool_id]; } + const VSOccluder_Hole &get_pool_occluder_local_hole(uint32_t p_pool_id) const { return _occluder_local_hole_pool[p_pool_id]; } + VSOccluder_Hole &get_pool_occluder_local_hole(uint32_t p_pool_id) { return _occluder_local_hole_pool[p_pool_id]; } + +private: + TrackedPooledList _occluder_resource_pool; + TrackedPooledList _occluder_local_sphere_pool; + TrackedPooledList _occluder_local_poly_pool; + TrackedPooledList _occluder_local_hole_pool; +}; + +#endif // PORTAL_RESOURCES_H diff --git a/servers/visual/portals/portal_types.h b/servers/visual/portals/portal_types.h index 0907988d654..6568316bac1 100644 --- a/servers/visual/portals/portal_types.h +++ b/servers/visual/portals/portal_types.h @@ -55,7 +55,8 @@ typedef uint32_t RoomHandle; typedef uint32_t RoomGroupHandle; typedef uint32_t OcclusionHandle; typedef uint32_t RGhostHandle; -typedef uint32_t OccluderHandle; +typedef uint32_t OccluderInstanceHandle; +typedef uint32_t OccluderResourceHandle; struct VSPortal { enum ClipResult { @@ -380,12 +381,12 @@ struct VSRoom { LocalVector _roomgroup_ids; }; -struct VSOccluder { +// Possibly shared data, in local space +struct VSOccluder_Resource { void create() { type = OT_UNDEFINED; - room_id = -1; - dirty = false; - active = true; + revision = 0; + list_ids.clear(); } // these should match the values in VisualServer::OccluderType @@ -396,6 +397,28 @@ struct VSOccluder { OT_NUM_TYPES, } type; + // If the revision of the instance and the resource don't match, + // then the local versions have been updated and need transforming + // to world space in the instance (i.e. it is dirty) + uint32_t revision; + + // ids of multiple objects in the appropriate occluder pool: + // local space for resources, and world space for occluder instances + LocalVector list_ids; +}; + +struct VSOccluder_Instance : public VSOccluder_Resource { + void create() { + VSOccluder_Resource::create(); + room_id = -1; + active = true; + resource_pool_id = UINT32_MAX; + } + + // Occluder instance can be bound to one resource (which will include data in local space) + // This should be set back to NULL if the resource is deleted + uint32_t resource_pool_id; + // which is the primary room this group of occluders is in // (it may sprawl into multiple rooms) int32_t room_id; @@ -409,14 +432,8 @@ struct VSOccluder { // global xform Transform xform; - // whether world space need calculating - bool dirty; - // controlled by the visible flag on the occluder bool active; - - // ids of multiple objects in the appropriate occluder pool - LocalVector list_ids; }; namespace Occlusion { @@ -472,42 +489,27 @@ struct PolyPlane : public Poly { } // namespace Occlusion -struct VSOccluder_Sphere { - void create() { - local.create(); - world.create(); - } - - Occlusion::Sphere local; - Occlusion::Sphere world; +struct VSOccluder_Sphere : public Occlusion::Sphere { }; -struct VSOccluder_Mesh { +struct VSOccluder_Poly { static const int MAX_POLY_HOLES = PortalDefines::OCCLUSION_POLY_MAX_HOLES; void create() { - poly_local.create(); - poly_world.create(); + poly.create(); num_holes = 0; two_way = false; for (int n = 0; n < MAX_POLY_HOLES; n++) { hole_pool_ids[n] = UINT32_MAX; } } - Occlusion::PolyPlane poly_local; - Occlusion::PolyPlane poly_world; + Occlusion::PolyPlane poly; bool two_way; int num_holes; uint32_t hole_pool_ids[MAX_POLY_HOLES]; }; -struct VSOccluder_Hole { - void create() { - poly_local.create(); - poly_world.create(); - } - Occlusion::Poly poly_local; - Occlusion::Poly poly_world; +struct VSOccluder_Hole : public Occlusion::Poly { }; #endif diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h index 63d92ce1811..4eb3b1b7e97 100644 --- a/servers/visual/visual_server_raster.h +++ b/servers/visual/visual_server_raster.h @@ -592,12 +592,16 @@ public: BIND2(roomgroup_add_room, RID, RID) // Occluders - BIND0R(RID, occluder_create) - BIND3(occluder_set_scenario, RID, RID, OccluderType) - BIND2(occluder_spheres_update, RID, const Vector &) - BIND2(occluder_mesh_update, RID, const Geometry::OccluderMeshData &) - BIND2(occluder_set_transform, RID, const Transform &) - BIND2(occluder_set_active, RID, bool) + BIND0R(RID, occluder_instance_create) + BIND2(occluder_instance_set_scenario, RID, RID) + BIND2(occluder_instance_link_resource, RID, RID) + BIND2(occluder_instance_set_transform, RID, const Transform &) + BIND2(occluder_instance_set_active, RID, bool) + + BIND0R(RID, occluder_resource_create) + BIND2(occluder_resource_prepare, RID, OccluderType) + BIND2(occluder_resource_spheres_update, RID, const Vector &) + BIND2(occluder_resource_mesh_update, RID, const Geometry::OccluderMeshData &) BIND1(set_use_occlusion_culling, bool) BIND1RC(Geometry::MeshData, occlusion_debug_get_current_polys, RID) diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp index d2b5b078fee..c3acd9d8538 100644 --- a/servers/visual/visual_server_scene.cpp +++ b/servers/visual/visual_server_scene.cpp @@ -1192,65 +1192,88 @@ void VisualServerScene::roomgroup_add_room(RID p_roomgroup, RID p_room) { } // Occluders -RID VisualServerScene::occluder_create() { - Occluder *ro = memnew(Occluder); +RID VisualServerScene::occluder_instance_create() { + OccluderInstance *ro = memnew(OccluderInstance); ERR_FAIL_COND_V(!ro, RID()); - RID occluder_rid = occluder_owner.make_rid(ro); + RID occluder_rid = occluder_instance_owner.make_rid(ro); return occluder_rid; } -void VisualServerScene::occluder_set_scenario(RID p_occluder, RID p_scenario, VisualServer::OccluderType p_type) { - Occluder *ro = occluder_owner.getornull(p_occluder); - ERR_FAIL_COND(!ro); +void VisualServerScene::occluder_instance_link_resource(RID p_occluder_instance, RID p_occluder_resource) { + OccluderInstance *oi = occluder_instance_owner.getornull(p_occluder_instance); + ERR_FAIL_COND(!oi); + ERR_FAIL_COND(!oi->scenario); + + OccluderResource *res = occluder_resource_owner.getornull(p_occluder_resource); + ERR_FAIL_COND(!res); + + oi->scenario->_portal_renderer.occluder_instance_link(oi->scenario_occluder_id, res->occluder_resource_id); +} + +void VisualServerScene::occluder_instance_set_scenario(RID p_occluder_instance, RID p_scenario) { + OccluderInstance *oi = occluder_instance_owner.getornull(p_occluder_instance); + ERR_FAIL_COND(!oi); Scenario *scenario = scenario_owner.getornull(p_scenario); // noop? - if (ro->scenario == scenario) { + if (oi->scenario == scenario) { return; } // if the portal is in a scenario already, remove it - if (ro->scenario) { - ro->scenario->_portal_renderer.occluder_destroy(ro->scenario_occluder_id); - ro->scenario = nullptr; - ro->scenario_occluder_id = 0; + if (oi->scenario) { + oi->scenario->_portal_renderer.occluder_instance_destroy(oi->scenario_occluder_id); + oi->scenario = nullptr; + oi->scenario_occluder_id = 0; } // create when entering the world if (scenario) { - ro->scenario = scenario; - - // defer the actual creation to here - ro->scenario_occluder_id = scenario->_portal_renderer.occluder_create((VSOccluder::Type)p_type); + oi->scenario = scenario; + oi->scenario_occluder_id = scenario->_portal_renderer.occluder_instance_create(); } } -void VisualServerScene::occluder_set_active(RID p_occluder, bool p_active) { - Occluder *ro = occluder_owner.getornull(p_occluder); - ERR_FAIL_COND(!ro); - ERR_FAIL_COND(!ro->scenario); - ro->scenario->_portal_renderer.occluder_set_active(ro->scenario_occluder_id, p_active); +void VisualServerScene::occluder_instance_set_active(RID p_occluder_instance, bool p_active) { + OccluderInstance *oi = occluder_instance_owner.getornull(p_occluder_instance); + ERR_FAIL_COND(!oi); + ERR_FAIL_COND(!oi->scenario); + oi->scenario->_portal_renderer.occluder_instance_set_active(oi->scenario_occluder_id, p_active); } -void VisualServerScene::occluder_set_transform(RID p_occluder, const Transform &p_xform) { - Occluder *ro = occluder_owner.getornull(p_occluder); - ERR_FAIL_COND(!ro); - ERR_FAIL_COND(!ro->scenario); - ro->scenario->_portal_renderer.occluder_set_transform(ro->scenario_occluder_id, p_xform); +void VisualServerScene::occluder_instance_set_transform(RID p_occluder_instance, const Transform &p_xform) { + OccluderInstance *oi = occluder_instance_owner.getornull(p_occluder_instance); + ERR_FAIL_COND(!oi); + ERR_FAIL_COND(!oi->scenario); + oi->scenario->_portal_renderer.occluder_instance_set_transform(oi->scenario_occluder_id, p_xform); } -void VisualServerScene::occluder_spheres_update(RID p_occluder, const Vector &p_spheres) { - Occluder *ro = occluder_owner.getornull(p_occluder); - ERR_FAIL_COND(!ro); - ERR_FAIL_COND(!ro->scenario); - ro->scenario->_portal_renderer.occluder_update_spheres(ro->scenario_occluder_id, p_spheres); +RID VisualServerScene::occluder_resource_create() { + OccluderResource *res = memnew(OccluderResource); + ERR_FAIL_COND_V(!res, RID()); + + res->occluder_resource_id = _portal_resources.occluder_resource_create(); + + RID occluder_resource_rid = occluder_resource_owner.make_rid(res); + return occluder_resource_rid; } -void VisualServerScene::occluder_mesh_update(RID p_occluder, const Geometry::OccluderMeshData &p_mesh_data) { - Occluder *ro = occluder_owner.getornull(p_occluder); - ERR_FAIL_COND(!ro); - ERR_FAIL_COND(!ro->scenario); - ro->scenario->_portal_renderer.occluder_update_mesh(ro->scenario_occluder_id, p_mesh_data); +void VisualServerScene::occluder_resource_prepare(RID p_occluder_resource, VisualServer::OccluderType p_type) { + OccluderResource *res = occluder_resource_owner.getornull(p_occluder_resource); + ERR_FAIL_COND(!res); + _portal_resources.occluder_resource_prepare(res->occluder_resource_id, (VSOccluder_Instance::Type)p_type); +} + +void VisualServerScene::occluder_resource_spheres_update(RID p_occluder_resource, const Vector &p_spheres) { + OccluderResource *res = occluder_resource_owner.getornull(p_occluder_resource); + ERR_FAIL_COND(!res); + _portal_resources.occluder_resource_update_spheres(res->occluder_resource_id, p_spheres); +} + +void VisualServerScene::occluder_resource_mesh_update(RID p_occluder_resource, const Geometry::OccluderMeshData &p_mesh_data) { + OccluderResource *res = occluder_resource_owner.getornull(p_occluder_resource); + ERR_FAIL_COND(!res); + _portal_resources.occluder_resource_update_mesh(res->occluder_resource_id, p_mesh_data); } void VisualServerScene::set_use_occlusion_culling(bool p_enable) { @@ -4153,10 +4176,15 @@ bool VisualServerScene::free(RID p_rid) { RoomGroup *roomgroup = roomgroup_owner.get(p_rid); roomgroup_owner.free(p_rid); memdelete(roomgroup); - } else if (occluder_owner.owns(p_rid)) { - Occluder *ro = occluder_owner.get(p_rid); - occluder_owner.free(p_rid); - memdelete(ro); + } else if (occluder_instance_owner.owns(p_rid)) { + OccluderInstance *occ_inst = occluder_instance_owner.get(p_rid); + occluder_instance_owner.free(p_rid); + memdelete(occ_inst); + } else if (occluder_resource_owner.owns(p_rid)) { + OccluderResource *occ_res = occluder_resource_owner.get(p_rid); + occ_res->destroy(_portal_resources); + occluder_resource_owner.free(p_rid); + memdelete(occ_res); } else { return false; } diff --git a/servers/visual/visual_server_scene.h b/servers/visual/visual_server_scene.h index d8c1b52d0f5..f4e783cec7a 100644 --- a/servers/visual/visual_server_scene.h +++ b/servers/visual/visual_server_scene.h @@ -678,29 +678,47 @@ public: virtual void roomgroup_add_room(RID p_roomgroup, RID p_room); // Occluders - struct Occluder : RID_Data { + struct OccluderInstance : RID_Data { uint32_t scenario_occluder_id = 0; Scenario *scenario = nullptr; - virtual ~Occluder() { + virtual ~OccluderInstance() { if (scenario) { - scenario->_portal_renderer.occluder_destroy(scenario_occluder_id); + scenario->_portal_renderer.occluder_instance_destroy(scenario_occluder_id); scenario = nullptr; scenario_occluder_id = 0; } } }; - RID_Owner occluder_owner; + RID_Owner occluder_instance_owner; - virtual RID occluder_create(); - virtual void occluder_set_scenario(RID p_occluder, RID p_scenario, VisualServer::OccluderType p_type); - virtual void occluder_spheres_update(RID p_occluder, const Vector &p_spheres); - virtual void occluder_mesh_update(RID p_occluder, const Geometry::OccluderMeshData &p_mesh_data); - virtual void occluder_set_transform(RID p_occluder, const Transform &p_xform); - virtual void occluder_set_active(RID p_occluder, bool p_active); + struct OccluderResource : RID_Data { + uint32_t occluder_resource_id = 0; + void destroy(PortalResources &r_portal_resources) { + r_portal_resources.occluder_resource_destroy(occluder_resource_id); + occluder_resource_id = 0; + } + virtual ~OccluderResource() { + DEV_ASSERT(occluder_resource_id == 0); + } + }; + RID_Owner occluder_resource_owner; + + virtual RID occluder_instance_create(); + virtual void occluder_instance_set_scenario(RID p_occluder_instance, RID p_scenario); + virtual void occluder_instance_link_resource(RID p_occluder_instance, RID p_occluder_resource); + virtual void occluder_instance_set_transform(RID p_occluder_instance, const Transform &p_xform); + virtual void occluder_instance_set_active(RID p_occluder_instance, bool p_active); + + virtual RID occluder_resource_create(); + virtual void occluder_resource_prepare(RID p_occluder_resource, VisualServer::OccluderType p_type); + virtual void occluder_resource_spheres_update(RID p_occluder_resource, const Vector &p_spheres); + virtual void occluder_resource_mesh_update(RID p_occluder_resource, const Geometry::OccluderMeshData &p_mesh_data); virtual void set_use_occlusion_culling(bool p_enable); // editor only .. slow virtual Geometry::MeshData occlusion_debug_get_current_polys(RID p_scenario) const; + const PortalResources &get_portal_resources() const { return _portal_resources; } + PortalResources &get_portal_resources() { return _portal_resources; } // Rooms struct Room : RID_Data { @@ -821,6 +839,7 @@ public: private: bool _use_bvh; VisualServerCallbacks *_visual_server_callbacks; + PortalResources _portal_resources; public: VisualServerScene(); diff --git a/servers/visual/visual_server_wrap_mt.cpp b/servers/visual/visual_server_wrap_mt.cpp index 574d5b6569e..ec8bbc5e33c 100644 --- a/servers/visual/visual_server_wrap_mt.cpp +++ b/servers/visual/visual_server_wrap_mt.cpp @@ -144,7 +144,8 @@ void VisualServerWrapMT::finish() { roomgroup_free_cached_ids(); portal_free_cached_ids(); ghost_free_cached_ids(); - occluder_free_cached_ids(); + occluder_instance_free_cached_ids(); + occluder_resource_free_cached_ids(); } void VisualServerWrapMT::set_use_vsync_callback(bool p_enable) { diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h index 68e157e5f0b..ec89bacd395 100644 --- a/servers/visual/visual_server_wrap_mt.h +++ b/servers/visual/visual_server_wrap_mt.h @@ -505,12 +505,16 @@ public: FUNC2(roomgroup_add_room, RID, RID) // Occluders - FUNCRID(occluder) - FUNC3(occluder_set_scenario, RID, RID, OccluderType) - FUNC2(occluder_spheres_update, RID, const Vector &) - FUNC2(occluder_mesh_update, RID, const Geometry::OccluderMeshData &) - FUNC2(occluder_set_transform, RID, const Transform &) - FUNC2(occluder_set_active, RID, bool) + FUNCRID(occluder_instance) + FUNC2(occluder_instance_set_scenario, RID, RID) + FUNC2(occluder_instance_link_resource, RID, RID) + FUNC2(occluder_instance_set_transform, RID, const Transform &) + FUNC2(occluder_instance_set_active, RID, bool) + + FUNCRID(occluder_resource) + FUNC2(occluder_resource_prepare, RID, OccluderType) + FUNC2(occluder_resource_spheres_update, RID, const Vector &) + FUNC2(occluder_resource_mesh_update, RID, const Geometry::OccluderMeshData &) FUNC1(set_use_occlusion_culling, bool) FUNC1RC(Geometry::MeshData, occlusion_debug_get_current_polys, RID) diff --git a/servers/visual_server.h b/servers/visual_server.h index bdf2308e759..8d6e2b0e92b 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -905,12 +905,17 @@ public: OCCLUDER_TYPE_NUM_TYPES, }; - virtual RID occluder_create() = 0; - virtual void occluder_set_scenario(RID p_occluder, RID p_scenario, VisualServer::OccluderType p_type) = 0; - virtual void occluder_spheres_update(RID p_occluder, const Vector &p_spheres) = 0; - virtual void occluder_mesh_update(RID p_occluder, const Geometry::OccluderMeshData &p_mesh_data) = 0; - virtual void occluder_set_transform(RID p_occluder, const Transform &p_xform) = 0; - virtual void occluder_set_active(RID p_occluder, bool p_active) = 0; + virtual RID occluder_instance_create() = 0; + virtual void occluder_instance_set_scenario(RID p_occluder_instance, RID p_scenario) = 0; + virtual void occluder_instance_link_resource(RID p_occluder_instance, RID p_occluder_resource) = 0; + virtual void occluder_instance_set_transform(RID p_occluder_instance, const Transform &p_xform) = 0; + virtual void occluder_instance_set_active(RID p_occluder_instance, bool p_active) = 0; + + virtual RID occluder_resource_create() = 0; + virtual void occluder_resource_prepare(RID p_occluder_resource, VisualServer::OccluderType p_type) = 0; + virtual void occluder_resource_spheres_update(RID p_occluder_resource, const Vector &p_spheres) = 0; + virtual void occluder_resource_mesh_update(RID p_occluder_resource, const Geometry::OccluderMeshData &p_mesh_data) = 0; + virtual void set_use_occlusion_culling(bool p_enable) = 0; virtual Geometry::MeshData occlusion_debug_get_current_polys(RID p_scenario) const = 0;