diff --git a/ios/include/AssetManager.hpp b/ios/include/AssetManager.hpp index 609bb0b6..e3edbec6 100644 --- a/ios/include/AssetManager.hpp +++ b/ios/include/AssetManager.hpp @@ -83,7 +83,9 @@ namespace polyvox const char *const boneName, const char **const meshName, int numMeshTargets, - float frameLengthInMs); + float frameLengthInMs, + bool isModelSpace); + void resetBones(EntityId entityId); void playAnimation(EntityId e, int index, bool loop, bool reverse, bool replaceActive, float crossfade = 0.3f); void stopAnimation(EntityId e, int index); void setMorphTargetWeights(const char *const entityName, float *weights, int count); @@ -95,6 +97,8 @@ namespace polyvox utils::Entity findChildEntityByName( EntityId entityId, const char *entityName); + int getEntityCount(EntityId entity, bool renderableOnly); + const char* getEntityNameAt(EntityId entity, int index, bool renderableOnly); private: AssetLoader *_assetLoader = nullptr; @@ -117,11 +121,5 @@ namespace polyvox inline void updateTransform(SceneAsset &asset); - void updateBoneTransformFromAnimationBuffer( - const BoneAnimation& animation, - int frameNumber, - FilamentAsset *asset - ); - }; } diff --git a/ios/include/FilamentViewer.hpp b/ios/include/FilamentViewer.hpp index 476b9b82..51d61e6d 100644 --- a/ios/include/FilamentViewer.hpp +++ b/ios/include/FilamentViewer.hpp @@ -67,6 +67,8 @@ namespace polyvox void loadIbl(const char *const iblUri, float intensity); void removeIbl(); + void rotateIbl(const math::mat3f & matrix); + void removeAsset(EntityId asset); void clearAssets(); diff --git a/ios/include/FlutterFilamentApi.h b/ios/include/FlutterFilamentApi.h index faed965c..d8217f59 100644 --- a/ios/include/FlutterFilamentApi.h +++ b/ios/include/FlutterFilamentApi.h @@ -40,7 +40,7 @@ #endif /* __STDBOOL_H */ -#ifdef __APPLE__ +#if defined(__APPLE__) || defined(__EMSCRIPTEN__) #include #endif @@ -67,6 +67,7 @@ extern "C" FLUTTER_PLUGIN_EXPORT void set_bloom(const void *const viewer, float strength); FLUTTER_PLUGIN_EXPORT void load_skybox(const void *const viewer, const char *skyboxPath); FLUTTER_PLUGIN_EXPORT void load_ibl(const void *const viewer, const char *iblPath, float intensity); + FLUTTER_PLUGIN_EXPORT void rotate_ibl(const void *const viewer, float* rotationMatrix); FLUTTER_PLUGIN_EXPORT void remove_skybox(const void *const viewer); FLUTTER_PLUGIN_EXPORT void remove_ibl(const void *const viewer); FLUTTER_PLUGIN_EXPORT EntityId add_light(const void *const viewer, uint8_t type, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows); @@ -113,6 +114,10 @@ extern "C" int numMorphTargets, int numFrames, float frameLengthInMs); + + FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose( + void *assetManager, + EntityId asset); FLUTTER_PLUGIN_EXPORT void add_bone_animation( void *assetManager, EntityId asset, @@ -121,7 +126,8 @@ extern "C" const char *const boneName, const char **const meshNames, int numMeshTargets, - float frameLengthInMs); + float frameLengthInMs, + bool isModelSpace); FLUTTER_PLUGIN_EXPORT bool set_bone_transform( void *assetManager, EntityId asset, @@ -171,6 +177,8 @@ extern "C" FLUTTER_PLUGIN_EXPORT void pick(void *const viewer, int x, int y, EntityId *entityId); FLUTTER_PLUGIN_EXPORT const char *get_name_for_entity(void *const assetManager, const EntityId entityId); FLUTTER_PLUGIN_EXPORT const EntityId find_child_entity_by_name(void *const assetManager, const EntityId parent, const char* name); + FLUTTER_PLUGIN_EXPORT int get_entity_count(void *const assetManager, const EntityId target, bool renderableOnly); + FLUTTER_PLUGIN_EXPORT const char* get_entity_name_at(void *const assetManager, const EntityId target, int index, bool renderableOnly); FLUTTER_PLUGIN_EXPORT void set_recording(void *const viewer, bool recording); FLUTTER_PLUGIN_EXPORT void set_recording_output_directory(void *const viewer, const char* outputDirectory); FLUTTER_PLUGIN_EXPORT void ios_dummy(); diff --git a/ios/include/FlutterFilamentFFIApi.h b/ios/include/FlutterFilamentFFIApi.h index aa797aa4..0d04e4ba 100644 --- a/ios/include/FlutterFilamentFFIApi.h +++ b/ios/include/FlutterFilamentFFIApi.h @@ -64,14 +64,34 @@ FLUTTER_PLUGIN_EXPORT void set_morph_target_weights_ffi(void* const assetManager const float *const morphData, int numWeights ); +FLUTTER_PLUGIN_EXPORT bool set_morph_animation_ffi( + void *assetManager, + EntityId asset, + const char *const entityName, + const float *const morphData, + const int *const morphIndices, + int numMorphTargets, + int numFrames, + float frameLengthInMs); FLUTTER_PLUGIN_EXPORT bool set_bone_transform_ffi( void *assetManager, EntityId asset, const char *entityName, const float *const transform, const char *boneName); +FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi( + void *assetManager, + EntityId asset, + const float *const frameData, + int numFrames, + const char *const boneName, + const char **const meshNames, + int numMeshTargets, + float frameLengthInMs, + bool isModelSpace); FLUTTER_PLUGIN_EXPORT void set_post_processing_ffi(void* const viewer, bool enabled); FLUTTER_PLUGIN_EXPORT void pick_ffi(void* const viewer, int x, int y, EntityId* entityId); +FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose_ffi(void* const assetManager, EntityId entityId); FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi(); #ifdef __cplusplus diff --git a/ios/include/SceneAsset.hpp b/ios/include/SceneAsset.hpp index 7d0d76e5..f5fe84d2 100644 --- a/ios/include/SceneAsset.hpp +++ b/ios/include/SceneAsset.hpp @@ -62,17 +62,17 @@ namespace polyvox { // Use this to construct a dynamic (i.e. non-glTF embedded) bone/joint animation. // struct BoneAnimation : AnimationStatus { - uint8_t boneIndex; + size_t boneIndex; vector meshTargets; size_t skinIndex = 0; int lengthInFrames; float frameLengthInMs = 0; - vector frameData; + vector frameData; }; struct SceneAsset { FilamentAsset* asset = nullptr; - + vector initialJointTransforms; vector gltfAnimations; vector morphAnimations; vector boneAnimations; diff --git a/ios/src/AssetManager.cpp b/ios/src/AssetManager.cpp index 2927be03..4a5168b4 100644 --- a/ios/src/AssetManager.cpp +++ b/ios/src/AssetManager.cpp @@ -77,7 +77,7 @@ namespace polyvox EntityManager &em = EntityManager::get(); _assetLoader = AssetLoader::create({_engine, _ubershaderProvider, _ncm, &em}); - _gltfResourceLoader->addTextureProvider("image/ktx2", _ktxDecoder); + _gltfResourceLoader->addTextureProvider ("image/ktx2", _ktxDecoder); _gltfResourceLoader->addTextureProvider("image/png", _stbDecoder); _gltfResourceLoader->addTextureProvider("image/jpeg", _stbDecoder); } @@ -121,17 +121,31 @@ namespace polyvox _gltfResourceLoader->addResourceData(resourceUris[i], std::move(b)); } - // load resources synchronously - if (!_gltfResourceLoader->loadResources(asset)) - { - Log("Unknown error loading glTF asset"); - _resourceLoaderWrapper->free(rbuf); - for (auto &rb : resourceBuffers) - { - _resourceLoaderWrapper->free(rb); + #ifdef __EMSCRIPTEN__ + if (!_gltfResourceLoader->asyncBeginLoad(asset)) { + Log("Unknown error loading glTF asset"); + _resourceLoaderWrapper->free(rbuf); + for(auto& rb : resourceBuffers) { + _resourceLoaderWrapper->free(rb); + } + return 0; } - return 0; - } + while(_gltfResourceLoader->asyncGetLoadProgress() < 1.0f) { + _gltfResourceLoader->asyncUpdateLoad(); + } + #else + // load resources synchronously + if (!_gltfResourceLoader->loadResources(asset)) + { + Log("Unknown error loading glTF asset"); + _resourceLoaderWrapper->free(rbuf); + for (auto &rb : resourceBuffers) + { + _resourceLoaderWrapper->free(rb); + } + return 0; + } + #endif _scene->addEntities(asset->getEntities(), asset->getEntityCount()); @@ -142,7 +156,18 @@ namespace polyvox asset->releaseSourceData(); SceneAsset sceneAsset(asset); + + const auto joints = inst->getJointsAt(0); + TransformManager &transformManager = _engine->getTransformManager(); + + for(int i = 0; i < inst->getJointCountAt(0); i++) { + const auto joint = joints[i]; + const auto& jointTransformInstance = transformManager.getInstance(joint); + const auto& jointTransform = transformManager.getTransform(jointTransformInstance); + sceneAsset.initialJointTransforms.push_back(jointTransform); + } + utils::Entity e = EntityManager::get().create(); EntityId eid = Entity::smuggle(e); @@ -166,7 +191,7 @@ namespace polyvox ResourceBuffer rbuf = _resourceLoaderWrapper->load(uri); - Log("Loaded GLB of size %d at URI %s", rbuf.size, uri); + Log("Loaded GLB data (%d bytes) from URI %s", rbuf.size, uri); FilamentAsset *asset = _assetLoader->createAsset( (const uint8_t *)rbuf.data, rbuf.size); @@ -181,12 +206,23 @@ namespace polyvox _scene->addEntities(asset->getEntities(), entityCount); - if (!_gltfResourceLoader->loadResources(asset)) - { - Log("Unknown error loading glb asset"); - _resourceLoaderWrapper->free(rbuf); - return 0; - } + #ifdef __EMSCRIPTEN__ + if (!_gltfResourceLoader->asyncBeginLoad(asset)) { + Log("Unknown error loading glb asset"); + _resourceLoaderWrapper->free(rbuf); + return 0; + } + while(_gltfResourceLoader->asyncGetLoadProgress() < 1.0f) { + _gltfResourceLoader->asyncUpdateLoad(); + } + #else + if (!_gltfResourceLoader->loadResources(asset)) + { + Log("Unknown error loading glb asset"); + _resourceLoaderWrapper->free(rbuf); + return 0; + } + #endif auto lights = asset->getLightEntities(); _scene->addEntities(lights, asset->getLightEntityCount()); @@ -202,6 +238,17 @@ namespace polyvox _resourceLoaderWrapper->free(rbuf); SceneAsset sceneAsset(asset); + + const auto joints = inst->getJointsAt(0); + + TransformManager &transformManager = _engine->getTransformManager(); + + for(int i = 0; i < inst->getJointCountAt(0); i++) { + const auto joint = joints[i]; + const auto& jointTransformInstance = transformManager.getInstance(joint); + const auto& jointTransform = transformManager.getTransform(jointTransformInstance); + sceneAsset.initialJointTransforms.push_back(jointTransform); + } utils::Entity e = EntityManager::get().create(); EntityId eid = Entity::smuggle(e); @@ -278,7 +325,6 @@ namespace polyvox void AssetManager::updateAnimations() { - std::lock_guard lock(_animationMutex); RenderableManager &rm = _engine->getRenderableManager(); @@ -287,7 +333,7 @@ namespace polyvox for (auto &asset : _assets) { - for (int i = asset.gltfAnimations.size() - 1; i >= 0; i--) { + for (int i = ((int)asset.gltfAnimations.size()) - 1; i >= 0; i--) { auto animationStatus = asset.gltfAnimations[i]; @@ -295,12 +341,15 @@ namespace polyvox if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs) { + asset.asset->getInstance()->getAnimator()->applyAnimation(animationStatus.index, animationStatus.durationInSecs - 0.001); + asset.asset->getInstance()->getAnimator()->updateBoneMatrices(); asset.gltfAnimations.erase(asset.gltfAnimations.begin() + i); asset.fadeGltfAnimationIndex = -1; continue; } - + asset.asset->getInstance()->getAnimator()->applyAnimation(animationStatus.index, elapsedInSecs); + if (asset.fadeGltfAnimationIndex != -1 && elapsedInSecs < asset.fadeDuration) { // cross-fade @@ -353,21 +402,47 @@ namespace polyvox asset.boneAnimations.erase(asset.boneAnimations.begin() + i); continue; } - - float frameLengthInMs = animationStatus.frameLengthInMs; - int frameNumber = static_cast(elapsedInSecs * 1000.0f / frameLengthInMs) % animationStatus.lengthInFrames; + float elapsedFrames = elapsedInSecs * 1000.0f / animationStatus.frameLengthInMs; + + int currFrame = static_cast(elapsedFrames) % animationStatus.lengthInFrames; + float delta = elapsedFrames - currFrame; + int nextFrame = currFrame; + auto restLocalTransform = asset.initialJointTransforms[animationStatus.boneIndex]; // offset from the end if reverse if (animationStatus.reverse) { - frameNumber = animationStatus.lengthInFrames - frameNumber; + currFrame = animationStatus.lengthInFrames - currFrame; + if(currFrame > 0) { + nextFrame = currFrame - 1; + } else { + nextFrame = 0; + } + } else { + if(currFrame < animationStatus.lengthInFrames - 1) { + nextFrame = currFrame + 1; + } else { + nextFrame = currFrame; + } } - updateBoneTransformFromAnimationBuffer( - animationStatus, - frameNumber, - asset.asset); - + + // simple linear interpolation + math::mat4f curr = (1 - delta) * (restLocalTransform * animationStatus.frameData[currFrame]); + math::mat4f next = delta * (restLocalTransform * animationStatus.frameData[nextFrame]); + math::mat4f localTransform = curr + next; + + auto filamentInstance = asset.asset->getInstance(); + TransformManager &transformManager = _engine->getTransformManager(); + + const Entity joint = filamentInstance->getJointsAt(animationStatus.skinIndex)[animationStatus.boneIndex]; + + auto jointTransform = transformManager.getInstance(joint); + + transformManager.setTransform(jointTransform, localTransform); + + asset.asset->getInstance()->getAnimator()->updateBoneMatrices(); + if (animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs) { animationStatus.start = now; @@ -381,7 +456,6 @@ namespace polyvox // - or is it better to add an option for "streaming" mode where we can just return a reference to a mat4 and then update the values directly? bool AssetManager::setBoneTransform(EntityId entityId, const char *entityName, int32_t skinIndex, const char* boneName, math::mat4f localTransform) { - std::lock_guard lock(_animationMutex); const auto &pos = _entityIdLookup.find(entityId); @@ -436,7 +510,6 @@ namespace polyvox return false; } - utils::Entity joint = filamentInstance->getJointsAt(skinIndex)[boneIndex]; if (joint.isNull()) @@ -447,8 +520,8 @@ namespace polyvox const auto& inverseBindMatrix = filamentInstance->getInverseBindMatricesAt(skinIndex)[boneIndex]; - auto jointTransformInstance = transformManager.getInstance(joint); - auto globalJointTransform = transformManager.getWorldTransform(jointTransformInstance); + auto jointTransform = transformManager.getInstance(joint); + auto globalJointTransform = transformManager.getWorldTransform(jointTransform); auto inverseGlobalTransform = inverse( transformManager.getWorldTransform( @@ -467,47 +540,6 @@ namespace polyvox return true; } - void AssetManager::updateBoneTransformFromAnimationBuffer( - const BoneAnimation& animation, - int frameNumber, - FilamentAsset *asset) - { - auto filamentInstance = asset->getInstance(); - TransformManager &transformManager = _engine->getTransformManager(); - - RenderableManager &rm = _engine->getRenderableManager(); - - auto boneIndex = animation.boneIndex; - - math::mat4f localTransform(animation.frameData[frameNumber]); - - const auto& inverseBindMatrix = filamentInstance->getInverseBindMatricesAt(animation.skinIndex)[boneIndex]; - - for(const auto& meshTarget : animation.meshTargets) { - - const Entity joint = filamentInstance->getJointsAt(animation.skinIndex)[animation.boneIndex]; - - auto jointInstance = transformManager.getInstance(joint); - auto globalJointTransform = transformManager.getWorldTransform(jointInstance); - - - auto inverseGlobalTransform = inverse( - transformManager.getWorldTransform( - transformManager.getInstance(meshTarget) - ) - ); - const auto boneTransform = inverseGlobalTransform * globalJointTransform * localTransform * inverseBindMatrix; - const auto &renderableInstance = rm.getInstance(meshTarget); - rm.setBones( - renderableInstance, - &boneTransform, - 1, - boneIndex - ); - } - - } - void AssetManager::remove(EntityId entityId) { std::lock_guard lock(_animationMutex); @@ -716,13 +748,45 @@ namespace polyvox return true; } + void AssetManager::resetBones(EntityId entityId) { + std::lock_guard lock(_animationMutex); + + const auto &pos = _entityIdLookup.find(entityId); + if (pos == _entityIdLookup.end()) + { + Log("ERROR: asset not found for entity."); + return; + } + auto &asset = _assets[pos->second]; + + auto filamentInstance = asset.asset->getInstance(); + filamentInstance->getAnimator()->resetBoneMatrices(); + + auto skinCount = filamentInstance->getSkinCount(); + + TransformManager &transformManager = _engine->getTransformManager(); + + for(int skinIndex = 0; skinIndex < skinCount; skinIndex++) { + for(int i =0; i < filamentInstance->getJointCountAt(skinIndex);i++) { + const Entity joint = filamentInstance->getJointsAt(skinIndex)[i]; + auto restLocalTransform = asset.initialJointTransforms[i]; + auto jointTransform = transformManager.getInstance(joint); + transformManager.setTransform(jointTransform, restLocalTransform); + } + } + filamentInstance->getAnimator()->updateBoneMatrices(); + filamentInstance->getAnimator()->resetBoneMatrices(); + + } + bool AssetManager::addBoneAnimation(EntityId entityId, const float *const frameData, int numFrames, const char *const boneName, const char **const meshNames, int numMeshTargets, - float frameLengthInMs) + float frameLengthInMs, + bool isModelSpace) { std::lock_guard lock(_animationMutex); @@ -734,8 +798,6 @@ namespace polyvox } auto &asset = _assets[pos->second]; - asset.asset->getInstance()->getAnimator()->resetBoneMatrices(); - auto filamentInstance = asset.asset->getInstance(); size_t skinCount = filamentInstance->getSkinCount(); @@ -768,15 +830,51 @@ namespace polyvox } animation.frameData.clear(); + const auto& tm = _engine->getTransformManager(); + const auto& inverseBindMatrix = filamentInstance->getInverseBindMatricesAt(skinIndex)[animation.boneIndex]; + const auto& bindMatrix = inverse(inverseBindMatrix); + math::float3 trans; + math::quatf rot; + math::float3 scale; + decomposeMatrix(inverseBindMatrix, &trans, &rot, &scale); + math::float3 btrans; + math::quatf brot; + math::float3 bscale; + decomposeMatrix(bindMatrix, &btrans, &brot, &bscale); + // Log("Bind matrix for bone %s is \n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n", boneName, bindMatrix[0][0],bindMatrix[1][0],bindMatrix[2][0],bindMatrix[3][0], + // bindMatrix[0][1],bindMatrix[1][1],bindMatrix[2][1],bindMatrix[3][1], + // bindMatrix[0][2],bindMatrix[1][2],bindMatrix[2][2],bindMatrix[3][2], + // bindMatrix[0][3],bindMatrix[1][3],bindMatrix[2][3],bindMatrix[3][3]); for(int i = 0; i < numFrames; i++) { - animation.frameData.push_back(math::quatf( - frameData[i*4], - frameData[(i*4)+1], - frameData[(i*4)+2], - frameData[(i*4)+3] - )); - - } + math::mat4f frame( + frameData[i*16], + frameData[(i*16)+1], + frameData[(i*16)+2], + frameData[(i*16)+3], + frameData[(i*16)+4], + frameData[(i*16)+5], + frameData[(i*16)+6], + frameData[(i*16)+7], + frameData[(i*16)+8], + frameData[(i*16)+9], + frameData[(i*16)+10], + frameData[(i*16)+11], + frameData[(i*16)+12], + frameData[(i*16)+13], + frameData[(i*16)+14], + frameData[(i*16)+15]); + // if(i == numFrames - 1) { Log("Model transform for bone %s is \n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n", boneName, + // frame[0][0],frame[1][0],frame[2][0],frame[3][0], + // frame[0][1],frame[1][1],frame[2][1],frame[3][1], + // frame[0][2],frame[1][2],frame[2][2],frame[3][2], + // frame[0][3],frame[1][3],frame[2][3],frame[3][3]); + // } + + if(isModelSpace) { + frame = (math::mat4f(rot) * frame) * math::mat4f(brot); + } + animation.frameData.push_back(frame); + } animation.frameLengthInMs = frameLengthInMs; @@ -789,7 +887,6 @@ namespace polyvox Log("Mesh target %s for bone animation could not be found", meshNames[i]); return false; } - Log("Added mesh target %s", meshNames[i]); animation.meshTargets.push_back(entity); } @@ -798,6 +895,7 @@ namespace polyvox animation.durationInSecs = (frameLengthInMs * numFrames) / 1000.0f; animation.lengthInFrames = numFrames; animation.frameLengthInMs = frameLengthInMs; + animation.skinIndex = 0; asset.boneAnimations.push_back(animation); return true; @@ -1172,4 +1270,63 @@ namespace polyvox return _ncm->getName(nameInstance); } + int AssetManager::getEntityCount(EntityId entityId, bool renderableOnly) { + const auto &pos = _entityIdLookup.find(entityId); + if (pos == _entityIdLookup.end()) + { + Log("ERROR: asset not found for entity."); + return 0; + } + auto &asset = _assets[pos->second]; + if(renderableOnly) { + int count = 0; + const auto& rm = _engine->getRenderableManager(); + const Entity *entities = asset.asset->getEntities(); + for(int i=0; i < asset.asset->getEntityCount(); i++) { + if(rm.hasComponent(entities[i])) { + count++; + } + } + return count; + } + return asset.asset->getEntityCount(); + } + + const char* AssetManager::getEntityNameAt(EntityId entityId, int index, bool renderableOnly) { + const auto &pos = _entityIdLookup.find(entityId); + if (pos == _entityIdLookup.end()) + { + Log("ERROR: asset not found for entity."); + return nullptr; + } + auto &asset = _assets[pos->second]; + int found = -1; + + if(renderableOnly) { + int count = 0; + const auto& rm = _engine->getRenderableManager(); + const Entity *entities = asset.asset->getEntities(); + for(int i=0; i < asset.asset->getEntityCount(); i++) { + if(rm.hasComponent(entities[i])) { + if(count == index) { + found = i; + break; + } + count++; + } + } + } else { + found = index; + } + + if(found >= asset.asset->getEntityCount()) { + Log("ERROR: index %d greater than number of child entities.", found); + return nullptr; + } + + const utils::Entity entity = asset.asset->getEntities()[found]; + auto inst = _ncm->getInstance(entity); + return _ncm->getName(inst); + } + } // namespace polyvox diff --git a/ios/src/FilamentViewer.cpp b/ios/src/FilamentViewer.cpp index 6a47d37f..48ccdd0f 100644 --- a/ios/src/FilamentViewer.cpp +++ b/ios/src/FilamentViewer.cpp @@ -25,6 +25,9 @@ #include #include +#ifdef __EMSCRIPTEN__ +#include +#endif #include #include #include @@ -131,6 +134,8 @@ namespace polyvox #elif TARGET_OS_OSX ASSERT_POSTCONDITION(platform == nullptr, "Custom Platform not supported on macOS"); _engine = Engine::create(Engine::Backend::METAL); +#elif defined(__EMSCRIPTEN__) + _engine = Engine::create(Engine::Backend::OPENGL, (backend::Platform *)new filament::backend::PlatformWebGL(), (void *)sharedContext, nullptr); #else _engine = Engine::create(Engine::Backend::OPENGL, (backend::Platform *)platform, (void *)sharedContext, nullptr); #endif @@ -154,11 +159,27 @@ namespace polyvox Log("View created"); setToneMapping(ToneMapping::ACES); - Log("Set tone mapping"); - setBloom(0.6f); - Log("Set bloom"); + #ifdef __EMSCRIPTEN__ + Log("Bloom is disabled on WebGL builds as it causes instability with certain drivers"); + decltype(_view->getBloomOptions()) opts; + opts.enabled = false; + _view->setBloomOptions(opts); + + _view->setAmbientOcclusionOptions({.enabled=false}); + + _view->setDynamicResolutionOptions({.enabled=false}); + + _view->setDithering(filament::Dithering::NONE); + _view->setAntiAliasing(filament::AntiAliasing::NONE); + _view->setShadowingEnabled(false); + _view->setScreenSpaceRefractionEnabled(false); + + #else + setBloom(0.6f); + Log("Set bloom"); + #endif _view->setScene(_scene); _view->setCamera(_mainCamera); @@ -215,7 +236,7 @@ namespace polyvox .package(IMAGE_IMAGE_DATA, IMAGE_IMAGE_SIZE) .build(*_engine); _imageMaterial->setDefaultParameter("showImage", 0); - _imageMaterial->setDefaultParameter("backgroundColor", RgbaType::sRGB, float4(0.5f, 0.5f, 0.5f, 1.0f)); + _imageMaterial->setDefaultParameter("backgroundColor", RgbaType::sRGB, float4(1.0f, 1.0f, 1.0f, 0.0f)); _imageMaterial->setDefaultParameter("image", _imageTexture, _imageSampler); } catch (...) @@ -265,10 +286,14 @@ namespace polyvox void FilamentViewer::setBloom(float strength) { - decltype(_view->getBloomOptions()) opts; - opts.enabled = true; - opts.strength = strength; - _view->setBloomOptions(opts); + #ifdef __EMSCRIPTEN__ + Log("Bloom is disabled on WebGL builds as it causes instability with certain drivers. setBloom will be ignored"); + #else + decltype(_view->getBloomOptions()) opts; + opts.enabled = true; + opts.strength = strength; + _view->setBloomOptions(opts); + #endif } void FilamentViewer::setToneMapping(ToneMapping toneMapping) @@ -481,6 +506,7 @@ namespace polyvox void FilamentViewer::setBackgroundColor(const float r, const float g, const float b, const float a) { + Log("Setting background color to rgba(%f,%f,%f,%f)", r, g, b, a); _imageMaterial->setDefaultParameter("showImage", 0); _imageMaterial->setDefaultParameter("backgroundColor", RgbaType::sRGB, float4(r, g, b, a)); _imageMaterial->setDefaultParameter("transform", _imageScale); @@ -926,6 +952,11 @@ namespace polyvox _scene->setIndirectLight(nullptr); } + void FilamentViewer::rotateIbl(const math::mat3f & matrix) { + _indirectLight->setRotation(matrix); + } + + void FilamentViewer::loadIbl(const char *const iblPath, float intensity) { removeIbl(); @@ -994,9 +1025,10 @@ namespace polyvox if (_frameCount == 60) { // Log("1 sec average for asset animation update %f", _elapsed / 60); + Log("Skipped frames : %d", _skippedFrames); _elapsed = 0; _frameCount = 0; - Log("Skipped frames : %d", _skippedFrames); + _skippedFrames = 0; } Timer tmr; @@ -1007,53 +1039,71 @@ namespace polyvox _frameCount++; // if a manipulator is active, update the active camera orientation - if (_manipulator) - { + if(_manipulator) { math::double3 eye, target, upward; - Camera &cam = _view->getCamera(); + Camera& cam =_view->getCamera(); _manipulator->getLookAt(&eye, &target, &upward); cam.lookAt(eye, target, upward); } - // Render the scene, unless the renderer wants to skip the frame. - if (_renderer->beginFrame(_swapChain, frameTimeInNanos)) - { - _renderer->render(_view); + // // TODO - this was an experiment but probably useful to keep for debugging + // // if pixelBuffer is provided, we will copy the framebuffer into the pixelBuffer. + // if (pixelBuffer) + // { + // auto pbd = Texture::PixelBufferDescriptor( + // pixelBuffer, size_t(1024 * 768 * 4), + // Texture::Format::RGBA, + // Texture::Type::BYTE, nullptr, callback, data); - if(_recording) { - Viewport const &vp = _view->getViewport(); - size_t pixelBufferSize = vp.width * vp.height * 4; - auto* pixelBuffer = new uint8_t[pixelBufferSize]; - auto callback = [](void *buf, size_t size, void *data) { - auto frameCallbackData = (FrameCallbackData*)data; - auto viewer = (FilamentViewer*)frameCallbackData->viewer; - viewer->savePng(buf, size, frameCallbackData->frameNumber); - delete frameCallbackData; - }; - - auto now = std::chrono::high_resolution_clock::now(); - auto elapsed = float(std::chrono::duration_cast(now - _startTime).count()); - - auto frameNumber = uint32_t(floor(elapsed / _frameInterval)); - - auto userData = new FrameCallbackData { this, frameNumber }; - - auto pbd = Texture::PixelBufferDescriptor( - pixelBuffer, pixelBufferSize, - Texture::Format::RGBA, - Texture::Type::UBYTE, nullptr, callback, userData); - - _renderer->readPixels(_rt, 0, 0, vp.width, vp.height, std::move(pbd)); - } + // _renderer->beginFrame(_swapChain, 0); + // _renderer->render(_view); + // _renderer->readPixels(0, 0, 1024, 768, std::move(pbd)); + // _renderer->endFrame(); + // } + // else + // { + // Render the scene, unless the renderer wants to skip the frame. + bool beginFrame = _renderer->beginFrame(_swapChain, frameTimeInNanos); - _renderer->endFrame(); - } - else - { - _skippedFrames++; - } + if (beginFrame) + { + _renderer->render(_view); + + if(_recording) { + Viewport const &vp = _view->getViewport(); + size_t pixelBufferSize = vp.width * vp.height * 4; + auto* pixelBuffer = new uint8_t[pixelBufferSize]; + auto callback = [](void *buf, size_t size, void *data) { + auto frameCallbackData = (FrameCallbackData*)data; + auto viewer = (FilamentViewer*)frameCallbackData->viewer; + viewer->savePng(buf, size, frameCallbackData->frameNumber); + delete frameCallbackData; + }; + + auto now = std::chrono::high_resolution_clock::now(); + auto elapsed = float(std::chrono::duration_cast(now - _startTime).count()); + + auto frameNumber = uint32_t(floor(elapsed / _frameInterval)); + + auto userData = new FrameCallbackData { this, frameNumber }; + + auto pbd = Texture::PixelBufferDescriptor( + pixelBuffer, pixelBufferSize, + Texture::Format::RGBA, + Texture::Type::UBYTE, nullptr, callback, userData); + + _renderer->readPixels(_rt, 0, 0, vp.width, vp.height, std::move(pbd)); + } + _renderer->endFrame(); + #ifdef __EMSCRIPTEN__ + _engine->execute(); + #endif + } else { + _skippedFrames++; + } } + void FilamentViewer::savePng(void* buf, size_t size, int frameNumber) { std::lock_guard lock(_recordingMutex); if(!_recording) { diff --git a/ios/src/FlutterFilamentApi.cpp b/ios/src/FlutterFilamentApi.cpp index c22a9ab9..c58fbe8b 100644 --- a/ios/src/FlutterFilamentApi.cpp +++ b/ios/src/FlutterFilamentApi.cpp @@ -76,6 +76,19 @@ extern "C" ((FilamentViewer *)viewer)->loadIbl(iblPath, intensity); } + FLUTTER_PLUGIN_EXPORT void rotate_ibl(const void *const viewer, float* rotationMatrix) { + math::mat3f matrix(rotationMatrix[0], rotationMatrix[1], + rotationMatrix[2], + rotationMatrix[3], + rotationMatrix[4], + rotationMatrix[5], + rotationMatrix[6], + rotationMatrix[7], + rotationMatrix[8]); + + ((FilamentViewer*)viewer)->rotateIbl(matrix); + } + FLUTTER_PLUGIN_EXPORT void remove_skybox(const void *const viewer) { ((FilamentViewer *)viewer)->removeSkybox(); @@ -330,6 +343,10 @@ extern "C" return ((AssetManager *)assetManager)->setMorphAnimationBuffer(asset, entityName, morphData, morphIndices, numMorphTargets, numFrames, frameLengthInMs); } + FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose(void *assetManager, EntityId entityId) { + ((AssetManager*)assetManager)->resetBones(entityId); + } + FLUTTER_PLUGIN_EXPORT void add_bone_animation( void *assetManager, EntityId asset, @@ -338,9 +355,10 @@ extern "C" const char *const boneName, const char **const meshNames, int numMeshTargets, - float frameLengthInMs) + float frameLengthInMs, + bool isModelSpace) { - ((AssetManager *)assetManager)->addBoneAnimation(asset, frameData, numFrames, boneName, meshNames, numMeshTargets, frameLengthInMs); + ((AssetManager *)assetManager)->addBoneAnimation(asset, frameData, numFrames, boneName, meshNames, numMeshTargets, frameLengthInMs, isModelSpace); } FLUTTER_PLUGIN_EXPORT void set_post_processing(void *const viewer, bool enabled) @@ -492,6 +510,15 @@ extern "C" return ((AssetManager *)assetManager)->getNameForEntity(entityId); } + FLUTTER_PLUGIN_EXPORT int get_entity_count(void *const assetManager, const EntityId target, bool renderableOnly) { + return ((AssetManager *)assetManager)->getEntityCount(target, renderableOnly); + } + + FLUTTER_PLUGIN_EXPORT const char* get_entity_name_at(void *const assetManager, const EntityId target, int index, bool renderableOnly) { + return ((AssetManager *)assetManager)->getEntityNameAt(target, index, renderableOnly); + } + + FLUTTER_PLUGIN_EXPORT void set_recording(void *const viewer, bool recording) { ((FilamentViewer*)viewer)->setRecording(recording); } diff --git a/ios/src/FlutterFilamentFFIApi.cpp b/ios/src/FlutterFilamentFFIApi.cpp index ddddee04..7bad2b3f 100644 --- a/ios/src/FlutterFilamentFFIApi.cpp +++ b/ios/src/FlutterFilamentFFIApi.cpp @@ -9,8 +9,31 @@ #include #include #include +#include + +#ifdef __EMSCRIPTEN__ +#define GL_GLEXT_PROTOTYPES +#include +#include + +#include +#include +#include +#include using namespace polyvox; +using namespace std::chrono_literals; + +#include +#include + +extern "C" +{ + extern FLUTTER_PLUGIN_EXPORT EMSCRIPTEN_WEBGL_CONTEXT_HANDLE flutter_filament_web_create_gl_context(); +} + +#endif +#include class RenderLoop { public: @@ -63,8 +86,20 @@ public: void (*renderCallback)(void *), void *const owner) { _renderCallback = renderCallback; _renderCallbackOwner = owner; - std::packaged_task lambda([&]() mutable { - return new FilamentViewer(context, loader, platform, uberArchivePath); + std::packaged_task lambda([=]() mutable { + #ifdef __EMSCRIPTEN__ + auto emContext = flutter_filament_web_create_gl_context(); + + auto success = emscripten_webgl_make_context_current((EMSCRIPTEN_WEBGL_CONTEXT_HANDLE)emContext); + if(success != EMSCRIPTEN_RESULT_SUCCESS) { + std::cout << "Failed to make context current." << std::endl; + return (FilamentViewer*)nullptr; + } + _viewer = new FilamentViewer((void* const) emContext, loader, platform, uberArchivePath); + #else + _viewer = new FilamentViewer(context, loader, platform, uberArchivePath); + #endif + return _viewer; }); auto fut = add_task(lambda); fut.wait(); @@ -90,10 +125,17 @@ public: } void doRender() { + auto now = std::chrono::high_resolution_clock::now(); + auto nanos = std::chrono::duration_cast(now.time_since_epoch()).count(); render(_viewer, 0, nullptr, nullptr, nullptr); + _lastRenderTime = std::chrono::high_resolution_clock::now(); if(_renderCallback) { _renderCallback(_renderCallbackOwner); } + #ifdef __EMSCRIPTEN__ + emscripten_webgl_commit_frame(); + #endif + } void setFrameIntervalInMilliseconds(float frameIntervalInMilliseconds) { @@ -121,6 +163,7 @@ private: std::thread *_t = nullptr; std::condition_variable _cond; std::deque> _tasks; + std::chrono::steady_clock::time_point _lastRenderTime = std::chrono::high_resolution_clock::now(); }; extern "C" { @@ -453,6 +496,25 @@ void set_morph_target_weights_ffi(void *const assetManager, fut.wait(); } +bool set_morph_animation_ffi( + void *assetManager, + EntityId asset, + const char *const entityName, + const float *const morphData, + const int *const morphIndices, + int numMorphTargets, + int numFrames, + float frameLengthInMs) { + std::packaged_task lambda( + [&] { + return set_morph_animation(assetManager, asset, entityName, morphData, morphIndices, numMorphTargets, numFrames, frameLengthInMs); + }); + auto fut = _rl->add_task(lambda); + fut.wait(); + return fut.get(); +} + + FLUTTER_PLUGIN_EXPORT bool set_bone_transform_ffi( void *assetManager, EntityId asset, @@ -466,5 +528,31 @@ FLUTTER_PLUGIN_EXPORT bool set_bone_transform_ffi( return fut.get(); } +FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose_ffi(void* const assetManager, EntityId entityId) { + std::packaged_task lambda( + [&] { return reset_to_rest_pose(assetManager, entityId); }); + auto fut = _rl->add_task(lambda); + fut.wait(); +} + +FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi( + void *assetManager, + EntityId asset, + const float *const frameData, + int numFrames, + const char *const boneName, + const char **const meshNames, + int numMeshTargets, + float frameLengthInMs, + bool isModelSpace) { + + std::packaged_task lambda( + [=] { + add_bone_animation(assetManager, asset, frameData, numFrames, boneName, meshNames, numMeshTargets, frameLengthInMs, isModelSpace); + }); + auto fut = _rl->add_task(lambda); + fut.wait(); +} + FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi() { Log("Dummy called"); } }