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 d0fc27489ce..56a643c7f4b 100644
--- a/servers/visual/visual_server_raster.h
+++ b/servers/visual/visual_server_raster.h
@@ -603,12 +603,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 709b60e1832..138fb2c164f 100644
--- a/servers/visual/visual_server_scene.cpp
+++ b/servers/visual/visual_server_scene.cpp
@@ -1517,65 +1517,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) {
@@ -4505,10 +4528,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 9c374ac9e1a..f1a9040791f 100644
--- a/servers/visual/visual_server_scene.h
+++ b/servers/visual/visual_server_scene.h
@@ -729,29 +729,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 {
@@ -876,6 +894,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 2608ddcf263..54102875e8f 100644
--- a/servers/visual/visual_server_wrap_mt.cpp
+++ b/servers/visual/visual_server_wrap_mt.cpp
@@ -174,7 +174,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 56328ce5916..e2c97cab3ba 100644
--- a/servers/visual/visual_server_wrap_mt.h
+++ b/servers/visual/visual_server_wrap_mt.h
@@ -518,12 +518,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 35008a743ea..356e7148e5f 100644
--- a/servers/visual_server.h
+++ b/servers/visual_server.h
@@ -917,12 +917,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;