Use the Jim Arvo method for transforming AABB's.
This is from the 1990 Graphics Gems article.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user