gltfio: fix up and enhance AABB functionality.
gltfio computes two types of bounding boxes: one for renderables (used for frustum culling) and one for the overall asset (used for positioning the camera or asset). Both of these are based on the min+max attributes in the glTF file, but the asset-level box was incorrect because only two corners of the transformed AABB were considered. This CL also adds optional computation of bounding boxes that crawls through the vertex positions. This is useful when diagnosing potential issues with the asset's min+max info. These enhancements are motivated by a culling issue seen with the voxel Cathedral on sketchfab.
This commit is contained in:
@@ -34,6 +34,7 @@ struct ResourceConfiguration {
|
||||
class filament::Engine* engine;
|
||||
utils::Path basePath;
|
||||
bool normalizeSkinningWeights;
|
||||
bool recomputeBoundingBoxes;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -60,7 +61,8 @@ public:
|
||||
private:
|
||||
bool createTextures(details::FFilamentAsset* asset) const;
|
||||
void computeTangents(details::FFilamentAsset* asset) const;
|
||||
void normalizeWeights(details::FFilamentAsset* asset) const;
|
||||
void normalizeSkinningWeights(details::FFilamentAsset* asset) const;
|
||||
void updateBoundingBoxes(details::FFilamentAsset* asset) const;
|
||||
details::AssetPool* mPool;
|
||||
const ResourceConfiguration mConfig;
|
||||
};
|
||||
|
||||
@@ -73,11 +73,12 @@ using MeshCache = tsl::robin_map<const cgltf_mesh*, std::vector<Primitive>>;
|
||||
// Filament materials are cached by the MaterialGenerator, but material instances are cached here.
|
||||
using MatInstanceCache = tsl::robin_map<intptr_t, MaterialInstance*>;
|
||||
|
||||
// Filament automatically infers the size of driver-level vertex buffers from the attribute data
|
||||
// (stride, count, offset) and clients are expected to avoid uploading data blobs that exceed this
|
||||
// size. Since this information doesn't exist in the glTF we need to compute it manually. This is a
|
||||
// bit of a cheat, cgltf_calc_size is private but its implementation file is available in this cpp
|
||||
// file.
|
||||
// Sometimes a glTF bufferview includes unused data at the end (e.g. in skinning.gltf) so we need to
|
||||
// compute the correct size of the vertex buffer. Filament automatically infers the size of
|
||||
// driver-level vertex buffers from the attribute data (stride, count, offset) and clients are
|
||||
// expected to avoid uploading data blobs that exceed this size. Since this information doesn't
|
||||
// exist in the glTF we need to compute it manually. This is a bit of a cheat, cgltf_calc_size is
|
||||
// private but its implementation file is available in this cpp file.
|
||||
static uint32_t computeBindingSize(const cgltf_accessor* accessor){
|
||||
cgltf_size element_size = cgltf_calc_size(accessor->type, accessor->component_type);
|
||||
return uint32_t(accessor->stride * (accessor->count - 1) + element_size);
|
||||
@@ -319,10 +320,19 @@ void FAssetLoader::createRenderable(const cgltf_node* node, Entity entity) {
|
||||
builder.geometry(index, primType, outputPrim->vertices, outputPrim->indices);
|
||||
}
|
||||
|
||||
// Expand the world-space bounding box.
|
||||
float3 minpt = (worldTransform * float4(aabb.min, 1.0)).xyz;
|
||||
float3 maxpt = (worldTransform * float4(aabb.max, 1.0)).xyz;
|
||||
// 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);
|
||||
|
||||
// Expand the world-space bounding box.
|
||||
mResult->mBoundingBox.min = min(mResult->mBoundingBox.min, minpt);
|
||||
mResult->mBoundingBox.max = max(mResult->mBoundingBox.max, maxpt);
|
||||
|
||||
@@ -331,7 +341,7 @@ void FAssetLoader::createRenderable(const cgltf_node* node, Entity entity) {
|
||||
}
|
||||
|
||||
builder
|
||||
.boundingBox({aabb.min, aabb.max})
|
||||
.boundingBox(Box().set(aabb.min, aabb.max))
|
||||
.culling(true)
|
||||
.castShadows(true)
|
||||
.receiveShadows(true)
|
||||
|
||||
@@ -138,7 +138,11 @@ bool ResourceLoader::loadResources(FilamentAsset* asset) {
|
||||
// feature, and instead simply require correct models. See also:
|
||||
// https://github.com/KhronosGroup/glTF-Sample-Models/issues/215
|
||||
if (mConfig.normalizeSkinningWeights) {
|
||||
normalizeWeights(fasset);
|
||||
normalizeSkinningWeights(fasset);
|
||||
}
|
||||
|
||||
if (mConfig.recomputeBoundingBoxes) {
|
||||
updateBoundingBoxes(fasset);
|
||||
}
|
||||
|
||||
// Upload data to the GPU.
|
||||
@@ -377,7 +381,7 @@ void ResourceLoader::computeTangents(FFilamentAsset* asset) const {
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceLoader::normalizeWeights(details::FFilamentAsset* asset) const {
|
||||
void ResourceLoader::normalizeSkinningWeights(details::FFilamentAsset* asset) const {
|
||||
auto normalize = [](cgltf_accessor* data) {
|
||||
if (data->type != cgltf_type_vec4 || data->component_type != cgltf_component_type_r_32f) {
|
||||
slog.w << "Cannot normalize weights, unsupported attribute type." << io::endl;
|
||||
@@ -409,4 +413,62 @@ void ResourceLoader::normalizeWeights(details::FFilamentAsset* asset) const {
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceLoader::updateBoundingBoxes(details::FFilamentAsset* asset) const {
|
||||
auto& rm = mConfig.engine->getRenderableManager();
|
||||
auto& tm = mConfig.engine->getTransformManager();
|
||||
|
||||
auto computeBoundingBox = [&](const cgltf_primitive& prim) {
|
||||
Aabb aabb;
|
||||
for (cgltf_size slot = 0; slot < prim.attributes_count; slot++) {
|
||||
const cgltf_attribute& attr = prim.attributes[slot];
|
||||
if (attr.type == cgltf_attribute_type_position) {
|
||||
const cgltf_accessor* accessor = attr.data;
|
||||
float3 pt;
|
||||
for (cgltf_size i = 0, n = accessor->count; i < n; ++i) {
|
||||
cgltf_accessor_read_float(accessor, i, &pt.x, 3);
|
||||
aabb.min = min(aabb.min, pt);
|
||||
aabb.max = max(aabb.max, pt);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return aabb;
|
||||
};
|
||||
|
||||
Aabb assetBounds;
|
||||
for (auto iter : asset->mNodeMap) {
|
||||
const cgltf_mesh* mesh = iter.first->mesh;
|
||||
if (mesh) {
|
||||
// Find the object-space bounds for the renderable by unioning the bounds of each prim.
|
||||
Aabb aabb;
|
||||
for (cgltf_size index = 0, nprims = mesh->primitives_count; index < nprims; ++index) {
|
||||
Aabb primBounds = computeBoundingBox(mesh->primitives[index]);
|
||||
aabb.min = min(aabb.min, primBounds.min);
|
||||
aabb.max = max(aabb.max, primBounds.max);
|
||||
}
|
||||
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.
|
||||
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);
|
||||
}
|
||||
}
|
||||
asset->mBoundingBox = assetBounds;
|
||||
}
|
||||
|
||||
} // namespace gltfio
|
||||
|
||||
Reference in New Issue
Block a user