Fixed false positives in the culling system.

This fixes numerous false positives coming out of the culling system.

AABB checks are now a full separating-axis check against the frustum, with the points of the frustum being compared to the planes of the box just as the points of the box were being compared to the planes of the frustum. This fixes large objects behind the camera not being culled correctly.

Some systems that used frustums that were (sometimes mistakenly?) unbounded on one or more side have been modified to be fully enclosed.
This commit is contained in:
Kiri Jolly 2020-04-28 17:14:06 -07:00
parent 2c6e859c2d
commit bac44ee19f
8 changed files with 93 additions and 24 deletions

View File

@ -76,7 +76,7 @@ public:
bool intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *r_clip = NULL, Vector3 *r_normal = NULL) const;
_FORCE_INLINE_ bool smits_intersect_ray(const Vector3 &p_from, const Vector3 &p_dir, real_t t0, real_t t1) const;
_FORCE_INLINE_ bool intersects_convex_shape(const Plane *p_planes, int p_plane_count) const;
_FORCE_INLINE_ bool intersects_convex_shape(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count) const;
_FORCE_INLINE_ bool inside_convex_shape(const Plane *p_planes, int p_plane_count) const;
bool intersects_plane(const Plane &p_plane) const;
@ -190,7 +190,7 @@ Vector3 AABB::get_endpoint(int p_point) const {
ERR_FAIL_V(Vector3());
}
bool AABB::intersects_convex_shape(const Plane *p_planes, int p_plane_count) const {
bool AABB::intersects_convex_shape(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count) const {
Vector3 half_extents = size * 0.5;
Vector3 ofs = position + half_extents;
@ -206,6 +206,30 @@ bool AABB::intersects_convex_shape(const Plane *p_planes, int p_plane_count) con
return false;
}
// Make sure all points in the shape aren't fully separated from the AABB on
// each axis.
int bad_point_counts_positive[3] = { 0 };
int bad_point_counts_negative[3] = { 0 };
for (int k = 0; k < 3; k++) {
for (int i = 0; i < p_point_count; i++) {
if (p_points[i].coord[k] > ofs.coord[k] + half_extents.coord[k]) {
bad_point_counts_positive[k]++;
}
if (p_points[i].coord[k] < ofs.coord[k] - half_extents.coord[k]) {
bad_point_counts_negative[k]++;
}
}
if (bad_point_counts_negative[k] == p_point_count) {
return false;
}
if (bad_point_counts_positive[k] == p_point_count) {
return false;
}
}
return true;
}

View File

@ -1185,3 +1185,42 @@ Vector<Vector<Point2> > Geometry::_polypath_offset(const Vector<Point2> &p_polyp
}
return polypaths;
}
Vector<Vector3> Geometry::compute_convex_mesh_points(const Plane *p_planes, int p_plane_count) {
Vector<Vector3> points;
// Iterate through every unique combination of any three planes.
for (int i = p_plane_count - 1; i >= 0; i--) {
for (int j = i - 1; j >= 0; j--) {
for (int k = j - 1; k >= 0; k--) {
// Find the point where these planes all cross over (if they
// do at all).
Vector3 convex_shape_point;
if (p_planes[i].intersect_3(p_planes[j], p_planes[k], &convex_shape_point)) {
// See if any *other* plane excludes this point because it's
// on the wrong side.
bool excluded = false;
for (int n = 0; n < p_plane_count; n++) {
if (n != i && n != j && n != k) {
real_t dp = p_planes[n].normal.dot(convex_shape_point);
if (dp - p_planes[n].d > CMP_EPSILON) {
excluded = true;
break;
}
}
}
// Only add the point if it passed all tests.
if (!excluded) {
points.push_back(convex_shape_point);
}
}
}
}
}
return points;
}

View File

@ -1014,6 +1014,8 @@ public:
static void make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_result, Size2i &r_size);
static Vector<Vector3> compute_convex_mesh_points(const Plane *p_planes, int p_plane_count);
private:
static Vector<Vector<Point2> > _polypaths_do_operation(PolyBooleanOperation p_op, const Vector<Point2> &p_polypath_a, const Vector<Point2> &p_polypath_b, bool is_a_open = false);
static Vector<Vector<Point2> > _polypath_offset(const Vector<Point2> &p_polypath, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type);

View File

@ -34,6 +34,7 @@
#include "core/list.h"
#include "core/map.h"
#include "core/math/aabb.h"
#include "core/math/geometry.h"
#include "core/math/vector3.h"
#include "core/print_string.h"
#include "core/variant.h"
@ -341,6 +342,8 @@ private:
const Plane *planes;
int plane_count;
const Vector3 *points;
int point_count;
T **result_array;
int *result_idx;
int result_max;
@ -1017,8 +1020,7 @@ void Octree<T, use_pairs, AL>::_cull_convex(Octant *p_octant, _CullConvexData *p
continue;
e->last_pass = pass;
if (e->aabb.intersects_convex_shape(p_cull->planes, p_cull->plane_count)) {
if (e->aabb.intersects_convex_shape(p_cull->planes, p_cull->plane_count, p_cull->points, p_cull->point_count)) {
if (*p_cull->result_idx < p_cull->result_max) {
p_cull->result_array[*p_cull->result_idx] = e->userdata;
(*p_cull->result_idx)++;
@ -1043,7 +1045,7 @@ void Octree<T, use_pairs, AL>::_cull_convex(Octant *p_octant, _CullConvexData *p
continue;
e->last_pass = pass;
if (e->aabb.intersects_convex_shape(p_cull->planes, p_cull->plane_count)) {
if (e->aabb.intersects_convex_shape(p_cull->planes, p_cull->plane_count, p_cull->points, p_cull->point_count)) {
if (*p_cull->result_idx < p_cull->result_max) {
@ -1059,7 +1061,7 @@ void Octree<T, use_pairs, AL>::_cull_convex(Octant *p_octant, _CullConvexData *p
for (int i = 0; i < 8; i++) {
if (p_octant->children[i] && p_octant->children[i]->aabb.intersects_convex_shape(p_cull->planes, p_cull->plane_count)) {
if (p_octant->children[i] && p_octant->children[i]->aabb.intersects_convex_shape(p_cull->planes, p_cull->plane_count, p_cull->points, p_cull->point_count)) {
_cull_convex(p_octant->children[i], p_cull);
}
}
@ -1291,11 +1293,15 @@ int Octree<T, use_pairs, AL>::cull_convex(const Vector<Plane> &p_convex, T **p_r
if (!root)
return 0;
Vector<Vector3> convex_points = Geometry::compute_convex_mesh_points(&p_convex[0], p_convex.size());
int result_count = 0;
pass++;
_CullConvexData cdata;
cdata.planes = &p_convex[0];
cdata.plane_count = p_convex.size();
cdata.points = &convex_points[0];
cdata.point_count = convex_points.size();
cdata.result_array = p_result_array;
cdata.result_max = p_result_max;
cdata.result_idx = &result_count;

View File

@ -511,7 +511,7 @@ bool TriangleMesh::intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, V
return inters;
}
bool TriangleMesh::intersect_convex_shape(const Plane *p_planes, int p_plane_count) const {
bool TriangleMesh::intersect_convex_shape(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count) const {
uint32_t *stack = (uint32_t *)alloca(sizeof(int) * max_depth);
//p_fully_inside = true;
@ -548,7 +548,7 @@ bool TriangleMesh::intersect_convex_shape(const Plane *p_planes, int p_plane_cou
switch (stack[level] >> VISITED_BIT_SHIFT) {
case TEST_AABB_BIT: {
bool valid = b.aabb.intersects_convex_shape(p_planes, p_plane_count);
bool valid = b.aabb.intersects_convex_shape(p_planes, p_plane_count, p_points, p_point_count);
if (!valid) {
stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
@ -629,7 +629,7 @@ bool TriangleMesh::intersect_convex_shape(const Plane *p_planes, int p_plane_cou
return false;
}
bool TriangleMesh::inside_convex_shape(const Plane *p_planes, int p_plane_count, Vector3 p_scale) const {
bool TriangleMesh::inside_convex_shape(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count, Vector3 p_scale) const {
uint32_t *stack = (uint32_t *)alloca(sizeof(int) * max_depth);
enum {
@ -666,7 +666,7 @@ bool TriangleMesh::inside_convex_shape(const Plane *p_planes, int p_plane_count,
switch (stack[level] >> VISITED_BIT_SHIFT) {
case TEST_AABB_BIT: {
bool intersects = scale.xform(b.aabb).intersects_convex_shape(p_planes, p_plane_count);
bool intersects = scale.xform(b.aabb).intersects_convex_shape(p_planes, p_plane_count, p_points, p_point_count);
if (!intersects) return false;
bool inside = scale.xform(b.aabb).inside_convex_shape(p_planes, p_plane_count);

View File

@ -90,8 +90,8 @@ public:
bool is_valid() const;
bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal) const;
bool intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, Vector3 &r_point, Vector3 &r_normal) const;
bool intersect_convex_shape(const Plane *p_planes, int p_plane_count) const;
bool inside_convex_shape(const Plane *p_planes, int p_plane_count, Vector3 p_scale = Vector3(1, 1, 1)) const;
bool intersect_convex_shape(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count) const;
bool inside_convex_shape(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count, Vector3 p_scale = Vector3(1, 1, 1)) const;
Vector3 get_area_normal(const AABB &p_aabb) const;
PoolVector<Face3> get_faces() const;

View File

@ -669,17 +669,13 @@ void SpatialEditorViewport::_select_region() {
}
}
if (!orthogonal) {
Plane near(cam_pos, -_get_camera_normal());
near.d -= get_znear();
Plane near(cam_pos, -_get_camera_normal());
near.d -= get_znear();
frustum.push_back(near);
frustum.push_back(near);
Plane far = -near;
far.d += get_zfar();
frustum.push_back(far);
}
Plane far = -near;
far.d += get_zfar();
frustum.push_back(far);
Vector<ObjectID> instances = VisualServer::get_singleton()->instances_cull_convex(frustum, get_tree()->get_root()->get_world()->get_scenario());
Vector<Node *> selected;

View File

@ -476,11 +476,13 @@ bool EditorSpatialGizmo::intersect_frustum(const Camera *p_camera, const Vector<
Vector<Plane> transformed_frustum;
for (int i = 0; i < 4; i++) {
for (int i = 0; i < p_frustum.size(); i++) {
transformed_frustum.push_back(it.xform(p_frustum[i]));
}
if (collision_mesh->inside_convex_shape(transformed_frustum.ptr(), transformed_frustum.size(), mesh_scale)) {
Vector<Vector3> convex_points = Geometry::compute_convex_mesh_points(p_frustum.ptr(), p_frustum.size());
if (collision_mesh->inside_convex_shape(transformed_frustum.ptr(), transformed_frustum.size(), convex_points.ptr(), convex_points.size(), mesh_scale)) {
return true;
}
}