VisibilityNotifier - add max_distance feature
Enables turning off objects by distance to the camera in addition to testing whether they are in the view frustum.
This commit is contained in:
parent
6ec7e2c230
commit
0c5a424f65
4 changed files with 78 additions and 2 deletions
|
@ -23,6 +23,10 @@
|
||||||
<member name="aabb" type="AABB" setter="set_aabb" getter="get_aabb" default="AABB( -1, -1, -1, 2, 2, 2 )">
|
<member name="aabb" type="AABB" setter="set_aabb" getter="get_aabb" default="AABB( -1, -1, -1, 2, 2, 2 )">
|
||||||
The VisibilityNotifier's bounding box.
|
The VisibilityNotifier's bounding box.
|
||||||
</member>
|
</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>
|
</members>
|
||||||
<signals>
|
<signals>
|
||||||
<signal name="camera_entered">
|
<signal name="camera_entered">
|
||||||
|
|
|
@ -72,6 +72,24 @@ 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) {
|
void VisibilityNotifier::set_aabb(const AABB &p_aabb) {
|
||||||
if (aabb == p_aabb) {
|
if (aabb == p_aabb) {
|
||||||
return;
|
return;
|
||||||
|
@ -79,7 +97,9 @@ void VisibilityNotifier::set_aabb(const AABB &p_aabb) {
|
||||||
aabb = p_aabb;
|
aabb = p_aabb;
|
||||||
|
|
||||||
if (is_inside_world()) {
|
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");
|
_change_notify("aabb");
|
||||||
|
@ -128,11 +148,15 @@ void VisibilityNotifier::_notification(int p_what) {
|
||||||
|
|
||||||
AABB world_aabb = get_global_transform().xform(aabb);
|
AABB world_aabb = get_global_transform().xform(aabb);
|
||||||
world->_register_notifier(this, world_aabb);
|
world->_register_notifier(this, world_aabb);
|
||||||
|
_world_aabb_center = world_aabb.get_center();
|
||||||
_refresh_portal_mode();
|
_refresh_portal_mode();
|
||||||
} break;
|
} break;
|
||||||
case NOTIFICATION_TRANSFORM_CHANGED: {
|
case NOTIFICATION_TRANSFORM_CHANGED: {
|
||||||
AABB world_aabb = get_global_transform().xform(aabb);
|
AABB world_aabb = get_global_transform().xform(aabb);
|
||||||
world->_update_notifier(this, world_aabb);
|
world->_update_notifier(this, world_aabb);
|
||||||
|
if (_max_distance_active) {
|
||||||
|
_world_aabb_center = world_aabb.get_center();
|
||||||
|
}
|
||||||
|
|
||||||
if (_cull_instance_rid != RID()) {
|
if (_cull_instance_rid != RID()) {
|
||||||
VisualServer::get_singleton()->ghost_update(_cull_instance_rid, world_aabb);
|
VisualServer::get_singleton()->ghost_update(_cull_instance_rid, world_aabb);
|
||||||
|
@ -176,7 +200,11 @@ void VisibilityNotifier::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("get_aabb"), &VisibilityNotifier::get_aabb);
|
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("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::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_entered", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera")));
|
||||||
ADD_SIGNAL(MethodInfo("camera_exited", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera")));
|
ADD_SIGNAL(MethodInfo("camera_exited", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera")));
|
||||||
|
@ -188,6 +216,10 @@ VisibilityNotifier::VisibilityNotifier() {
|
||||||
aabb = AABB(Vector3(-1, -1, -1), Vector3(2, 2, 2));
|
aabb = AABB(Vector3(-1, -1, -1), Vector3(2, 2, 2));
|
||||||
set_notify_transform(true);
|
set_notify_transform(true);
|
||||||
_in_gameplay = false;
|
_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() {
|
VisibilityNotifier::~VisibilityNotifier() {
|
||||||
|
|
|
@ -42,11 +42,21 @@ class VisibilityNotifier : public CullInstance {
|
||||||
Set<Camera *> cameras;
|
Set<Camera *> cameras;
|
||||||
|
|
||||||
AABB aabb;
|
AABB aabb;
|
||||||
|
Vector3 _world_aabb_center;
|
||||||
|
|
||||||
// if using rooms and portals
|
// if using rooms and portals
|
||||||
RID _cull_instance_rid;
|
RID _cull_instance_rid;
|
||||||
bool _in_gameplay;
|
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:
|
protected:
|
||||||
virtual void _screen_enter() {}
|
virtual void _screen_enter() {}
|
||||||
virtual void _screen_exit() {}
|
virtual void _screen_exit() {}
|
||||||
|
@ -64,6 +74,21 @@ public:
|
||||||
AABB get_aabb() const;
|
AABB get_aabb() const;
|
||||||
bool is_on_screen() 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();
|
||||||
~VisibilityNotifier();
|
~VisibilityNotifier();
|
||||||
};
|
};
|
||||||
|
|
|
@ -146,9 +146,11 @@ struct SpatialIndexer {
|
||||||
for (Map<Camera *, CameraData>::Element *E = cameras.front(); E; E = E->next()) {
|
for (Map<Camera *, CameraData>::Element *E = cameras.front(); E; E = E->next()) {
|
||||||
pass++;
|
pass++;
|
||||||
|
|
||||||
|
// prepare camera info
|
||||||
Camera *c = E->key();
|
Camera *c = E->key();
|
||||||
|
Vector3 cam_pos = c->get_global_transform().origin;
|
||||||
Vector<Plane> planes = c->get_frustum();
|
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());
|
int culled = octree.cull_convex(planes, cull.ptrw(), cull.size());
|
||||||
|
|
||||||
|
@ -160,6 +162,19 @@ struct SpatialIndexer {
|
||||||
for (int i = 0; i < culled; i++) {
|
for (int i = 0; i < culled; i++) {
|
||||||
//notifiers in frustum
|
//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;
|
||||||
|
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]);
|
Map<VisibilityNotifier *, uint64_t>::Element *H = E->get().notifiers.find(ptr[i]);
|
||||||
if (!H) {
|
if (!H) {
|
||||||
E->get().notifiers.insert(ptr[i], pass);
|
E->get().notifiers.insert(ptr[i], pass);
|
||||||
|
|
Loading…
Reference in a new issue