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:
parent
685d4b4739
commit
3c2df49832
20 changed files with 728 additions and 386 deletions
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
216
servers/visual/portals/portal_resources.cpp
Normal file
216
servers/visual/portals/portal_resources.cpp
Normal 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
|
||||
}
|
68
servers/visual/portals/portal_resources.h
Normal file
68
servers/visual/portals/portal_resources.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue