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.
This commit is contained in:
lawnjelly 2022-02-11 14:36:02 +00:00
parent 685d4b4739
commit 3c2df49832
20 changed files with 728 additions and 386 deletions

View file

@ -28,7 +28,7 @@
</method>
</methods>
<members>
<member name="spheres" type="Array" setter="set_spheres" getter="get_spheres" default="[ ]">
<member name="spheres" type="Array" setter="set_spheres" getter="get_spheres" default="[ Plane( 0, 0, 0, 1 ) ]">
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.
</member>
</members>

View file

@ -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<OccluderShape> &p_shape) {
@ -52,16 +48,9 @@ void Occluder::set_shape(const Ref<OccluderShape> &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);
}

View file

@ -40,6 +40,7 @@ class Occluder : public Spatial {
friend class OccluderSpatialGizmo;
friend class OccluderEditorPlugin;
RID _occluder_instance;
Ref<OccluderShape> _shape;
void resource_changed(RES res);

View file

@ -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<Plane> &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<Plane> &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<Plane> planes;
planes.push_back(Plane(Vector3(0, 0, 0), 1));
set_spheres(planes);
}

View file

@ -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

View file

@ -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<Vector2> &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<Vector2> &p_points) {
_hole_pts_local_raw = p_points;
_sanitize_points();
update_shape_to_visual_server();
notify_change_to_owners();
}
@ -141,10 +145,6 @@ PoolVector<Vector2> 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<Vector2> points;

View file

@ -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

View file

@ -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);

View file

@ -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<Plane> &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<Plane> &p_planes) const {
for (unsigned int p = 0; p < p_planes.size(); p++) {

View file

@ -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<Geometry::OccluderMeshData::Face> &faces = p_mesh_data.faces;
const LocalVectori<Vector3> &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<Plane> &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<Plane>());
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);

View file

@ -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<Plane> &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<Plane> &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<Plane> &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<uint32_t, uint32_t> &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<uint32_t, uint32_t> &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<uint32_t, int32_t> _moving_list_roaming;
// occluders
TrackedPooledList<VSOccluder> _occluder_pool;
TrackedPooledList<VSOccluder_Sphere, uint32_t, true> _occluder_sphere_pool;
TrackedPooledList<VSOccluder_Mesh, uint32_t, true> _occluder_mesh_pool;
TrackedPooledList<VSOccluder_Hole, uint32_t, true> _occluder_hole_pool;
TrackedPooledList<VSOccluder_Instance> _occluder_instance_pool;
TrackedPooledList<VSOccluder_Sphere, uint32_t, true> _occluder_world_sphere_pool;
TrackedPooledList<VSOccluder_Poly, uint32_t, true> _occluder_world_poly_pool;
TrackedPooledList<VSOccluder_Hole, uint32_t, true> _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]);
}
}
}

View file

@ -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<Plane>());
} 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<Plane> &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<Geometry::OccluderMeshData::Face> &faces = p_mesh_data.faces;
const LocalVectori<Vector3> &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
}

View file

@ -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<Plane> &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<VSOccluder_Resource> _occluder_resource_pool;
TrackedPooledList<VSOccluder_Sphere, uint32_t, true> _occluder_local_sphere_pool;
TrackedPooledList<VSOccluder_Poly, uint32_t, true> _occluder_local_poly_pool;
TrackedPooledList<VSOccluder_Hole, uint32_t, true> _occluder_local_hole_pool;
};
#endif // PORTAL_RESOURCES_H

View file

@ -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<uint32_t, int32_t> _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<uint32_t, int32_t> 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<uint32_t, int32_t> 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

View file

@ -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<Plane> &)
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<Plane> &)
BIND2(occluder_resource_mesh_update, RID, const Geometry::OccluderMeshData &)
BIND1(set_use_occlusion_culling, bool)
BIND1RC(Geometry::MeshData, occlusion_debug_get_current_polys, RID)

View file

@ -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<Plane> &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<Plane> &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;
}

View file

@ -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> occluder_owner;
RID_Owner<OccluderInstance> 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<Plane> &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<OccluderResource> 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<Plane> &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();

View file

@ -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) {

View file

@ -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<Plane> &)
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<Plane> &)
FUNC2(occluder_resource_mesh_update, RID, const Geometry::OccluderMeshData &)
FUNC1(set_use_occlusion_culling, bool)
FUNC1RC(Geometry::MeshData, occlusion_debug_get_current_polys, RID)

View file

@ -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<Plane> &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<Plane> &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;