From e81d053f35cdef1f86e7228f67d022d14d9b140d Mon Sep 17 00:00:00 2001 From: Philip Rideout Date: Thu, 2 Jun 2022 15:04:25 -0700 Subject: [PATCH] gltfio: fix morphing for non-packed floats. --- RELEASE_NOTES.md | 2 + libs/geometry/include/geometry/Transcoder.h | 1 + libs/geometry/src/Transcoder.cpp | 14 ++++++ libs/gltfio/src/GltfEnums.h | 7 --- libs/gltfio/src/ResourceLoader.cpp | 51 ++++++++++++++++++++- 5 files changed, 67 insertions(+), 8 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index faed24a4fe..b8ad3e07c7 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -5,6 +5,8 @@ A new header is inserted each time a *tag* is created. ## main branch +- gltfio: fix morphing for un-packed accessors + ## v1.23.1 - gltfio: support skinning with bones that do not belong to any scene diff --git a/libs/geometry/include/geometry/Transcoder.h b/libs/geometry/include/geometry/Transcoder.h index 0c77c94170..805c2609cc 100644 --- a/libs/geometry/include/geometry/Transcoder.h +++ b/libs/geometry/include/geometry/Transcoder.h @@ -31,6 +31,7 @@ enum class ComponentType { SHORT, //!< If normalization is enabled, this maps from [-32767,32767] to [-1,+1] USHORT, //!< If normalization is enabled, this maps from [0,65535] to [0, +1] HALF, //!< 1 sign bit, 5 exponent bits, and 5 mantissa bits. + FLOAT, //!< Standard 32-bit float }; /** diff --git a/libs/geometry/src/Transcoder.cpp b/libs/geometry/src/Transcoder.cpp index ae3582f3e5..c3c427a1b8 100644 --- a/libs/geometry/src/Transcoder.cpp +++ b/libs/geometry/src/Transcoder.cpp @@ -188,6 +188,20 @@ size_t Transcoder::operator()(float* UTILS_RESTRICT target, void const* UTILS_RE } return required; } + case ComponentType::FLOAT: { + const uint32_t srcStride = + mConfig.inputStrideBytes ? mConfig.inputStrideBytes : (4 * comp); + uint8_t const* srcBytes = (uint8_t const*) source; + for (size_t i = 0; i < count; ++i, target += comp, srcBytes += srcStride) { + // This will never break alignment rules because the glTF spec stipulates that the + // stride must be a multiple of the component size. + float const* src = (float const*) srcBytes; + for (int n = 0; n < comp; ++n) { + target[n] = src[n]; + } + } + return required; + } } return 0; } diff --git a/libs/gltfio/src/GltfEnums.h b/libs/gltfio/src/GltfEnums.h index 39e71b7a0c..245240cfd2 100644 --- a/libs/gltfio/src/GltfEnums.h +++ b/libs/gltfio/src/GltfEnums.h @@ -263,11 +263,4 @@ inline bool getElementType(cgltf_type type, cgltf_component_type ctype, return false; } -inline bool requiresConversion(cgltf_type type, cgltf_component_type ctype) { - filament::VertexBuffer::AttributeType permitted; - filament::VertexBuffer::AttributeType actual; - bool supported = getElementType(type, ctype, &permitted, &actual); - return supported && permitted != actual; -} - #endif // GLTFIO_GLTFENUMS_H diff --git a/libs/gltfio/src/ResourceLoader.cpp b/libs/gltfio/src/ResourceLoader.cpp index da19320a0e..c43f69ff87 100644 --- a/libs/gltfio/src/ResourceLoader.cpp +++ b/libs/gltfio/src/ResourceLoader.cpp @@ -176,8 +176,9 @@ static ComponentType getComponentType(const cgltf_accessor* accessor) { case cgltf_component_type_r_8u: return ComponentType::UBYTE; case cgltf_component_type_r_16: return ComponentType::SHORT; case cgltf_component_type_r_16u: return ComponentType::USHORT; + case cgltf_component_type_r_32f: return ComponentType::FLOAT; + case cgltf_component_type_r_32u: default: - // This should be unreachable because other types do not require conversion. assert_invariant(false); return {}; } @@ -197,6 +198,34 @@ static void convertToFloats(float* dest, const cgltf_accessor* accessor) { transcode(dest, source, accessor->count); } +static bool requiresConversion(cgltf_type type, cgltf_component_type ctype) { + filament::VertexBuffer::AttributeType permitted; + filament::VertexBuffer::AttributeType actual; + bool supported = getElementType(type, ctype, &permitted, &actual); + return supported && permitted != actual; +} + +static bool requiresPacking(const cgltf_accessor* accessor) { + if (requiresConversion(accessor->type, accessor->component_type)) { + return true; + } + const size_t dim = cgltf_num_components(accessor->type); + switch (accessor->component_type) { + case cgltf_component_type_r_8: + case cgltf_component_type_r_8u: + return accessor->stride != dim; + case cgltf_component_type_r_16: + case cgltf_component_type_r_16u: + return accessor->stride != dim * 2; + case cgltf_component_type_r_32u: + case cgltf_component_type_r_32f: + return accessor->stride != dim * 4; + default: + assert_invariant(false); + return true; + } +} + static void decodeDracoMeshes(FFilamentAsset* asset) { DracoCache* dracoCache = &asset->mSourceAsset->dracoCache; @@ -492,7 +521,27 @@ bool ResourceLoader::loadResources(FFilamentAsset* asset, bool async) { slot.indexBuffer->setBuffer(engine, std::move(bd)); continue; } + + // If the buffer slot does not have an associated VertexBuffer or IndexBuffer, then this + // must be a morph target. assert(slot.morphTargetBuffer); + + if (requiresPacking(accessor)) { + const size_t dim = cgltf_num_components(accessor->type); + const size_t floatsSize = accessor->count * sizeof(float) * dim; + float* floatsData = (float*) malloc(floatsSize); + convertToFloats(floatsData, accessor); + if (accessor->type == cgltf_type_vec3) { + slot.morphTargetBuffer->setPositionsAt(engine, slot.bufferIndex, + (const float3*) floatsData, slot.morphTargetBuffer->getVertexCount()); + } else { + slot.morphTargetBuffer->setPositionsAt(engine, slot.bufferIndex, + (const float4*) data, slot.morphTargetBuffer->getVertexCount()); + } + free(floatsData); + continue; + } + if (accessor->type == cgltf_type_vec3) { slot.morphTargetBuffer->setPositionsAt(engine, slot.bufferIndex, (const float3*) data, slot.morphTargetBuffer->getVertexCount());