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

Fix Transform::xform(Plane) functions, add Transform unit tests #50637

Merged
merged 1 commit into from
Aug 7, 2021
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
83 changes: 58 additions & 25 deletions core/math/transform.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,16 +76,24 @@ class Transform {
bool operator!=(const Transform &p_transform) const;

_FORCE_INLINE_ Vector3 xform(const Vector3 &p_vector) const;
_FORCE_INLINE_ AABB xform(const AABB &p_aabb) const;
_FORCE_INLINE_ PoolVector<Vector3> xform(const PoolVector<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_ PoolVector<Vector3> xform_inv(const PoolVector<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_ PoolVector<Vector3> xform(const PoolVector<Vector3> &p_array) const;
_FORCE_INLINE_ PoolVector<Vector3> xform_inv(const PoolVector<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 Transform &p_inverse, const Basis &p_basis_transpose);

void operator*=(const Transform &p_transform);
Transform operator*(const Transform &p_transform) const;
Expand Down Expand Up @@ -118,6 +126,7 @@ _FORCE_INLINE_ Vector3 Transform::xform(const Vector3 &p_vector) const {
basis[1].dot(p_vector) + origin.y,
basis[2].dot(p_vector) + origin.z);
}

_FORCE_INLINE_ Vector3 Transform::xform_inv(const Vector3 &p_vector) const {
Vector3 v = p_vector - origin;

Expand All @@ -127,29 +136,20 @@ _FORCE_INLINE_ Vector3 Transform::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 Transform::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 Transform::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);
_FORCE_INLINE_ Plane Transform::xform_inv(const Plane &p_plane) const {
Transform inv = affine_inverse();
Basis basis_transpose = basis.transposed();
return xform_inv_fast(p_plane, inv, basis_transpose);
}

_FORCE_INLINE_ AABB Transform::xform(const AABB &p_aabb) const {
Expand Down Expand Up @@ -227,4 +227,37 @@ PoolVector<Vector3> Transform::xform_inv(const PoolVector<Vector3> &p_array) con
return array;
}

_FORCE_INLINE_ Plane Transform::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 Transform::xform_inv_fast(const Plane &p_plane, const Transform &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
6 changes: 6 additions & 0 deletions main/tests/test_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,15 @@
#include "test_render.h"
#include "test_shader_lang.h"
#include "test_string.h"
#include "test_transform.h"
#include "test_xml_parser.h"

const char **tests_get_names() {
static const char *test_names[] = {
"string",
"math",
"basis",
"transform",
"physics",
"physics_2d",
"render",
Expand Down Expand Up @@ -86,6 +88,10 @@ MainLoop *test_main(String p_test, const List<String> &p_args) {
return TestBasis::test();
}

if (p_test == "transform") {
return TestTransform::test();
}

if (p_test == "physics") {
return TestPhysics::test();
}
Expand Down
Loading