diff --git a/ios/Classes/filament/FilamentMethodCallHandler.mm b/ios/Classes/filament/FilamentMethodCallHandler.mm index 0098dbe9..9e468ee9 100644 --- a/ios/Classes/filament/FilamentMethodCallHandler.mm +++ b/ios/Classes/filament/FilamentMethodCallHandler.mm @@ -83,8 +83,10 @@ static void* freeResourceGlobal(void* mem, size_t size, void* misc) { if(!_viewer) return; _viewer->manipulator->grabEnd(); + } else if([@"releaseSourceAssets" isEqualToString:call.method]) { + _viewer->releaseSourceAssets(); } else if([@"createMorpher" isEqualToString:call.method]) { - _viewer->createMorpher([call.arguments[0] UTF8String], [call.arguments[1] UTF8String],[call.arguments[2] UTF8String]); + _viewer->createMorpher([call.arguments[0] UTF8String], [call.arguments[1] intValue]); } else if([@"getTargetNames" isEqualToString:call.method]) { mimetic::StringList list = _viewer->getTargetNames([call.arguments UTF8String]); NSMutableArray* asArray = [NSMutableArray arrayWithCapacity:list.count]; @@ -96,15 +98,13 @@ static void* freeResourceGlobal(void* mem, size_t size, void* misc) { if(!_viewer) return; NSArray* nWeights = call.arguments[0]; - NSNumber* nPrimitiveIndex = call.arguments[1]; - int primitiveIndex = [nPrimitiveIndex intValue]; int count = [nWeights count]; float weights[count]; for(int i=0; i < count; i++) { weights[i] = [nWeights[i] floatValue]; } - _viewer->morphHelper->applyWeights(weights, count, primitiveIndex); + _viewer->morphHelper->applyWeights(weights, count); } else if([@"zoom" isEqualToString:call.method]) { if(!_viewer) return; diff --git a/ios/src/FilamentViewer.cpp b/ios/src/FilamentViewer.cpp index 0e135adf..c51183ae 100644 --- a/ios/src/FilamentViewer.cpp +++ b/ios/src/FilamentViewer.cpp @@ -142,11 +142,14 @@ void FilamentViewer::loadResources(string relativeResourcePath) { } _animator = _asset->getAnimator(); -// _asset->releaseSourceData(); // we need to wait until the Morpher is created to release the source data _scene->addEntities(_asset->getEntities(), _asset->getEntityCount()); }; +void FilamentViewer::releaseSourceAssets() { + _asset->releaseSourceData(); +} + void FilamentViewer::loadGltf(const char* const uri, const char* const relativeResourcePath) { if(_asset) { _resourceLoader->evictResourceData(); @@ -192,8 +195,8 @@ StringList FilamentViewer::getTargetNames(const char* meshName) { return StringList(nullptr, 0); } -void FilamentViewer::createMorpher(const char* meshName, const char* entityName, const char* materialInstanceName) { - morphHelper = new gltfio::GPUMorphHelper((FFilamentAsset*)_asset, meshName, entityName, materialInstanceName); +void FilamentViewer::createMorpher(const char* meshName, int primitiveIndex) { + morphHelper = new gltfio::GPUMorphHelper((FFilamentAsset*)_asset, meshName, primitiveIndex); } diff --git a/ios/src/FilamentViewer.hpp b/ios/src/FilamentViewer.hpp index 80ddebc3..51bd26fd 100644 --- a/ios/src/FilamentViewer.hpp +++ b/ios/src/FilamentViewer.hpp @@ -65,7 +65,8 @@ namespace mimetic { void loadSkybox(const char* const skyboxUri, const char* const iblUri); void updateViewportAndCameraProjection(int height, int width, float scaleFactor); void render(); - void createMorpher(const char* meshName, const char* entityName, const char* materialInstanceName); + void createMorpher(const char* meshName, int primitiveIndex); + void releaseSourceAssets(); StringList getTargetNames(const char* meshName); Manipulator* manipulator; GPUMorphHelper* morphHelper; diff --git a/ios/src/morph/GPUMorphHelper.cpp b/ios/src/morph/GPUMorphHelper.cpp index c094b57c..c68ceff2 100644 --- a/ios/src/morph/GPUMorphHelper.cpp +++ b/ios/src/morph/GPUMorphHelper.cpp @@ -49,7 +49,7 @@ namespace gltfio { free(mem); }; - GPUMorphHelper::GPUMorphHelper(FFilamentAsset *asset, const char* meshName, const char* entityName, int primitiveIndex) : mAsset(asset) { + GPUMorphHelper::GPUMorphHelper(FFilamentAsset *asset, const char* meshName, int primitiveIndex) : mAsset(asset), mPrimitiveIndex(primitiveIndex) { cgltf_size num_primitives = 0; NodeMap &sourceNodes = asset->isInstanced() ? asset->mInstances[0]->nodeMap @@ -64,15 +64,13 @@ namespace gltfio { if(strcmp(meshName, mesh->name) == 0) { targetMesh = mesh; num_primitives = mesh->primitives_count; - for (cgltf_size pi = 0, count = mesh->primitives_count; pi < count; ++pi) { - addPrimitive(mesh, pi, &mMorphTable[pair.second]); - } + addPrimitive(mesh); } } } auto materialInstances = mAsset->getMaterialInstances(); - std::cout << "Listing all material instances in asset" << std::endl; + std::cout << "MaterialInstances in asset:" << std::endl; for(int i = 0; i < mAsset->getMaterialInstanceCount(); i++) { const char* name = materialInstances[i]->getName(); std::cout << "Material : " << name << std::endl; @@ -92,134 +90,108 @@ namespace gltfio { void GPUMorphHelper::createTextures() { auto materialInstances = mAsset->getMaterialInstances(); auto &engine = *(mAsset->mEngine); + + auto& prim = animatedPrimitive; - for (auto&& entry : mMorphTable) { - std::vector& prims = (std::vector&)entry.second.primitives; - for(int i = 0; i < prims.size(); i++) { -// prims.at(i).texture = nullptr; - auto& prim = prims.at(i); -// } -// for (auto&& prim : entry.second.primitives) { - // for a single morph target, each vertex will be assigned 2 pixels, corresponding to a position vec3 and a normal vec3 - // these two vectors will be laid out adjacent in memory - // the total texture "width" is the total number of these pixels - // morph targets are then assigned to the depth channel - auto textureWidth = prim.numVertices * 2; + // for a single morph target, each vertex will be assigned 2 pixels, corresponding to a position vec3 and a normal vec3 + // these two vectors will be laid out adjacent in memory + // the total texture "width" is the total number of these pixels + // morph targets are then assigned to the depth channel + auto textureWidth = prim->numVertices * 2; - // the total size of the texture in bytes - // equal to (numVertices * numAttributes * vectorSize (3) * sizeof(float) * numMorphTargets) - auto textureSize = textureWidth * 3 * sizeof(float) * prim.numTargets; - auto textureBuffer = (float *const) malloc(textureSize); - - if(!textureBuffer) { - std::cout << "Error allocating texture buffer" << std::endl; - exit(-1); - } - - uint32_t offset = 0; - - // assume the primitive morph target source buffer is laid out like: - // |target0_v0_pos * 3|target0_v0_norm * 3|target0_v1_pos * 3|target0_v1_norm * 3|...|target1_v0_pos * 3|target1_v0_norm * 3|target1_v1_pos * 3|target1_v1_norm * 3|... - // where: - // - target0/target1/etc is the first/second/etc morph target - // - v0/v1/etc is the first/second/etc vertex - // - pos/norm are each 3-float vectors - for (auto &target : prim.targets) { - if(target.type == cgltf_attribute_type_position - || target.type == cgltf_attribute_type_normal - ) { - memcpy(textureBuffer+offset, target.bufferObject, target.bufferSize); - offset += int(target.bufferSize / sizeof(float)); - } - } - - Texture* texture = Texture::Builder() - .width(textureWidth) // - .height(1) - .depth(prim.numTargets) - .sampler(Texture::Sampler::SAMPLER_2D_ARRAY) - .format(Texture::InternalFormat::RGB32F) - .levels(0x01) - .build(engine); - - prim.texture = texture; //std::unique_ptr(texture); - - Texture::PixelBufferDescriptor descriptor( - textureBuffer, - textureSize, - Texture::Format::RGB, - Texture::Type::FLOAT, - FREE_CALLBACK, - nullptr); - prim.texture->setImage(engine, 0, 0,0, 0, textureWidth, 1, prim.numTargets, std::move(descriptor)); - - for(int i = 0; i < mAsset->getMaterialInstanceCount(); i++) { - const char* name = materialInstances[i]->getName(); - - if(strcmp(name, prim.materialName) == 0) { - std::cout << "Found material instance for primitive under name : " << name << std::endl; - prim.materialInstance = materialInstances[i]; //std::unique_ptr(materialInstances[i]); - break; - } - } - - if(!prim.materialInstance) { - exit(-1); - } - - // this won't work if material instance is shared between primitives? - prim.materialInstance->setParameter("dimensions", filament::math::int3 { prim.numVertices * 2, numAttributes, prim.numTargets }); - prim.materialInstance->setParameter("morphTargets", prim.texture, TextureSampler()); - float weights[prim.numTargets]; - memset(weights, 0, prim.numTargets * sizeof(float)); - prim.materialInstance->setParameter("morphTargetWeights", weights, prim.numTargets); + // the total size of the texture in bytes + // equal to (numVertices * numAttributes * vectorSize (3) * sizeof(float) * numMorphTargets) + auto textureSize = textureWidth * 3 * sizeof(float) * prim->numTargets; + auto textureBuffer = (float *const) malloc(textureSize); + + if(!textureBuffer) { + std::cout << "Error allocating texture buffer" << std::endl; + exit(-1); + } + + uint32_t offset = 0; + + // assume the primitive morph target source buffer is laid out like: + // |target0_v0_pos * 3|target0_v0_norm * 3|target0_v1_pos * 3|target0_v1_norm * 3|...|target1_v0_pos * 3|target1_v0_norm * 3|target1_v1_pos * 3|target1_v1_norm * 3|... + // where: + // - target0/target1/etc is the first/second/etc morph target + // - v0/v1/etc is the first/second/etc vertex + // - pos/norm are each 3-float vectors + for (auto &target : prim->targets) { + if(target.type == cgltf_attribute_type_position + || target.type == cgltf_attribute_type_normal + ) { + memcpy(textureBuffer+offset, target.bufferObject, target.bufferSize); + offset += int(target.bufferSize / sizeof(float)); } } + + Texture* texture = Texture::Builder() + .width(textureWidth) // + .height(1) + .depth(prim->numTargets) + .sampler(Texture::Sampler::SAMPLER_2D_ARRAY) + .format(Texture::InternalFormat::RGB32F) + .levels(0x01) + .build(engine); + + prim->texture = texture; //std::unique_ptr(texture); + + Texture::PixelBufferDescriptor descriptor( + textureBuffer, + textureSize, + Texture::Format::RGB, + Texture::Type::FLOAT, + FREE_CALLBACK, + nullptr); + prim->texture->setImage(engine, 0, 0,0, 0, textureWidth, 1, prim->numTargets, std::move(descriptor)); + + for(int i = 0; i < mAsset->getMaterialInstanceCount(); i++) { + const char* name = materialInstances[i]->getName(); + if(strcmp(name, prim->materialName) == 0) { + std::cout << "Found material instance for primitive under name : " << name << std::endl; + prim->materialInstance = materialInstances[i]; //std::unique_ptr(materialInstances[i]); + break; + } + } + + if(!prim->materialInstance) { + exit(-1); + } + + prim->materialInstance->setParameter("dimensions", filament::math::int3 { prim->numVertices * 2, numAttributes, prim->numTargets }); + prim->materialInstance->setParameter("morphTargets", prim->texture, TextureSampler()); + float weights[prim->numTargets]; + memset(weights, 0, prim->numTargets * sizeof(float)); + prim->materialInstance->setParameter("morphTargetWeights", weights, prim->numTargets); } - void GPUMorphHelper::applyWeights(float const *weights, size_t count, int primitiveIndex) noexcept { - std::cout << "Applying " << count << " weights to primitive index " << primitiveIndex << std::endl; - for (auto &entry : mMorphTable) { - int i = 0 ; - for (auto &prim : entry.second.primitives) { - if(i == primitiveIndex) { - if(!prim.materialInstance) { - std::cout << "Error, couldn't find material instance for primitive under name " << prim.materialName << std::endl; - } else { - prim.materialInstance->setParameter("morphTargetWeights", weights, count); - break; - } - } - i++; - } - } + void GPUMorphHelper::applyWeights(float const *weights, size_t count) noexcept { + std::cout << "Applying " << count << " weights " << std::endl; + animatedPrimitive->materialInstance->setParameter("morphTargetWeights", weights, count); } void - GPUMorphHelper::addPrimitive(cgltf_mesh const *mesh, int primitiveIndex, TableEntry *entry) { + GPUMorphHelper::addPrimitive(cgltf_mesh const *mesh) { auto &engine = *mAsset->mEngine; - const cgltf_primitive &prim = mesh->primitives[primitiveIndex]; + const cgltf_primitive &prim = mesh->primitives[mPrimitiveIndex]; - const auto &gltfioPrim = mAsset->mMeshCache.at(mesh)[primitiveIndex]; + const auto &gltfioPrim = mAsset->mMeshCache.at(mesh)[mPrimitiveIndex]; VertexBuffer *vertexBuffer = gltfioPrim.vertices; - entry->primitives.push_back({vertexBuffer}); + animatedPrimitive = std::make_unique(GltfPrimitive{vertexBuffer}); - auto &morphHelperPrim = entry->primitives.back(); - morphHelperPrim.materialName = prim.material->name; - morphHelperPrim.numTargets = prim.targets_count; - morphHelperPrim.numVertices = vertexBuffer->getVertexCount(); + animatedPrimitive->materialName = prim.material->name; + animatedPrimitive->numTargets = prim.targets_count; + animatedPrimitive->numVertices = vertexBuffer->getVertexCount(); cgltf_size maxIndex = 0; for(int i = 0; i < prim.indices->count; i++) { maxIndex = std::max(cgltf_accessor_read_index(prim.indices, i), maxIndex); } - - std::cout << "Max index for primitive index " << primitiveIndex << " is " << maxIndex << " and numVertices was " << morphHelperPrim.numVertices << std::endl; - const cgltf_accessor *previous = nullptr; - // for this primitive, iterate over every target + // iterate over every target for the primitive, 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++) { @@ -237,10 +209,6 @@ namespace gltfio { // // the texture needs to be sized according to the total number of vertices in the mesh // this is identified by the highest vertex index of all primitives in the mesh - // -// if(numVertices == 0) - // numVertices = accessor->count; -//assert(numVertices == accessor->count); // All position & normal attributes must have the same data type. assert_invariant( @@ -254,141 +222,10 @@ namespace gltfio { assert_invariant(bufferData); const uint8_t *data = computeBindingOffset(accessor) + bufferData; const uint32_t size = computeBindingSize(accessor); - morphHelperPrim.targets.push_back({data, size, targetIndex, atype}); + animatedPrimitive->targets.push_back({data, size, targetIndex, atype}); } } } } } } - - -//VertexBuffer* vBuf = VertexBuffer::Builder() -// .vertexCount(numVertices) -// .bufferCount(numPrimitives) -// .attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT4, 0) -// .build(*engine); - -//numIndices = maxIndex+1; */ -//numIndices = prim.indices->count; - -/*indicesBuffer = (uint32_t*)malloc(sizeof(unsigned int) * prim.indices->count); - -//materialInstance->setParameter("vertexIndices", indicesBuffer, numIndices); - -//.require(VertexAttribute::UV0) -//.require(MaterialBuilder.VertexAttribute.CUSTOM0) -//MaterialBuilder::init(); -//MaterialBuilder builder = MaterialBuilder() -// .name("DefaultMaterial") -// .platform(MaterialBuilder::Platform::MOBILE) -// .targetApi(MaterialBuilder::TargetApi::ALL) -// .optimization(MaterialBuilderBase::Optimization::NONE) -// .shading(MaterialBuilder::Shading::LIT) -// .parameter(MaterialBuilder::UniformType::FLOAT3, "baseColor") -// .parameter(MaterialBuilder::UniformType::INT3, "dimensions") -// .parameter(MaterialBuilder::UniformType::FLOAT, numTargets, MaterialBuilder::ParameterPrecision::DEFAULT, "morphTargetWeights") -// .parameter(MaterialBuilder::SamplerType::SAMPLER_2D_ARRAY, MaterialBuilder::SamplerFormat::FLOAT, MaterialBuilder::ParameterPrecision::DEFAULT, "morphTargets") -// .vertexDomain(VertexDomain::WORLD) -// .material(R"SHADER(void material(inout MaterialInputs material) { -// prepareMaterial(material); -// material.baseColor.rgb = materialParams.baseColor; -// })SHADER") -// .materialVertex(R"SHADER( -// vec3 getMorphTarget(int vertexIndex, int morphTargetIndex) { -// // our texture is laid out as (x,y,z) where y is 1, z is the number of morph targets, and x is the number of vertices * 2 (multiplication accounts for position + normal) -// // UV coordinates are normalized to (-1,1), so we divide the current vertex index by the total number of vertices to find the correct coordinate for this vertex -// vec3 uv = vec3( -// (float(vertexIndex) + 0.5) / float(materialParams.dimensions.x), -// 0.0f, -// //(float(morphTargetIndex) + 0.5f) / float(materialParams.dimensions.z)); -// float(morphTargetIndex)); -// return texture(materialParams_morphTargets, uv).xyz; -// } -// -// void materialVertex(inout MaterialVertexInputs material) { -// return; -// // for every morph target -// for(int morphTargetIndex = 0; morphTargetIndex < materialParams.dimensions.z; morphTargetIndex++) { -// -// // get the weight to apply -// float weight = materialParams.morphTargetWeights[morphTargetIndex]; -// -// // get the ID of this vertex, which will be the x-offset of the position attribute in the texture sampler -// int vertexId = getVertexIndex(); -// -// // get the position of the target for this vertex -// vec3 morphTargetPosition = getMorphTarget(vertexId, morphTargetIndex); -// // update the world position of this vertex -// material.worldPosition.xyz += (weight * morphTargetPosition); -// -// // increment the vertexID by half the size of the texture to get the x-offset of the normal (all positions stored in the first half, all normals stored in the second half) -// -// vertexId += (materialParams.dimensions.x / 2); -// -// // get the normal of this target for this vertex -// vec3 morphTargetNormal = getMorphTarget(vertexId, morphTargetIndex); -// material.worldNormal += (weight * morphTargetNormal); -// } -// mat4 transform = getWorldFromModelMatrix(); -// material.worldPosition = mulMat4x4Float3(transform, material.worldPosition.xyz); -// })SHADER"); -// -//Package pkg = builder.build(mAsset->mEngine->getJobSystem()); -//Material* material = Material::Builder().package(pkg.getData(), pkg.getSize()) -// .build(*mAsset->mEngine); - -//size_t normal_size = sizeof(short4); -//assert(textureWidth * (position_size + normal_size) == textureSize); -//assert(textureWidth * position_size == textureSize); -/*__android_log_print(ANDROID_LOG_INFO, "MyTag", "Expected size %d width at level 0 %d height", Texture::PixelBufferDescriptor::computeDataSize(Texture::Format::RGB, - Texture::Type::FLOAT, 24, 1, 4), texture->getWidth(0),texture->getHeight(0)); */ - -/* Texture::PixelBufferDescriptor descriptor( - textureBuffer, - textureSize, - Texture::Format::RGB, - Texture::Type::FLOAT, - 4, 0,0, 24, - FREE_CALLBACK, - nullptr); */ - -/*for(int i = 0; i < int(textureSize / sizeof(float)); i++) { - __android_log_print(ANDROID_LOG_INFO, "MyTag", "offset %d %f", i, *(textureBuffer+i)); -//}*/ -//std::cout << "Checking for " << materialInstanceName << std::endl; -//if(materialInstanceName) { -// for(int i = 0; i < asset->getMaterialInstanceCount(); i++) { -// const char* name = instances[i]->getName(); -// std::cout << name << std::endl; -// if(strcmp(name, materialInstanceName) == 0) { -// materialInstance = instances[i]; -// break; -// } -// } -//} else { -// materialInstance = instances[0]; -//} -// -//if(!materialInstance) { -// exit(-1); -//} - -// std::cout << std::endl; - /* for (int i = 0; i < 4; i++) { - morphHelperPrim.positions[i] = gltfioPrim.morphPositions[i]; - morphHelperPrim.tangents[i] = gltfioPrim.morphTangents[i]; - } */ - -// applyTextures(materialInstance); - -/* const Entity* entities = mAsset->getEntities(); - for(int i=0; i < mAsset->getEntityCount();i++) { - std::cout << mAsset->getName(entities[i]); - } */ - -// Entity entity = mAsset->getFirstEntityByName(entityName); -// RenderableManager::Instance rInst = mAsset->mEngine->getRenderableManager().getInstance(entity); -// for(int i = 0; imEngine->getRenderableManager().setMaterialInstanceAt(rInst, i, materialInstance); -// } diff --git a/ios/src/morph/GPUMorphHelper.h b/ios/src/morph/GPUMorphHelper.h index cddea90f..4b67c4f7 100644 --- a/ios/src/morph/GPUMorphHelper.h +++ b/ios/src/morph/GPUMorphHelper.h @@ -42,13 +42,14 @@ namespace gltfio { public: using Entity = utils::Entity; - GPUMorphHelper(FFilamentAsset *asset, const char* meshName, const char* entityName, const char* materialInstanceName); + GPUMorphHelper(FFilamentAsset *asset, const char* meshName, int primitiveIndex); ~GPUMorphHelper(); - void applyWeights(float const *weights, size_t count, int primitiveIndex) noexcept; + void applyWeights(float const *weights, size_t count) noexcept; private: + int mPrimitiveIndex; struct GltfTarget { const void *bufferObject; uint32_t bufferSize; @@ -58,32 +59,25 @@ namespace gltfio { struct GltfPrimitive { filament::VertexBuffer *vertexBuffer; -// const std::unique_ptr texture; Texture* texture; std::vector targets; // TODO: flatten this? const char* materialName; cgltf_size numTargets = 0; cgltf_size numVertices = 0; - //const std::unique_ptr materialInstance; MaterialInstance* materialInstance; }; - struct TableEntry { - std::vector primitives; // TODO: flatten this? - }; - int numAttributes = 2; // position & normal uint32_t* indicesBuffer = nullptr; - void addPrimitive(cgltf_mesh const *mesh, int primitiveIndex, TableEntry *entry); + void addPrimitive(cgltf_mesh const *mesh); void createTextures(); - cgltf_mesh const* targetMesh; - tsl::robin_map mMorphTable; FFilamentAsset *mAsset; + std::unique_ptr animatedPrimitive; }; } diff --git a/lib/filament_controller.dart b/lib/filament_controller.dart index 42a94d03..6197046b 100644 --- a/lib/filament_controller.dart +++ b/lib/filament_controller.dart @@ -19,8 +19,7 @@ abstract class FilamentController { void animate( List> weights, int primitiveIndex, double frameRate); - Future createMorpher(String meshName, String entityName, - {String? materialName}); + Future createMorpher(String meshName, int primitiveIndex); Future zoom(double z); } @@ -47,7 +46,8 @@ class MimeticFilamentController extends FilamentController { @override Future _initialize() async { - print("Initializing"); + final foo = await rootBundle.load(materialPath); + print("Initializing with material path of size ${foo.lengthInBytes}"); await _channel.invokeMethod("initialize", materialPath); } @@ -114,13 +114,15 @@ class MimeticFilamentController extends FilamentController { }); } + Future releaseSourceAssets() async { + await _channel.invokeMethod("releaseSourceAssets"); + } + Future zoom(double z) async { await _channel.invokeMethod("zoom", z); } - Future createMorpher(String meshName, String entityName, - {String? materialName}) async { - await _channel - .invokeMethod("createMorpher", [meshName, entityName, materialName]); + Future createMorpher(String meshName, int primitiveIndex) async { + await _channel.invokeMethod("createMorpher", [meshName, primitiveIndex]); } }