diff --git a/ios/include/AssetManager.hpp b/ios/include/AssetManager.hpp index f3d0dc9b..77c36bd3 100644 --- a/ios/include/AssetManager.hpp +++ b/ios/include/AssetManager.hpp @@ -92,6 +92,9 @@ namespace polyvox bool hide(EntityId entity, const char *meshName); bool reveal(EntityId entity, const char *meshName); const char *getNameForEntity(EntityId entityId); + utils::Entity findChildEntityByName( + EntityId entityId, + const char *entityName); private: AssetLoader *_assetLoader = nullptr; @@ -108,6 +111,8 @@ namespace polyvox vector _assets; tsl::robin_map _entityIdLookup; + + utils::Entity findEntityByName( SceneAsset asset, const char *entityName); diff --git a/ios/include/FlutterFilamentApi.h b/ios/include/FlutterFilamentApi.h index 83ba85f8..97230c96 100644 --- a/ios/include/FlutterFilamentApi.h +++ b/ios/include/FlutterFilamentApi.h @@ -168,6 +168,7 @@ extern "C" FLUTTER_PLUGIN_EXPORT void set_post_processing(void *const viewer, bool enabled); 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 void ios_dummy(); FLUTTER_PLUGIN_EXPORT void flutter_filament_free(void *ptr); #ifdef __cplusplus diff --git a/ios/include/FlutterFilamentFFIApi.h b/ios/include/FlutterFilamentFFIApi.h index 78431162..aa797aa4 100644 --- a/ios/include/FlutterFilamentFFIApi.h +++ b/ios/include/FlutterFilamentFFIApi.h @@ -58,6 +58,18 @@ FLUTTER_PLUGIN_EXPORT int get_animation_count_ffi(void* const assetManager, Enti FLUTTER_PLUGIN_EXPORT void get_animation_name_ffi(void* const assetManager, EntityId asset, char *const outPtr, int index); FLUTTER_PLUGIN_EXPORT void get_morph_target_name_ffi(void* const assetManager, EntityId asset, const char *meshName, char *const outPtr, int index); FLUTTER_PLUGIN_EXPORT int get_morph_target_name_count_ffi(void* const assetManager, EntityId asset, const char *meshName); +FLUTTER_PLUGIN_EXPORT void set_morph_target_weights_ffi(void* const assetManager, + EntityId asset, + const char *const entityName, + const float *const morphData, + int numWeights + ); +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 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 ios_dummy_ffi(); diff --git a/ios/src/AssetManager.cpp b/ios/src/AssetManager.cpp index 654bd74d..373dd376 100644 --- a/ios/src/AssetManager.cpp +++ b/ios/src/AssetManager.cpp @@ -280,16 +280,6 @@ namespace polyvox return _assets[pos->second].asset; } - // vector completedBoneAnimations; - - // for (int i = completed.size() - 1; i >= 0; i--) { - // auto completedAnimationIndex = completed[i]; - // if(asset.animations.mType == AnimationType::BONE) { - // completedBoneAnimations.push_back() - // } - // (completedAnimationIndex); - // } - void AssetManager::updateAnimations() { @@ -390,10 +380,13 @@ namespace polyvox } } + // TODO - we really don't want to be looking up the bone index/entity by name every single frame + // - could use findChildEntityByName + // - 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) { - Log("Setting transform for bone %s/skin %d for mesh target %s", boneName, skinIndex, entityName); + std::lock_guard lock(_animationMutex); const auto &pos = _entityIdLookup.find(entityId); if (pos == _entityIdLookup.end()) @@ -414,6 +407,11 @@ namespace polyvox const auto &renderableInstance = rm.getInstance(entity); + if(!renderableInstance.isValid()) { + Log("Invalid renderable"); + return false; + } + TransformManager &transformManager = _engine->getTransformManager(); const auto &filamentInstance = sceneAsset.asset->getInstance(); @@ -442,6 +440,7 @@ namespace polyvox return false; } + utils::Entity joint = filamentInstance->getJointsAt(skinIndex)[boneIndex]; if (joint.isNull()) @@ -577,6 +576,28 @@ namespace polyvox count); } + utils::Entity AssetManager::findChildEntityByName(EntityId entityId, const char *entityName) { + std::lock_guard lock(_animationMutex); + + const auto &pos = _entityIdLookup.find(entityId); + if (pos == _entityIdLookup.end()) + { + Log("Couldn't find asset under specified entity id."); + return utils::Entity(); + } + SceneAsset &sceneAsset = _assets[pos->second]; + + const auto entity = findEntityByName(sceneAsset, entityName); + + if(entity.isNull()) { + Log("Failed to find entity %s.", entityName); + } + + return entity; + + } + + utils::Entity AssetManager::findEntityByName(SceneAsset asset, const char *entityName) { utils::Entity entity; diff --git a/ios/src/FilamentViewer.cpp b/ios/src/FilamentViewer.cpp index d25a8a99..8c73f2a1 100644 --- a/ios/src/FilamentViewer.cpp +++ b/ios/src/FilamentViewer.cpp @@ -962,6 +962,7 @@ namespace polyvox double _elapsed = 0; int _frameCount = 0; + int _skippedFrames = 0; void FilamentViewer::render( uint64_t frameTimeInNanos, @@ -981,6 +982,7 @@ namespace polyvox // Log("1 sec average for asset animation update %f", _elapsed / 60); _elapsed = 0; _frameCount = 0; + Log("Skipped frames : %d", _skippedFrames); } Timer tmr; @@ -1023,6 +1025,8 @@ namespace polyvox } else { + _skippedFrames++; + // std::cout << "Skipped" << std::endl; // skipped frame } diff --git a/ios/src/FlutterFilamentFFIApi.cpp b/ios/src/FlutterFilamentFFIApi.cpp index fd7eee6a..59e8c945 100644 --- a/ios/src/FlutterFilamentFFIApi.cpp +++ b/ios/src/FlutterFilamentFFIApi.cpp @@ -16,24 +16,36 @@ class RenderLoop { public: explicit RenderLoop() { _t = new std::thread([this]() { + auto last = std::chrono::high_resolution_clock::now(); while (!_stop) { - { - if (_rendering) { - doRender(); - } - } - std::function task; - { + + auto now = std::chrono::high_resolution_clock::now(); + + float elapsed = float(std::chrono::duration_cast(now - last).count()); + + while(elapsed < 3 * _frameIntervalInMilliseconds / 4) { + + std::function task; std::unique_lock lock(_access); if (_tasks.empty()) { - _cond.wait_for(lock, std::chrono::duration( - _frameIntervalInMilliseconds)); + _cond.wait_for(lock, std::chrono::duration(1)); + now = std::chrono::high_resolution_clock::now(); + elapsed = float(std::chrono::duration_cast(now - last).count()); continue; } task = std::move(_tasks.front()); _tasks.pop_front(); + task(); + + now = std::chrono::high_resolution_clock::now(); + elapsed = float(std::chrono::duration_cast(now - last).count()); } - task(); + + if (_rendering) { + doRender(); + } + + last = now; } }); } @@ -428,5 +440,29 @@ get_name_for_entity_ffi(void *const assetManager, const EntityId entityId) { return fut.get(); } +void set_morph_target_weights_ffi(void *const assetManager, + EntityId asset, + const char *const entityName, + const float *const morphData, + int numWeights) { + std::packaged_task lambda( + [&] { return set_morph_target_weights(assetManager, asset, entityName, morphData, numWeights); }); + auto fut = _rl->add_task(lambda); + fut.wait(); +} + +FLUTTER_PLUGIN_EXPORT bool set_bone_transform_ffi( + void *assetManager, + EntityId asset, + const char *entityName, + const float *const transform, + const char *boneName) { + std::packaged_task lambda( + [&] { return set_bone_transform(assetManager, asset, entityName, transform, boneName); }); + auto fut = _rl->add_task(lambda); + fut.wait(); + return fut.get(); +} + FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi() { Log("Dummy called"); } } diff --git a/lib/filament_controller.dart b/lib/filament_controller.dart index 4992859e..4f25e00a 100644 --- a/lib/filament_controller.dart +++ b/lib/filament_controller.dart @@ -485,4 +485,11 @@ abstract class FilamentController { double orbitSpeedX = 0.01, double orbitSpeedY = 0.01, double zoomSpeed = 0.01}); + + /// + /// Finds the child entity named [childName] associated with the given parent. + /// Usually, [parent] will be the return value from [loadGlb]/[loadGltf] and [childName] will be the name of a node/mesh. + /// + Future getChildEntity( + FilamentEntity parent, String childName); } diff --git a/lib/filament_controller_ffi.dart b/lib/filament_controller_ffi.dart index 0f44203a..9ae5ec7a 100644 --- a/lib/filament_controller_ffi.dart +++ b/lib/filament_controller_ffi.dart @@ -125,7 +125,9 @@ class FilamentControllerFFI extends FilamentController { @override Future setFrameRate(int framerate) async { - set_frame_interval_ffi(1.0 / framerate); + final interval = 1000.0 / framerate; + set_frame_interval_ffi(interval); + dev.log("Set frame interval to $interval"); } @override @@ -596,7 +598,7 @@ class FilamentControllerFFI extends FilamentController { weightsPtr.elementAt(i).value = weights[i]; } var meshNamePtr = meshName.toNativeUtf8(allocator: calloc).cast(); - set_morph_target_weights( + set_morph_target_weights_ffi( _assetManager!, entity, meshNamePtr, weightsPtr, weights.length); calloc.free(weightsPtr); calloc.free(meshNamePtr); @@ -1178,7 +1180,7 @@ class FilamentControllerFFI extends FilamentController { var meshNamePtr = meshName.toNativeUtf8(allocator: calloc).cast(); var boneNamePtr = boneName.toNativeUtf8(allocator: calloc).cast(); - var result = set_bone_transform( + var result = set_bone_transform_ffi( _assetManager!, entity, meshNamePtr, ptr, boneNamePtr); calloc.free(ptr); @@ -1188,4 +1190,21 @@ class FilamentControllerFFI extends FilamentController { throw Exception("Failed to set bone transform. See logs for details"); } } + + @override + Future getChildEntity( + FilamentEntity parent, String childName) async { + var childNamePtr = childName.toNativeUtf8(allocator: calloc).cast(); + try { + var childEntity = + find_child_entity_by_name(_assetManager!, parent, childNamePtr); + if (childEntity == _FILAMENT_ASSET_ERROR) { + throw Exception( + "Could not find child ${childName} under the specified entity"); + } + return childEntity; + } finally { + calloc.free(childNamePtr); + } + } } diff --git a/lib/generated_bindings.dart b/lib/generated_bindings.dart index a3eea5e1..75ec197f 100644 --- a/lib/generated_bindings.dart +++ b/lib/generated_bindings.dart @@ -736,6 +736,16 @@ external ffi.Pointer get_name_for_entity( int entityId, ); +@ffi.Native< + EntityId Function( + ffi.Pointer, EntityId, ffi.Pointer)>( + symbol: 'find_child_entity_by_name', assetId: 'flutter_filament_plugin') +external int find_child_entity_by_name( + ffi.Pointer assetManager, + int parent, + ffi.Pointer name, +); + @ffi.Native( symbol: 'ios_dummy', assetId: 'flutter_filament_plugin') external void ios_dummy(); @@ -1092,6 +1102,34 @@ external int get_morph_target_name_count_ffi( ffi.Pointer meshName, ); +@ffi.Native< + ffi.Void Function(ffi.Pointer, EntityId, + ffi.Pointer, ffi.Pointer, ffi.Int)>( + symbol: 'set_morph_target_weights_ffi', assetId: 'flutter_filament_plugin') +external void set_morph_target_weights_ffi( + ffi.Pointer assetManager, + int asset, + ffi.Pointer entityName, + ffi.Pointer morphData, + int numWeights, +); + +@ffi.Native< + ffi.Bool Function( + ffi.Pointer, + EntityId, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>( + symbol: 'set_bone_transform_ffi', assetId: 'flutter_filament_plugin') +external bool set_bone_transform_ffi( + ffi.Pointer assetManager, + int asset, + ffi.Pointer entityName, + ffi.Pointer transform, + ffi.Pointer boneName, +); + @ffi.Native, ffi.Bool)>( symbol: 'set_post_processing_ffi', assetId: 'flutter_filament_plugin') external void set_post_processing_ffi(