Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VisibilityNotifier - add max_distance feature #61544

Merged
merged 1 commit into from
Jun 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/classes/VisibilityNotifier.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
<member name="aabb" type="AABB" setter="set_aabb" getter="get_aabb" default="AABB( -1, -1, -1, 2, 2, 2 )">
The VisibilityNotifier's bounding box.
</member>
<member name="max_distance" type="float" setter="set_max_distance" getter="get_max_distance" default="0.0">
In addition to checking whether a node is on screen or within a [Camera]'s view, VisibilityNotifier can also optionally check whether a node is within a specified maximum distance when using a [Camera] with perspective projection. This is useful for throttling the performance requirements of nodes that are far away.
[b]Note:[/b] This feature will be disabled if set to 0.0.
</member>
</members>
<signals>
<signal name="camera_entered">
Expand Down
34 changes: 33 additions & 1 deletion scene/3d/visibility_notifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,34 @@ void VisibilityNotifier::_exit_camera(Camera *p_camera) {
}
}

void VisibilityNotifier::set_max_distance(real_t p_max_distance) {
if (p_max_distance > CMP_EPSILON) {
_max_distance = p_max_distance;
_max_distance_squared = _max_distance * _max_distance;
_max_distance_active = true;

// make sure world aabb centre is up to date
if (is_inside_world()) {
AABB world_aabb = get_global_transform().xform(aabb);
_world_aabb_center = world_aabb.get_center();
}
} else {
_max_distance = 0.0;
_max_distance_squared = 0.0;
_max_distance_active = false;
}
}

void VisibilityNotifier::set_aabb(const AABB &p_aabb) {
if (aabb == p_aabb) {
return;
}
aabb = p_aabb;

if (is_inside_world()) {
get_world()->_update_notifier(this, get_global_transform().xform(aabb));
AABB world_aabb = get_global_transform().xform(aabb);
get_world()->_update_notifier(this, world_aabb);
_world_aabb_center = world_aabb.get_center();
}

_change_notify("aabb");
Expand Down Expand Up @@ -128,11 +148,15 @@ void VisibilityNotifier::_notification(int p_what) {

AABB world_aabb = get_global_transform().xform(aabb);
world->_register_notifier(this, world_aabb);
_world_aabb_center = world_aabb.get_center();
_refresh_portal_mode();
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
AABB world_aabb = get_global_transform().xform(aabb);
world->_update_notifier(this, world_aabb);
if (_max_distance_active) {
_world_aabb_center = world_aabb.get_center();
}

if (_cull_instance_rid != RID()) {
VisualServer::get_singleton()->ghost_update(_cull_instance_rid, world_aabb);
Expand Down Expand Up @@ -176,7 +200,11 @@ void VisibilityNotifier::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_aabb"), &VisibilityNotifier::get_aabb);
ClassDB::bind_method(D_METHOD("is_on_screen"), &VisibilityNotifier::is_on_screen);

ClassDB::bind_method(D_METHOD("set_max_distance", "distance"), &VisibilityNotifier::set_max_distance);
ClassDB::bind_method(D_METHOD("get_max_distance"), &VisibilityNotifier::get_max_distance);

ADD_PROPERTY(PropertyInfo(Variant::AABB, "aabb"), "set_aabb", "get_aabb");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_max_distance", "get_max_distance");

ADD_SIGNAL(MethodInfo("camera_entered", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera")));
ADD_SIGNAL(MethodInfo("camera_exited", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera")));
Expand All @@ -188,6 +216,10 @@ VisibilityNotifier::VisibilityNotifier() {
aabb = AABB(Vector3(-1, -1, -1), Vector3(2, 2, 2));
set_notify_transform(true);
_in_gameplay = false;
_max_distance_active = false;
_max_distance = 0.0;
_max_distance_squared = 0.0;
_max_distance_leadin_counter = 1; // this could later be exposed as a property if necessary
}

VisibilityNotifier::~VisibilityNotifier() {
Expand Down
25 changes: 25 additions & 0 deletions scene/3d/visibility_notifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,21 @@ class VisibilityNotifier : public CullInstance {
Set<Camera *> cameras;

AABB aabb;
Vector3 _world_aabb_center;

// if using rooms and portals
RID _cull_instance_rid;
bool _in_gameplay;

bool _max_distance_active;
real_t _max_distance;
real_t _max_distance_squared;

// This is a first number of frames where distance objects
// are forced seen as visible, to make sure their animations
// and physics positions etc are something reasonable.
uint32_t _max_distance_leadin_counter;

protected:
virtual void _screen_enter() {}
virtual void _screen_exit() {}
Expand All @@ -64,6 +74,21 @@ class VisibilityNotifier : public CullInstance {
AABB get_aabb() const;
bool is_on_screen() const;

// This is only currently kept up to date if max_distance is active
const Vector3 &get_world_aabb_center() const { return _world_aabb_center; }

void set_max_distance(real_t p_max_distance);
real_t get_max_distance() const { return _max_distance; }
real_t get_max_distance_squared() const { return _max_distance_squared; }
bool is_max_distance_active() const { return _max_distance_active; }
bool inside_max_distance_leadin() {
if (!_max_distance_leadin_counter) {
return false;
}
_max_distance_leadin_counter--;
return true;
}

VisibilityNotifier();
~VisibilityNotifier();
};
Expand Down
17 changes: 16 additions & 1 deletion scene/resources/world.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,11 @@ struct SpatialIndexer {
for (Map<Camera *, CameraData>::Element *E = cameras.front(); E; E = E->next()) {
pass++;

// prepare camera info
Camera *c = E->key();

Vector3 cam_pos = c->get_global_transform().origin;
Vector<Plane> planes = c->get_frustum();
bool cam_is_ortho = c->get_projection() == Camera::PROJECTION_ORTHOGONAL;

int culled = octree.cull_convex(planes, cull.ptrw(), cull.size());

Expand All @@ -160,6 +162,19 @@ struct SpatialIndexer {
for (int i = 0; i < culled; i++) {
//notifiers in frustum

// check and remove notifiers that have a max range
VisibilityNotifier &nt = *ptr[i];
if (nt.is_max_distance_active() && !cam_is_ortho) {
Vector3 offset = nt.get_world_aabb_center() - cam_pos;
clayjohn marked this conversation as resolved.
Show resolved Hide resolved
if ((offset.length_squared() >= nt.get_max_distance_squared()) && !nt.inside_max_distance_leadin()) {
// unordered remove
cull.set(i, cull[culled - 1]);
culled--;
i--;
continue;
}
}

Map<VisibilityNotifier *, uint64_t>::Element *H = E->get().notifiers.find(ptr[i]);
if (!H) {
E->get().notifiers.insert(ptr[i], pass);
Expand Down