From fa108d6a6551ea00b0ef4af4dbc697d17f4cda23 Mon Sep 17 00:00:00 2001 From: Philip Rideout Date: Wed, 30 Oct 2019 11:33:03 -0700 Subject: [PATCH] Use the Jim Arvo method for transforming AABB's. This is from the 1990 Graphics Gems article. --- filament/include/filament/Box.h | 11 ++++++++++- filament/src/Box.cpp | 18 +++++++++++++++++- filament/test/filament_test.cpp | 27 +++++++++++++++++++++++++++ libs/gltfio/src/AssetLoader.cpp | 16 +++------------- libs/gltfio/src/ResourceLoader.cpp | 21 +++++---------------- 5 files changed, 62 insertions(+), 31 deletions(-) diff --git a/filament/include/filament/Box.h b/filament/include/filament/Box.h index 42c8d4fdbc..a352970337 100644 --- a/filament/include/filament/Box.h +++ b/filament/include/filament/Box.h @@ -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 diff --git a/filament/src/Box.cpp b/filament/src/Box.cpp index f3315b9b4d..44235f0e87 100644 --- a/filament/src/Box.cpp +++ b/filament/src/Box.cpp @@ -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 diff --git a/filament/test/filament_test.cpp b/filament/test/filament_test.cpp index 479a33a35f..1eea12db14 100644 --- a/filament/test/filament_test.cpp +++ b/filament/test/filament_test.cpp @@ -21,8 +21,11 @@ #include #include +#include #include +#include +#include #include #include #include @@ -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 { diff --git a/libs/gltfio/src/AssetLoader.cpp b/libs/gltfio/src/AssetLoader.cpp index bccb0eb24c..12820a1df9 100644 --- a/libs/gltfio/src/AssetLoader.cpp +++ b/libs/gltfio/src/AssetLoader.cpp @@ -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); diff --git a/libs/gltfio/src/ResourceLoader.cpp b/libs/gltfio/src/ResourceLoader.cpp index 763f1236e0..f6f7e374f0 100644 --- a/libs/gltfio/src/ResourceLoader.cpp +++ b/libs/gltfio/src/ResourceLoader.cpp @@ -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); } }