From 82fcb656d9a5270103231cee441cc0e5f23b4c02 Mon Sep 17 00:00:00 2001 From: Philip Rideout Date: Mon, 5 Apr 2021 13:00:39 -0700 Subject: [PATCH] gltfio: Use BufferObject API, simplify MorphHelper. (#3737) * gltfio: Use BufferObject API, simplify MorphHelper. * Repair Android build. --- android/filament-android/libfilament-jni.map | 1 + libs/gltfio/src/Animator.cpp | 21 -- libs/gltfio/src/AssetLoader.cpp | 7 +- libs/gltfio/src/FFilamentAsset.h | 1 + libs/gltfio/src/FilamentAsset.cpp | 3 + libs/gltfio/src/MorphHelper.cpp | 375 ++++++------------- libs/gltfio/src/MorphHelper.h | 38 +- libs/gltfio/src/ResourceLoader.cpp | 23 +- 8 files changed, 157 insertions(+), 312 deletions(-) diff --git a/android/filament-android/libfilament-jni.map b/android/filament-android/libfilament-jni.map index b87c1001cb..fa0f0f920d 100644 --- a/android/filament-android/libfilament-jni.map +++ b/android/filament-android/libfilament-jni.map @@ -2,6 +2,7 @@ LIBFILAMENT { global: Java_com_google_android_filament_*; JNI*; + *filament*BufferObject*; *filament*Camera*; *filament*Color*; *filament*Exposure*; diff --git a/libs/gltfio/src/Animator.cpp b/libs/gltfio/src/Animator.cpp index d813b4540e..4879a637fc 100644 --- a/libs/gltfio/src/Animator.cpp +++ b/libs/gltfio/src/Animator.cpp @@ -78,7 +78,6 @@ struct AnimatorImpl { vector weights; MorphHelper* morpher; void addChannels(const NodeMap& nodeMap, const cgltf_animation& srcAnim, Animation& dst); - void prepareMorphing(const Channel& channel); void applyAnimation(const Channel& channel, float t, size_t prevIndex, size_t nextIndex); }; @@ -371,29 +370,9 @@ void AnimatorImpl::addChannels(const NodeMap& nodeMap, const cgltf_animation& sr dstChannel.targetEntity = targetEntity; setTransformType(srcChannel, dstChannel); dst.channels.push_back(dstChannel); - if (dstChannel.transformType == Channel::WEIGHTS) { - prepareMorphing(dstChannel); - } } } -// This function primes the cache in MorphHelper, which allows users to free their source data -// after obtaining the Animator interface. We do this by sampling exactly half-way between key -// frames. In practice this is sufficient, and MorphHelper degrades gracefully for corner cases. -void AnimatorImpl::prepareMorphing(const Channel& channel) { - const Sampler* sampler = channel.sourceData; - const TimeValues& times = sampler->times; - auto iter = times.begin(); - morpher->disableWrites(true); - for (size_t timeIndex = 0; timeIndex < times.size(); ++timeIndex, ++iter) { - auto next = iter; - ++next; - next = next == times.end() ? iter : next; - applyAnimation(channel, 0.5, iter->second, next->second); - } - morpher->disableWrites(false); -} - void AnimatorImpl::applyAnimation(const Channel& channel, float t, size_t prevIndex, size_t nextIndex) { const Sampler* sampler = channel.sourceData; diff --git a/libs/gltfio/src/AssetLoader.cpp b/libs/gltfio/src/AssetLoader.cpp index d051f41f20..0f5f10a72c 100644 --- a/libs/gltfio/src/AssetLoader.cpp +++ b/libs/gltfio/src/AssetLoader.cpp @@ -22,6 +22,7 @@ #include "GltfEnums.h" #include +#include #include #include #include @@ -560,6 +561,7 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive* inPrim, Primitive* out mResult->mIndexBuffers.push_back(indices); VertexBuffer::Builder vbb; + vbb.enableBufferObjects(); bool hasUv0 = false, hasUv1 = false, hasVertexColor = false, hasNormals = false; uint32_t vertexCount = 0; @@ -777,10 +779,13 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive* inPrim, Primitive* out if (needsDummyData) { uint32_t size = sizeof(ubyte4) * vertexCount; + BufferObject* bufferObject = BufferObject::Builder().size(size).build(*mEngine); + mResult->mBufferObjects.push_back(bufferObject); uint32_t* dummyData = (uint32_t*) malloc(size); memset(dummyData, 0xff, size); VertexBuffer::BufferDescriptor bd(dummyData, size, FREE_CALLBACK); - vertices->setBufferAt(*mEngine, slot, std::move(bd)); + bufferObject->setBuffer(*mEngine, std::move(bd)); + vertices->setBufferObjectAt(*mEngine, slot, bufferObject); } return true; diff --git a/libs/gltfio/src/FFilamentAsset.h b/libs/gltfio/src/FFilamentAsset.h index 4d7dca0879..25176d6739 100644 --- a/libs/gltfio/src/FFilamentAsset.h +++ b/libs/gltfio/src/FFilamentAsset.h @@ -229,6 +229,7 @@ struct FFilamentAsset : public FilamentAsset { std::vector mCameraEntities; std::vector mMaterialInstances; std::vector mVertexBuffers; + std::vector mBufferObjects; std::vector mIndexBuffers; std::vector mTextures; filament::Aabb mBoundingBox; diff --git a/libs/gltfio/src/FilamentAsset.cpp b/libs/gltfio/src/FilamentAsset.cpp index 696445e2f7..5e3200b254 100644 --- a/libs/gltfio/src/FilamentAsset.cpp +++ b/libs/gltfio/src/FilamentAsset.cpp @@ -61,6 +61,9 @@ FFilamentAsset::~FFilamentAsset() { for (auto vb : mVertexBuffers) { mEngine->destroy(vb); } + for (auto bo : mBufferObjects) { + mEngine->destroy(bo); + } for (auto ib : mIndexBuffers) { mEngine->destroy(ib); } diff --git a/libs/gltfio/src/MorphHelper.cpp b/libs/gltfio/src/MorphHelper.cpp index fe6d6aef4d..f976b3a6d5 100644 --- a/libs/gltfio/src/MorphHelper.cpp +++ b/libs/gltfio/src/MorphHelper.cpp @@ -16,6 +16,7 @@ #include "MorphHelper.h" +#include #include #include @@ -33,6 +34,8 @@ namespace gltfio { uint32_t computeBindingSize(const cgltf_accessor* accessor); uint32_t computeBindingOffset(const cgltf_accessor* accessor); +static const auto FREE_CALLBACK = [](void* mem, size_t, void*) { free(mem); }; + // Returns true if a is a subset of b. static bool isSubsetOf(ubyte4 a, ubyte4 b) { if (a.x != kUnused && a.x != b.x && a.x != b.y && a.x != b.z && a.x != b.w) return false; @@ -42,40 +45,52 @@ static bool isSubsetOf(ubyte4 a, ubyte4 b) { return true; } +static int indexOf(int a, ubyte4 b) { + if (a == b.x) return 0; + if (a == b.y) return 1; + if (a == b.z) return 2; + if (a == b.w) return 3; + return -1; +} + MorphHelper::MorphHelper(FFilamentAsset* asset, FFilamentInstance* inst) : mAsset(asset), mInstance(inst) { NodeMap& sourceNodes = asset->isInstanced() ? asset->mInstances[0]->nodeMap : asset->mNodeMap; for (auto pair : sourceNodes) { - mMorphTable[pair.second].sourceNode = pair.first; + cgltf_node const* node = pair.first; + cgltf_mesh const* mesh = node->mesh; + if (mesh) { + cgltf_primitive const* prims = mesh->primitives; + for (cgltf_size pi = 0, count = mesh->primitives_count; pi < count; ++pi) { + addPrimitive(mesh, pi, &mMorphTable[pair.second]); + } + } } } MorphHelper::~MorphHelper() { auto engine = mAsset->mEngine; for (auto& entry : mMorphTable) { - for (auto& perm : entry.second.permutations) { - if (perm.owner) { - for (auto& prim : perm.primitives) { - engine->destroy(prim.vertices); - } + for (auto& prim : entry.second.primitives) { + for (auto& target : prim.targets) { + engine->destroy(target.bufferObject); } } } } void MorphHelper::applyWeights(Entity entity, float const* weights, size_t count) noexcept { - auto renderableManager = &mAsset->mEngine->getRenderableManager(); + auto& engine = *mAsset->mEngine; + auto renderableManager = &engine.getRenderableManager(); auto renderable = renderableManager->getInstance(entity); // If there are 4 or fewer targets, we can simply re-use the original VertexBuffer. if (count <= 4) { - if (!mWritesDisabled) { - float4 vec{}; - for (size_t i = 0; i < count; i++) { - vec[i] = weights[i]; - } - renderableManager->setMorphWeights(renderable, vec); + float4 vec{}; + for (size_t i = 0; i < count; i++) { + vec[i] = weights[i]; } + renderableManager->setMorphWeights(renderable, vec); return; } @@ -102,216 +117,38 @@ void MorphHelper::applyWeights(Entity entity, float const* weights, size_t count } } - // Check if we already encountered this set. - Permutation* permutation = nullptr; - auto iter = mMorphTable.find(entity); - if (iter == mMorphTable.end()) return; - for (auto& perm : iter.value().permutations) { - if (isSubsetOf(primaryIndices, perm.primaryIndices)) { - permutation = &perm; - break; + // Swap out the buffer objects for the primary indices. + for (const auto& prim : mMorphTable[entity].primitives) { + for (const auto& target : prim.targets) { + const int index = indexOf(target.morphTargetIndex, primaryIndices); + if (index > -1) { + VertexBuffer* vb = prim.vertexBuffer; + vb->setBufferObjectAt(engine, prim.baseSlot + index, target.bufferObject); + // Do not break out early because there could be more than one target entry for this + // particular target index (e.g. positions + tangents). + } } } - // If we haven't found an existing set, then create a new partition. This is a potentially - // slow operation but it should only occur during the priming phase. - if (permutation == nullptr) { - if (!mAsset->getSourceAsset()) return; - addPermutation(entity, primaryIndices); - permutation = &mMorphTable[entity].permutations.back(); - } - - // Leave early if we're priming the cache. - if (mWritesDisabled) return; - - // Swap out the vertex buffer on all affected renderables. Often this will be a no-op, - // but this is a fairly efficient operation in Filament. - for (size_t primIndex = 0; primIndex < permutation->primitives.size(); ++primIndex) { - const GltfPrimitive& prim = permutation->primitives[primIndex]; - renderableManager->setGeometryAt(renderable, primIndex, prim.type, prim.vertices, - prim.indices, 0, prim.indices->getIndexCount()); - } - // Finally, set the 4-tuple uniform for the weight values by derefing the primary indices. // Note that we first create a "safe set" by replacing the unused sentinel with zero. - ubyte4 safe = permutation->primaryIndices; + float4 highest; for (int i = 0; i < 4; i++) { - if (safe[i] == kUnused) { - safe[i] = 0; - } + highest[i] = (primaryIndices[i] == kUnused) ? 0 : weights[primaryIndices[i]]; } - float4 highest(weights[safe[0]], weights[safe[1]], weights[safe[2]], weights[safe[3]]); renderableManager->setMorphWeights(renderable, highest); } -void MorphHelper::addPermutation(Entity entity, ubyte4 primaryIndices) { - Permutation result = { primaryIndices }; - const cgltf_node* node = mMorphTable[entity].sourceNode; - const cgltf_mesh* mesh = node->mesh; - const cgltf_primitive* prims = mesh->primitives; - const cgltf_size prim_count = mesh->primitives_count; +void MorphHelper::addPrimitive(cgltf_mesh const* mesh, int primitiveIndex, TableEntry* entry) { + auto& engine = *mAsset->mEngine; + const cgltf_primitive& prim = mesh->primitives[primitiveIndex]; + VertexBuffer* vertexBuffer = mAsset->mMeshCache.at(mesh)[primitiveIndex].vertices; - // If the original VertexBuffer can be used, just use that instead of creating a new one. - // In such a case, the MorphHelper is not the owner of the vertex buffer. - const ubyte4 kOriginalPrimaryIndices(0, 1, 2, 3); - result.owner = !isSubsetOf(primaryIndices, kOriginalPrimaryIndices); - if (!result.owner) { - result.primaryIndices = kOriginalPrimaryIndices; - } + entry->primitives.push_back({ vertexBuffer, determineBaseSlot(prim) }); + std::vector& targets = entry->primitives.back().targets; - for (cgltf_size pi = 0; pi < prim_count; ++pi) { - const cgltf_primitive& prim = prims[pi]; - RenderableManager::PrimitiveType prim_type; - getPrimitiveType(prim.type, &prim_type); - const auto& gltfioPrim = mAsset->mMeshCache.at(mesh)[pi]; - VertexBuffer* const vertices = result.owner ? - createVertexBuffer(prim, gltfioPrim.uvmap, primaryIndices) : - gltfioPrim.vertices; - result.primitives.emplace_back(GltfPrimitive { - .vertices = vertices, - .indices = gltfioPrim.indices, - .type = prim_type, - }); - } - mMorphTable[entity].permutations.emplace_back(std::move(result)); -} - -// Creates a VertexBuffer for a given permutation of "primary indices" (i.e. set of 4 targets). In -// some ways this mimics AssetLoader::createPrimitive() but it is simpler and less efficient because -// it immediately clones and uploads buffer data. -// -// TODO: This strategy is inefficient because it results in duplication of data. For example, an -// unmorphed UV0 attribute will have duplicated uploads and duplicated GPU data. We will soon be -// adding a new API to Filament to fix this. -VertexBuffer* MorphHelper::createVertexBuffer(const cgltf_primitive& prim, const UvMap& uvmap, - ubyte4 primaryIndices) { - - // Determine the number of vertices by looking at the first usable attribute. - const uint32_t vertexCount = [&prim]() { - uint32_t count = 0; - for (cgltf_size aindex = 0; aindex < prim.attributes_count; aindex++) { - const cgltf_attribute& attribute = prim.attributes[aindex]; - const cgltf_accessor* accessor = attribute.data; - if (accessor) { - count = accessor->count; - break; - } - } - return count; - }(); - - // This creates a copy because we don't know when the user will free the cgltf source data. - // For non-morphed vertex buffers, we use a sharing mechanism to prevent copies, but here - // we just want to keep it as simple as possible. - auto createBufferDescriptor = [](const cgltf_accessor* accessor) { - auto bufferData = (const uint8_t*) accessor->buffer_view->buffer->data; - const uint8_t* data = computeBindingOffset(accessor) + bufferData; - const uint32_t size = computeBindingSize(accessor); - uint8_t* clone = (uint8_t*) malloc(size); - memcpy(clone, data, size); - return VertexBuffer::BufferDescriptor(clone, size, [](void* mem, size_t, void*) { - free(mem); - }); - }; - - // This is used to populate unused attributes (e.g. UV1 or COLOR) in ubershader mode. - // It mimics the dummy buffer in AssetLoader created for non-morphing renderables. - auto createDummyBuffer = [vertexCount]() { - uint32_t size = sizeof(ubyte4) * vertexCount; - uint32_t* dummyData = (uint32_t*) malloc(size); - memset(dummyData, 0xff, size); - return VertexBuffer::BufferDescriptor(dummyData, size, [](void* mem, size_t, void*) { - free(mem); - }); - }; - - // TODO: The tangents computation should be invoked by the job system, similar to what we - // already do in the non-morphing codepath. - auto createTangentsBuffer = [&prim](int morphTarget) { - TangentsJob::Params params = {{ &prim, morphTarget }}; - TangentsJob::run(¶ms); - uint32_t size = sizeof(short4) * params.out.vertexCount; - short4* quats = params.out.results; - return VertexBuffer::BufferDescriptor(quats, size, [](void* mem, size_t, void*) { - free(mem); - }); - }; - - // TODO: this magic number should be exposed by a static VertexBuffer method or constant. - static constexpr int kMaxBufferCount = 16; - VertexBuffer::BufferDescriptor buffers[kMaxBufferCount] = {}; - - VertexBuffer::Builder vbb; - vbb.vertexCount(vertexCount); - - bool hasUv0 = false, hasUv1 = false, hasVertexColor = false, hasNormals = false; - int slot = 0; - for (cgltf_size aindex = 0; aindex < prim.attributes_count; aindex++) { - const cgltf_attribute& attribute = prim.attributes[aindex]; - const int index = attribute.index; - const cgltf_attribute_type atype = attribute.type; - const cgltf_accessor* accessor = attribute.data; - if (atype == cgltf_attribute_type_tangent) { - continue; - } - if (atype == cgltf_attribute_type_normal) { - vbb.attribute(VertexAttribute::TANGENTS, slot, VertexBuffer::AttributeType::SHORT4); - vbb.normalized(VertexAttribute::TANGENTS); - buffers[slot++] = createTangentsBuffer(TangentsJob::kMorphTargetUnused); - hasNormals = true; - continue; - } - if (atype == cgltf_attribute_type_color) { - hasVertexColor = true; - } - VertexAttribute semantic; - getVertexAttrType(atype, &semantic); - if (atype == cgltf_attribute_type_texcoord) { - if (index >= UvMapSize) { - continue; - } - UvSet uvset = uvmap[index]; - switch (uvset) { - case UV0: - semantic = VertexAttribute::UV0; - hasUv0 = true; - break; - case UV1: - semantic = VertexAttribute::UV1; - hasUv1 = true; - break; - case UNUSED: - if (!hasUv0 && getNumUvSets(uvmap) == 0) { - semantic = VertexAttribute::UV0; - hasUv0 = true; - break; - } - continue; - } - } - VertexBuffer::AttributeType fatype; - getElementType(accessor->type, accessor->component_type, &fatype); - vbb.attribute(semantic, slot, fatype, 0, accessor->stride); - vbb.normalized(semantic, accessor->normalized); - buffers[slot++] = createBufferDescriptor(accessor); - } - - // If the model is lit but does not have normals, we'll need to generate flat normals. - if (prim.material && !prim.material->unlit && !hasNormals) { - vbb.attribute(VertexAttribute::TANGENTS, slot, VertexBuffer::AttributeType::SHORT4); - vbb.normalized(VertexAttribute::TANGENTS); - buffers[slot++] = createTangentsBuffer(TangentsJob::kMorphTargetUnused); - cgltf_attribute_type atype = cgltf_attribute_type_normal; - } - - constexpr int baseTangentsAttr = (int) VertexAttribute::MORPH_TANGENTS_0; - constexpr int basePositionAttr = (int) VertexAttribute::MORPH_POSITION_0; - - for (int targetCount = 0; targetCount < 4; ++targetCount) { - cgltf_size targetIndex = primaryIndices[targetCount]; - if (targetIndex == kUnused) { - break; - } + const cgltf_accessor* previous = nullptr; + for (int targetIndex = 0; targetIndex < prim.targets_count; targetIndex++) { const cgltf_morph_target& morphTarget = prim.targets[targetIndex]; for (cgltf_size aindex = 0; aindex < morphTarget.attributes_count; aindex++) { const cgltf_attribute& attribute = morphTarget.attributes[aindex]; @@ -321,58 +158,78 @@ VertexBuffer* MorphHelper::createVertexBuffer(const cgltf_primitive& prim, const continue; } if (atype == cgltf_attribute_type_normal) { - VertexAttribute attr = (VertexAttribute) (baseTangentsAttr + targetCount); - vbb.attribute(attr, slot, VertexBuffer::AttributeType::SHORT4); - vbb.normalized(attr); - buffers[slot++] = createTangentsBuffer(targetIndex); + + // TODO: use JobSystem for this, like what we do for non-morph tangents. + TangentsJob job; + TangentsJob::Params params = { .in = { &prim, targetIndex } }; + TangentsJob::run(¶ms); + + if (params.out.results) { + const size_t size = params.out.vertexCount * sizeof(short4); + BufferObject* bufferObject = BufferObject::Builder().size(size).build(engine); + VertexBuffer::BufferDescriptor bd(params.out.results, size, FREE_CALLBACK); + bufferObject->setBuffer(engine, std::move(bd)); + params.out.results = nullptr; + targets.push_back({bufferObject, targetIndex, atype}); + } continue; } - VertexBuffer::AttributeType fatype; - getElementType(accessor->type, accessor->component_type, &fatype); - VertexAttribute attr = (VertexAttribute) (basePositionAttr + targetCount); - vbb.attribute(attr, slot, fatype, 0, accessor->stride); - vbb.normalized(attr, accessor->normalized); - buffers[slot++] = createBufferDescriptor(accessor); + if (atype == cgltf_attribute_type_position) { + // All position attributes must have the same data type. + assert(!previous || previous->component_type == accessor->component_type); + assert(!previous || previous->type == accessor->type); + previous = accessor; + + // This should always be non-null, but don't crash if the glTF is malformed. + if (accessor->buffer_view) { + auto bufferData = (const uint8_t*) accessor->buffer_view->buffer->data; + assert(bufferData); + const uint8_t* data = computeBindingOffset(accessor) + bufferData; + const uint32_t size = computeBindingSize(accessor); + + // This creates a copy because we don't know when the user will free the cgltf + // source data. For non-morphed vertex buffers, we use a sharing mechanism to + // prevent copies, but here we just want to keep it as simple as possible. + uint8_t* clone = (uint8_t*) malloc(size); + memcpy(clone, data, size); + + BufferObject* bufferObject = BufferObject::Builder().size(size).build(engine); + VertexBuffer::BufferDescriptor bd(clone, size, FREE_CALLBACK); + bufferObject->setBuffer(engine, std::move(bd)); + targets.push_back({bufferObject, targetIndex, atype}); + } + } } } - - // Next determine if we need to create a dummy buffer for tex coords or vertex color. If so, - // create a single dummy buffer and share it. This is inefficient because it assumes the worst - // case (i.e. ubershader mode) and assigns the dummy buffer to every unused attribute. We - // should instead remember which attributes are actually required by MaterialProvider. - - bool needsDummyData = false; - if (!hasUv0) { - needsDummyData = true; - vbb.attribute(VertexAttribute::UV0, slot, VertexBuffer::AttributeType::USHORT2); - vbb.normalized(VertexAttribute::UV0); - } - if (!hasUv1) { - needsDummyData = true; - vbb.attribute(VertexAttribute::UV1, slot, VertexBuffer::AttributeType::USHORT2); - vbb.normalized(VertexAttribute::UV1); - } - if (!hasVertexColor) { - needsDummyData = true; - vbb.attribute(VertexAttribute::COLOR, slot, VertexBuffer::AttributeType::UBYTE4); - vbb.normalized(VertexAttribute::COLOR); - } - if (needsDummyData) { - buffers[slot++] = createDummyBuffer(); - } - - const int bufferCount = slot; - - vbb.bufferCount(bufferCount); - VertexBuffer* vertices = vbb.build(*mAsset->mEngine); - - for (int bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex) { - if (buffers[bufferIndex].buffer != nullptr) { - vertices->setBufferAt(*mAsset->mEngine, bufferIndex, std::move(buffers[bufferIndex])); - } - } - - return vertices; } +// This mimics some of the logic in AssetLoader in order to determine which VertexBuffer slots +// correspond to the morph targets. +int MorphHelper::determineBaseSlot(const cgltf_primitive& prim) const { + int slot = 0; + bool hasNormals = false; + for (cgltf_size aindex = 0; aindex < prim.attributes_count; aindex++) { + const cgltf_attribute& attribute = prim.attributes[aindex]; + const int index = attribute.index; + const cgltf_attribute_type atype = attribute.type; + const cgltf_accessor* accessor = attribute.data; + if (atype == cgltf_attribute_type_tangent) { + continue; + } + if (atype == cgltf_attribute_type_normal) { + slot++; + hasNormals = true; + continue; + } + slot++; + } + + // If the model has lighting but not normals, then a slot is used for generated flat normals. + if (prim.material && !prim.material->unlit && !hasNormals) { + slot++; + } + + return slot; +}; + } // namespace gltfio diff --git a/libs/gltfio/src/MorphHelper.h b/libs/gltfio/src/MorphHelper.h index dae63699cd..74684097d7 100644 --- a/libs/gltfio/src/MorphHelper.h +++ b/libs/gltfio/src/MorphHelper.h @@ -24,13 +24,14 @@ #include struct cgltf_node; +struct cgltf_mesh; struct cgltf_primitive; namespace gltfio { /** - * Internal class that partitions lists of morph weights and maintains a cache of VertexBuffer - * objects for each partition. This allows gltfio to support up to 255 morph targets. + * Internal class that partitions lists of morph weights and maintains a cache of BufferObject + * instances. This allows gltfio to support up to 255 morph targets. * * Each partition is associated with an unordered set of 4 (or fewer) morph target indices, which * we call the "primary indices" for that time slice. @@ -48,41 +49,30 @@ public: */ void applyWeights(Entity targetEntity, float const* weights, size_t count) noexcept; - /** - * Disables or enables writes to renderables. - * - * This allows the MorphHelper cache to be primed without actually affecting any renderables. - */ - void disableWrites(bool disabled) noexcept { mWritesDisabled = disabled; } - private: - struct GltfPrimitive { - filament::VertexBuffer* vertices; - filament::IndexBuffer* indices; - filament::RenderableManager::PrimitiveType type; + struct GltfTarget { + filament::BufferObject* bufferObject; + int morphTargetIndex; + cgltf_attribute_type type; }; - struct Permutation { - filament::math::ubyte4 primaryIndices; - std::vector primitives; - bool owner; + struct GltfPrimitive { + filament::VertexBuffer* vertexBuffer; + int baseSlot; + std::vector targets; // TODO: flatten this? }; struct TableEntry { - std::vector permutations; - const cgltf_node* sourceNode; + std::vector primitives; // TODO: flatten this? }; - void addPermutation(Entity targetEntity, filament::math::ubyte4 primaryIndices); - - filament::VertexBuffer* createVertexBuffer(const cgltf_primitive& prim, const UvMap& uvmap, - filament::math::ubyte4 primaryIndices); + void addPrimitive(cgltf_mesh const* mesh, int primitiveIndex, TableEntry* entry); + int determineBaseSlot(const cgltf_primitive& prim) const; std::vector mPartiallySortedWeights; tsl::robin_map mMorphTable; const FFilamentAsset* mAsset; const FFilamentInstance* mInstance; - bool mWritesDisabled = false; }; } // namespace gltfio diff --git a/libs/gltfio/src/ResourceLoader.cpp b/libs/gltfio/src/ResourceLoader.cpp index 0e1047a721..e0834c8fb6 100644 --- a/libs/gltfio/src/ResourceLoader.cpp +++ b/libs/gltfio/src/ResourceLoader.cpp @@ -21,6 +21,7 @@ #include "TangentsJob.h" #include "upcast.h" +#include #include #include #include @@ -431,8 +432,11 @@ bool ResourceLoader::loadResources(FFilamentAsset* asset, bool async) { const uint8_t* data = computeBindingOffset(accessor) + bufferData; const uint32_t size = computeBindingSize(accessor); if (slot.vertexBuffer) { - VertexBuffer::BufferDescriptor bd(data, size, uploadCallback, uploadUserdata(asset)); - slot.vertexBuffer->setBufferAt(engine, slot.bufferIndex, std::move(bd)); + BufferObject* bo = BufferObject::Builder().size(size).build(engine); + asset->mBufferObjects.push_back(bo); + bo->setBuffer(engine, BufferDescriptor(data, size, + uploadCallback, uploadUserdata(asset))); + slot.vertexBuffer->setBufferObjectAt(engine, slot.bufferIndex, bo); continue; } assert(slot.indexBuffer); @@ -846,9 +850,12 @@ void ResourceLoader::Impl::computeTangents(FFilamentAsset* asset) { // Finally, upload quaternions to the GPU from the main thread. for (Params& params : jobParams) { - VertexBuffer::BufferDescriptor bd(params.out.results, - params.out.vertexCount * sizeof(short4), FREE_CALLBACK); - params.context.vb->setBufferAt(*mEngine, params.context.slot, std::move(bd)); + BufferObject* bo = BufferObject::Builder() + .size(params.out.vertexCount * sizeof(short4)).build(*mEngine); + asset->mBufferObjects.push_back(bo); + bo->setBuffer(*mEngine, BufferDescriptor( + params.out.results, bo->getByteCount(), FREE_CALLBACK)); + params.context.vb->setBufferObjectAt(*mEngine, params.context.slot, bo); } } @@ -868,8 +875,10 @@ void ResourceLoader::applySparseData(FFilamentAsset* asset) const { cgltf_size numBytes = sizeof(float) * numFloats; float* generated = (float*) malloc(numBytes); cgltf_accessor_unpack_floats(accessor, generated, numFloats); - VertexBuffer::BufferDescriptor bd(generated, numBytes, FREE_CALLBACK); - slot.vertexBuffer->setBufferAt(*pImpl->mEngine, slot.bufferIndex, std::move(bd)); + BufferObject* bo = BufferObject::Builder().size(numBytes).build(*asset->mEngine); + asset->mBufferObjects.push_back(bo); + bo->setBuffer(*pImpl->mEngine, BufferDescriptor(generated, numBytes, FREE_CALLBACK)); + slot.vertexBuffer->setBufferObjectAt(*pImpl->mEngine, slot.bufferIndex, bo); } }