Use the Jim Arvo method for transforming AABB's.

This is from the 1990 Graphics Gems article.
This commit is contained in:
Philip Rideout
2019-10-30 11:33:03 -07:00
parent a82d93914e
commit fa108d6a65
5 changed files with 62 additions and 31 deletions

View File

@@ -172,16 +172,25 @@ struct Aabb {
};
/**
* Return the 8 corner vertices of the AABB
* Returns the 8 corner vertices of the AABB.
*/
Corners getCorners() const;
/**
* Returns whether the box contains a given point.
*
* @param p the point to test
* @return the maximum signed distance to the box. Negative if p is in the box
*/
float contains(math::float3 p) const noexcept;
/**
* Applies an affine transformation to the AABB.
*
* @param m the 4x4 transformation to apply
* @return the transformed box
*/
Aabb transform(const math::mat4f& m) const noexcept;
};
} // namespace filament

View File

@@ -42,7 +42,7 @@ Aabb::Corners Aabb::getCorners() const {
}};
}
float Aabb::contains(math::float3 p) const noexcept {
float Aabb::contains(float3 p) const noexcept {
float d = min.x - p.x;
d = std::max(d, min.y - p.y);
d = std::max(d, min.z - p.z);
@@ -52,4 +52,20 @@ float Aabb::contains(math::float3 p) const noexcept {
return d;
}
// Fast AABB transformation per Jim Arvo in Graphics Gems (1990).
Aabb Aabb::transform(const mat4f& mat) const noexcept {
const float3 translation = mat[3].xyz;
const mat3f upperLeft = mat.upperLeft();
Aabb result = {translation, translation};
for (size_t col = 0; col < upperLeft.NUM_COLS; ++col) {
for (size_t row = 0; row < upperLeft.NUM_ROWS; ++row) {
const float a = upperLeft[col][row] * min[col];
const float b = upperLeft[col][row] * max[col];
result.min[row] += a < b ? a : b;
result.max[row] += a < b ? b : a;
}
}
return result;
}
} // namespace filament

View File

@@ -21,8 +21,11 @@
#include <math/vec3.h>
#include <math/vec4.h>
#include <math/mat3.h>
#include <math/mat4.h>
#include <math/scalar.h>
#include <filament/Box.h>
#include <filament/Camera.h>
#include <filament/Color.h>
#include <filament/Frustum.h>
@@ -61,6 +64,30 @@ static bool vec3eq(float3 a, float3 b) {
almostEqualUlps(a.z, b.z, 1);
}
TEST(FilamentTest, AabbMath) {
constexpr Aabb aabb = {{4, 5, 6}, {12, 14, 11}};
const mat4f m(mat3f::rotation(F_PI_2, float3 {0, 0, 1}), float3 {-4, -5, -6});
const Aabb result = aabb.transform(m);
// Compare Arvo's method (above) with the naive method (below).
const float3 a = (m * float4(aabb.min.x, aabb.min.y, aabb.min.z, 1.0)).xyz;
const float3 b = (m * float4(aabb.min.x, aabb.min.y, aabb.max.z, 1.0)).xyz;
const float3 c = (m * float4(aabb.min.x, aabb.max.y, aabb.min.z, 1.0)).xyz;
const float3 d = (m * float4(aabb.min.x, aabb.max.y, aabb.max.z, 1.0)).xyz;
const float3 e = (m * float4(aabb.max.x, aabb.min.y, aabb.min.z, 1.0)).xyz;
const float3 f = (m * float4(aabb.max.x, aabb.min.y, aabb.max.z, 1.0)).xyz;
const float3 g = (m * float4(aabb.max.x, aabb.max.y, aabb.min.z, 1.0)).xyz;
const float3 h = (m * float4(aabb.max.x, aabb.max.y, aabb.max.z, 1.0)).xyz;
const Aabb expected {
.min = min(min(min(min(min(min(min(a, b), c), d), e), f), g), h),
.max = max(max(max(max(max(max(max(a, b), c), d), e), f), g), h),
};
EXPECT_PRED2(vec3eq, result.min, expected.min);
EXPECT_PRED2(vec3eq, result.max, expected.max);
}
TEST(FilamentTest, SkinningMath) {
struct Bone {

View File

@@ -375,21 +375,11 @@ void FAssetLoader::createRenderable(const cgltf_node* node, Entity entity) {
builder.morphing(true);
}
// Transform all eight corners of the bounding box and find the new AABB.
float3 a = (worldTransform * float4(aabb.min.x, aabb.min.y, aabb.min.z, 1.0)).xyz;
float3 b = (worldTransform * float4(aabb.min.x, aabb.min.y, aabb.max.z, 1.0)).xyz;
float3 c = (worldTransform * float4(aabb.min.x, aabb.max.y, aabb.min.z, 1.0)).xyz;
float3 d = (worldTransform * float4(aabb.min.x, aabb.max.y, aabb.max.z, 1.0)).xyz;
float3 e = (worldTransform * float4(aabb.max.x, aabb.min.y, aabb.min.z, 1.0)).xyz;
float3 f = (worldTransform * float4(aabb.max.x, aabb.min.y, aabb.max.z, 1.0)).xyz;
float3 g = (worldTransform * float4(aabb.max.x, aabb.max.y, aabb.min.z, 1.0)).xyz;
float3 h = (worldTransform * float4(aabb.max.x, aabb.max.y, aabb.max.z, 1.0)).xyz;
float3 minpt = min(min(min(min(min(min(min(a, b), c), d), e), f), g), h);
float3 maxpt = max(max(max(max(max(max(max(a, b), c), d), e), f), g), h);
const Aabb transformed = aabb.transform(worldTransform);
// Expand the world-space bounding box.
mResult->mBoundingBox.min = min(mResult->mBoundingBox.min, minpt);
mResult->mBoundingBox.max = max(mResult->mBoundingBox.max, maxpt);
mResult->mBoundingBox.min = min(mResult->mBoundingBox.min, transformed.min);
mResult->mBoundingBox.max = max(mResult->mBoundingBox.max, transformed.max);
if (node->skin) {
builder.skinning(node->skin->joints_count);

View File

@@ -642,23 +642,12 @@ void ResourceLoader::updateBoundingBoxes(details::FFilamentAsset* asset) const {
auto renderable = rm.getInstance(iter.second);
rm.setAxisAlignedBoundingBox(renderable, Box().set(aabb.min, aabb.max));
// Transform all eight corners of the bounding box to world space and find the new AABB.
// This is used for the asset-level bounding box.
// Transform this bounding box, then update the asset-level bounding box.
auto transformable = tm.getInstance(iter.second);
mat4f worldTransform = tm.getWorldTransform(transformable);
float3 a = (worldTransform * float4(aabb.min.x, aabb.min.y, aabb.min.z, 1.0)).xyz;
float3 b = (worldTransform * float4(aabb.min.x, aabb.min.y, aabb.max.z, 1.0)).xyz;
float3 c = (worldTransform * float4(aabb.min.x, aabb.max.y, aabb.min.z, 1.0)).xyz;
float3 d = (worldTransform * float4(aabb.min.x, aabb.max.y, aabb.max.z, 1.0)).xyz;
float3 e = (worldTransform * float4(aabb.max.x, aabb.min.y, aabb.min.z, 1.0)).xyz;
float3 f = (worldTransform * float4(aabb.max.x, aabb.min.y, aabb.max.z, 1.0)).xyz;
float3 g = (worldTransform * float4(aabb.max.x, aabb.max.y, aabb.min.z, 1.0)).xyz;
float3 h = (worldTransform * float4(aabb.max.x, aabb.max.y, aabb.max.z, 1.0)).xyz;
float3 minpt = min(min(min(min(min(min(min(a, b), c), d), e), f), g), h);
float3 maxpt = max(max(max(max(max(max(max(a, b), c), d), e), f), g), h);
assetBounds.min = min(assetBounds.min, minpt);
assetBounds.max = max(assetBounds.max, maxpt);
const mat4f worldTransform = tm.getWorldTransform(transformable);
const Aabb transformed = aabb.transform(worldTransform);
assetBounds.min = min(assetBounds.min, transformed.min);
assetBounds.max = max(assetBounds.max, transformed.max);
}
}