Fix Transform::xform(Plane) functions
The Transform::xform and xform_inv are made safe for Planes when using non-uniform scaling. Optimization of calling sites to prevent loss of performance from the changes to xform(Plane).
This commit is contained in:
parent
3c85ef14c3
commit
dd0f54a368
2 changed files with 77 additions and 33 deletions
|
@ -75,16 +75,24 @@ public:
|
|||
bool operator!=(const Transform3D &p_transform) const;
|
||||
|
||||
_FORCE_INLINE_ Vector3 xform(const Vector3 &p_vector) const;
|
||||
_FORCE_INLINE_ Vector3 xform_inv(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;
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue