Skip to content

Commit

Permalink
Merge pull request #51355 from lawnjelly/fix_plane_xform_4_new
Browse files Browse the repository at this point in the history
  • Loading branch information
akien-mga authored Aug 7, 2021
2 parents d6329c5 + dd0f54a commit a888f14
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 32 deletions.
81 changes: 56 additions & 25 deletions core/math/transform_3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,24 @@ class Transform3D {
bool operator!=(const Transform3D &p_transform) const;

_FORCE_INLINE_ Vector3 xform(const Vector3 &p_vector) const;
_FORCE_INLINE_ AABB xform(const AABB &p_aabb) const;
_FORCE_INLINE_ Vector<Vector3> xform(const Vector<Vector3> &p_array) const;

// NOTE: These are UNSAFE with non-uniform scaling, and will produce incorrect results.
// They use the transpose.
// For safe inverse transforms, xform by the affine_inverse.
_FORCE_INLINE_ Vector3 xform_inv(const Vector3 &p_vector) const;
_FORCE_INLINE_ AABB xform_inv(const AABB &p_aabb) const;
_FORCE_INLINE_ Vector<Vector3> xform_inv(const Vector<Vector3> &p_array) const;

// Safe with non-uniform scaling (uses affine_inverse).
_FORCE_INLINE_ Plane xform(const Plane &p_plane) const;
_FORCE_INLINE_ Plane xform_inv(const Plane &p_plane) const;

_FORCE_INLINE_ AABB xform(const AABB &p_aabb) const;
_FORCE_INLINE_ AABB xform_inv(const AABB &p_aabb) const;

_FORCE_INLINE_ Vector<Vector3> xform(const Vector<Vector3> &p_array) const;
_FORCE_INLINE_ Vector<Vector3> xform_inv(const Vector<Vector3> &p_array) const;
// These fast versions use precomputed affine inverse, and should be used in bottleneck areas where
// multiple planes are to be transformed.
_FORCE_INLINE_ Plane xform_fast(const Plane &p_plane, const Basis &p_basis_inverse_transpose) const;
static _FORCE_INLINE_ Plane xform_inv_fast(const Plane &p_plane, const Transform3D &p_inverse, const Basis &p_basis_transpose);

void operator*=(const Transform3D &p_transform);
Transform3D operator*(const Transform3D &p_transform) const;
Expand Down Expand Up @@ -130,30 +138,20 @@ _FORCE_INLINE_ Vector3 Transform3D::xform_inv(const Vector3 &p_vector) const {
(basis.elements[0][2] * v.x) + (basis.elements[1][2] * v.y) + (basis.elements[2][2] * v.z));
}

// Neither the plane regular xform or xform_inv are particularly efficient,
// as they do a basis inverse. For xforming a large number
// of planes it is better to pre-calculate the inverse transpose basis once
// and reuse it for each plane, by using the 'fast' version of the functions.
_FORCE_INLINE_ Plane Transform3D::xform(const Plane &p_plane) const {
Vector3 point = p_plane.normal * p_plane.d;
Vector3 point_dir = point + p_plane.normal;
point = xform(point);
point_dir = xform(point_dir);

Vector3 normal = point_dir - point;
normal.normalize();
real_t d = normal.dot(point);

return Plane(normal, d);
Basis b = basis.inverse();
b.transpose();
return xform_fast(p_plane, b);
}

_FORCE_INLINE_ Plane Transform3D::xform_inv(const Plane &p_plane) const {
Vector3 point = p_plane.normal * p_plane.d;
Vector3 point_dir = point + p_plane.normal;
point = xform_inv(point);
point_dir = xform_inv(point_dir);

Vector3 normal = point_dir - point;
normal.normalize();
real_t d = normal.dot(point);

return Plane(normal, d);
Transform3D inv = affine_inverse();
Basis basis_transpose = basis.transposed();
return xform_inv_fast(p_plane, inv, basis_transpose);
}

_FORCE_INLINE_ AABB Transform3D::xform(const AABB &p_aabb) const {
Expand Down Expand Up @@ -231,4 +229,37 @@ Vector<Vector3> Transform3D::xform_inv(const Vector<Vector3> &p_array) const {
return array;
}

_FORCE_INLINE_ Plane Transform3D::xform_fast(const Plane &p_plane, const Basis &p_basis_inverse_transpose) const {
// Transform a single point on the plane.
Vector3 point = p_plane.normal * p_plane.d;
point = xform(point);

// Use inverse transpose for correct normals with non-uniform scaling.
Vector3 normal = p_basis_inverse_transpose.xform(p_plane.normal);
normal.normalize();

real_t d = normal.dot(point);
return Plane(normal, d);
}

_FORCE_INLINE_ Plane Transform3D::xform_inv_fast(const Plane &p_plane, const Transform3D &p_inverse, const Basis &p_basis_transpose) {
// Transform a single point on the plane.
Vector3 point = p_plane.normal * p_plane.d;
point = p_inverse.xform(point);

// Note that instead of precalculating the transpose, an alternative
// would be to use the transpose for the basis transform.
// However that would be less SIMD friendly (requiring a swizzle).
// So the cost is one extra precalced value in the calling code.
// This is probably worth it, as this could be used in bottleneck areas. And
// where it is not a bottleneck, the non-fast method is fine.

// Use transpose for correct normals with non-uniform scaling.
Vector3 normal = p_basis_transpose.xform(p_plane.normal);
normal.normalize();

real_t d = normal.dot(point);
return Plane(normal, d);
}

#endif // TRANSFORM_H
27 changes: 20 additions & 7 deletions servers/physics_3d/collision_solver_3d_sat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -956,9 +956,12 @@ static void _collision_sphere_convex_polygon(const Shape3DSW *p_a, const Transfo
const Vector3 *vertices = mesh.vertices.ptr();
int vertex_count = mesh.vertices.size();

// Precalculating this makes the transforms faster.
Basis b_xform_normal = p_transform_b.basis.inverse().transposed();

// faces of B
for (int i = 0; i < face_count; i++) {
Vector3 axis = p_transform_b.xform(faces[i].plane).normal;
Vector3 axis = b_xform_normal.xform(faces[i].plane.normal).normalized();

if (!separator.test_axis(axis)) {
return;
Expand Down Expand Up @@ -1379,9 +1382,12 @@ static void _collision_box_convex_polygon(const Shape3DSW *p_a, const Transform3
}
}

// Precalculating this makes the transforms faster.
Basis b_xform_normal = p_transform_b.basis.inverse().transposed();

// faces of B
for (int i = 0; i < face_count; i++) {
Vector3 axis = p_transform_b.xform(faces[i].plane).normal;
Vector3 axis = b_xform_normal.xform(faces[i].plane.normal).normalized();

if (!separator.test_axis(axis)) {
return;
Expand Down Expand Up @@ -1733,9 +1739,12 @@ static void _collision_capsule_convex_polygon(const Shape3DSW *p_a, const Transf
int edge_count = mesh.edges.size();
const Vector3 *vertices = mesh.vertices.ptr();

// Precalculating this makes the transforms faster.
Basis b_xform_normal = p_transform_b.basis.inverse().transposed();

// faces of B
for (int i = 0; i < face_count; i++) {
Vector3 axis = p_transform_b.xform(faces[i].plane).normal;
Vector3 axis = b_xform_normal.xform(faces[i].plane.normal).normalized();

if (!separator.test_axis(axis)) {
return;
Expand Down Expand Up @@ -2057,20 +2066,24 @@ static void _collision_convex_polygon_convex_polygon(const Shape3DSW *p_a, const
const Vector3 *vertices_B = mesh_B.vertices.ptr();
int vertex_count_B = mesh_B.vertices.size();

// Precalculating this makes the transforms faster.
Basis a_xform_normal = p_transform_b.basis.inverse().transposed();

// faces of A
for (int i = 0; i < face_count_A; i++) {
Vector3 axis = p_transform_a.xform(faces_A[i].plane).normal;
//Vector3 axis = p_transform_a.basis.xform( faces_A[i].plane.normal ).normalized();
Vector3 axis = a_xform_normal.xform(faces_A[i].plane.normal).normalized();

if (!separator.test_axis(axis)) {
return;
}
}

// Precalculating this makes the transforms faster.
Basis b_xform_normal = p_transform_b.basis.inverse().transposed();

// faces of B
for (int i = 0; i < face_count_B; i++) {
Vector3 axis = p_transform_b.xform(faces_B[i].plane).normal;
//Vector3 axis = p_transform_b.basis.xform( faces_B[i].plane.normal ).normalized();
Vector3 axis = b_xform_normal.xform(faces_B[i].plane.normal).normalized();

if (!separator.test_axis(axis)) {
return;
Expand Down

0 comments on commit a888f14

Please sign in to comment.