diff --git a/example/lib/main.dart b/example/lib/main.dart index 7628d53c..3c6d9654 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -37,7 +37,7 @@ class _MyAppState extends State with SingleTickerProviderStateMixin { @override void initState() { super.initState(); - _filamentController = FilamentController(); + _filamentController = FilamentController(this); } void onClick(int index) async { @@ -117,26 +117,25 @@ class _MyAppState extends State with SingleTickerProviderStateMixin { _filamentController.setCamera(_cube!, "Camera_Orientation"); break; case 15: - throw Exception("FIXME"); - // final animation = AnimationBuilder() - // .setFramerate(30) - // .setDuration(4) - // .setNumMorphWeights(8) - // .interpolateMorphWeights(0, 4, 0, 1) - // .interpolateBoneTransform( - // "Bone.001", - // "Cube.001", - // 2, - // 4, - // v.Vector3.zero(), - // v.Vector3.zero(), - // // Vec3(x: 1, y: 1, z: 1), - // v.Quaternion(0, 0, 0, 1), - // v.Quaternion(1, 1, 1, 1)) - // // Quaternion(x: 1, y: 1, z: 1, w: 1)) - // .build(); - - // _filamentController.setAnimation(_cube!, animation); + final animation = AnimationBuilder( + controller: _filamentController, + asset: _cube!, + framerate: 30, + meshName: "Cube.001") + .setDuration(4) + .interpolateMorphWeights(0, 4, 0, 1) + // .interpolateBoneTransform( + // "Bone.001", + // "Cube.001", + // 2, + // 4, + // v.Vector3.zero(), + // v.Vector3.zero(), + // // Vec3(x: 1, y: 1, z: 1), + // v.Quaternion(0, 0, 0, 1), + // v.Quaternion(1, 1, 1, 1)) + // Quaternion(x: 1, y: 1, z: 1, w: 1)) + .set(); break; case 16: _targetNames = _filamentController.getMorphTargetNames(_cube!, "Cube"); diff --git a/ios/include/PolyvoxFilamentApi.h b/ios/include/PolyvoxFilamentApi.h index 5cd11529..792ebc3f 100644 --- a/ios/include/PolyvoxFilamentApi.h +++ b/ios/include/PolyvoxFilamentApi.h @@ -4,14 +4,12 @@ #include "ResourceBuffer.hpp" #include -#include "dart/dart_api_dl.h" typedef struct ResourceBuffer ResourceBuffer; typedef int32_t EntityId; intptr_t init_dart_api_dl(void* data); -void register_filament_port(Dart_Port port); void* create_filament_viewer(void *context, ResourceBuffer (*loadResource)(const char *), void (*freeResource)(uint32_t)); void delete_filament_viewer(void *viewer); void* get_asset_manager(void* viewer); @@ -52,7 +50,7 @@ void apply_weights( int count ); -void set_morph_animation( +bool set_morph_animation( void* assetManager, EntityId asset, const char *const entityName, diff --git a/ios/include/SceneAsset.hpp b/ios/include/SceneAsset.hpp index 1da592ab..d0d68fed 100644 --- a/ios/include/SceneAsset.hpp +++ b/ios/include/SceneAsset.hpp @@ -37,7 +37,7 @@ namespace polyvox { bool mLoop = false; bool mReverse = false; float mDuration = 0; - int mFrameNumber = -1; + bool mAnimating = false; // AnimationStatus() { // Log("default constr"); @@ -73,7 +73,7 @@ namespace polyvox { // Use this to manually construct a buffer of frame data for morph animations. // struct MorphAnimationBuffer { - utils::EntityInstance* mInstance = nullptr; + utils::Entity mMeshTarget; int mNumFrames = -1; float mFrameLengthInMs = 0; vector mFrameData; @@ -124,38 +124,7 @@ namespace polyvox { float mScale = 1; - // SceneAsset(const SceneAsset&& a) { - // Log("MOVE"); - // } - - - // SceneAsset(const SceneAsset& a) { - // mAsset = a.mAsset; - // mAnimator = a.mAnimator; - // mAnimations = a.mAnimations; - // mMorphAnimationBuffer = a.mMorphAnimationBuffer; - // mBoneAnimationBuffer = a.mBoneAnimationBuffer; - // mTexture = a.mTexture; - // mPosition = a.mPosition; - // mRotation = a.mRotation; - // mScale = a.mScale; - // } - - - // SceneAsset& operator=(SceneAsset a) { - // mAsset = a.mAsset; - // mAnimator = a.mAnimator; - // mAnimations = a.mAnimations; - // mMorphAnimationBuffer = a.mMorphAnimationBuffer; - // mBoneAnimationBuffer = a.mBoneAnimationBuffer; - // mTexture = a.mTexture; - // mPosition = a.mPosition; - // mRotation = a.mRotation; - // mScale = a.mScale; - // return *this; - // } - - SceneAsset( + SceneAsset( FilamentAsset* asset ) : mAsset(asset) { mAnimator = mAsset->getInstance()->getAnimator(); diff --git a/ios/src/AssetManager.cpp b/ios/src/AssetManager.cpp index b90005ab..7bc06f84 100644 --- a/ios/src/AssetManager.cpp +++ b/ios/src/AssetManager.cpp @@ -91,11 +91,6 @@ EntityId AssetManager::loadGltf(const char *uri, string(relativeResourcePath) + string("/") + string(resourceUris[i]); ResourceBuffer buf = _loadResource(uri.c_str()); - // using FunctionCallback = std::function; auto cb = [&] (void * ptr, unsigned int len, void * misc) { - // }; - // FunctionCallback fcb = cb; - ResourceLoader::BufferDescriptor b(buf.data, buf.size); _gltfResourceLoader->addResourceData(resourceUris[i], std::move(b)); _freeResource(buf.id); @@ -223,39 +218,44 @@ void AssetManager::updateAnimations() { if(!asset.mAnimating) { continue; } - asset.mAnimating = false; - // // morph animation - // AnimationStatus morphAnimation = asset.mAnimations[0]; - // auto elapsed = (now - morphAnimation.mStart).count(); + // dynamically constructed morph animation + AnimationStatus& morphAnimation = asset.mAnimations[0]; - // int lengthInFrames = static_cast(morphAnimation.mDuration / asset.mMorphAnimationBuffer.mFrameLengthInMs); + if(morphAnimation.mAnimating) { + + auto elapsed = float( + std::chrono::duration_cast( + now - morphAnimation.mStart + ).count()) / 1000.0f; + int lengthInFrames = static_cast( + morphAnimation.mDuration * 1000.0f / + asset.mMorphAnimationBuffer.mFrameLengthInMs + ); - // if(elapsed >= morphAnimation.mDuration) { - // if(morphAnimation.mLoop) { - // morphAnimation.mStart = now; - // if(morphAnimation.mReverse) { - // morphAnimation.mFrameNumber = lengthInFrames; - // } - // asset.mAnimating = true; - // } else { - // morphAnimation.mStart = time_point_t::max(); - // } - // } else { - // asset.mAnimating = true; - // } - - // int frameNumber = static_cast(elapsed / asset.mMorphAnimationBuffer.mFrameLengthInMs); - // if(frameNumber < lengthInFrames) { - // if(morphAnimation.mReverse) { - // frameNumber = lengthInFrames - frameNumber; - // } - // rm.setMorphWeights( - // *(asset.mMorphAnimationBuffer.mInstance), - // asset.mMorphAnimationBuffer.mFrameData.data() + (morphAnimation.mFrameNumber * asset.mMorphAnimationBuffer.mNumMorphWeights), - // asset.mMorphAnimationBuffer.mNumMorphWeights); - // } + // if more time has elapsed than the animation duration && not looping + // mark the animation as complete + if(elapsed >= morphAnimation.mDuration && !morphAnimation.mLoop) { + morphAnimation.mStart = time_point_t::max(); + morphAnimation.mAnimating = false; + } else { + + asset.mAnimating = true; + int frameNumber = static_cast(elapsed * 1000.0f / asset.mMorphAnimationBuffer.mFrameLengthInMs) % lengthInFrames; + // offset from the end if reverse + if(morphAnimation.mReverse) { + frameNumber = lengthInFrames - frameNumber; + } + + Log("setting weights for framenumber %d, elapsed is %f, lengthInFrames is %d, mNumMorphWeights %d", frameNumber, elapsed, lengthInFrames,asset.mMorphAnimationBuffer.mNumMorphWeights ); + // set the weights appropriately + rm.setMorphWeights( + rm.getInstance(asset.mMorphAnimationBuffer.mMeshTarget), + asset.mMorphAnimationBuffer.mFrameData.data() + (frameNumber * asset.mMorphAnimationBuffer.mNumMorphWeights), + asset.mMorphAnimationBuffer.mNumMorphWeights); + } + } // // bone animation // AnimationStatus boneAnimation = asset.mAnimations[1]; @@ -292,35 +292,35 @@ void AssetManager::updateAnimations() { // GLTF animations - Animator* animator = asset.mAnimator; - - for(int j = 2; j < asset.mAnimations.size(); j++) { - - AnimationStatus& anim = asset.mAnimations[j]; + int j = -1; + for(AnimationStatus& anim : asset.mAnimations) { + j++; + if(j < 2) { + continue; + } + + if(!anim.mAnimating) { + continue; + } auto elapsed = float(std::chrono::duration_cast(now - anim.mStart).count()) / 1000.0f; - if(elapsed < anim.mDuration) { - if(anim.mLoop) { - animator->applyAnimation(j-2, anim.mDuration - elapsed); - } else { - animator->applyAnimation(j-2, elapsed); - } - asset.mAnimating = true; - } else if(anim.mLoop) { - animator->applyAnimation(j-2, float(elapsed) ); //% anim.mDuration + if(anim.mLoop || elapsed < anim.mDuration) { + asset.mAnimator->applyAnimation(j-2, elapsed); asset.mAnimating = true; } else if(elapsed - anim.mDuration < 0.3) { // cross-fade // animator->applyCrossFade(j-2, anim.mDuration - 0.05, elapsed / 0.3); // asset.mAnimating = true; - anim.mStart = time_point_t::max(); + // anim.mStart = time_point_t::max(); } else { // stop anim.mStart = time_point_t::max(); } } - asset.mAnimator->updateBoneMatrices(); + if(asset.mAnimating) { + asset.mAnimator->updateBoneMatrices(); + } } } @@ -379,27 +379,33 @@ bool AssetManager::setMorphAnimationBuffer( Log("ERROR: asset not found for entity."); return false; } - auto asset = _assets[pos->second]; + auto& asset = _assets[pos->second]; auto entity = findEntityByName(asset, entityName); if(!entity) { Log("Warning: failed to find entity %s", entityName); return false; } - RenderableManager &rm = _engine->getRenderableManager(); - auto inst = rm.getInstance(entity); - - asset.mMorphAnimationBuffer.mInstance = &inst; - asset.mMorphAnimationBuffer.mNumFrames = numFrames; - asset.mMorphAnimationBuffer.mFrameLengthInMs = frameLengthInMs; + + asset.mMorphAnimationBuffer.mMeshTarget = entity; asset.mMorphAnimationBuffer.mFrameData.clear(); asset.mMorphAnimationBuffer.mFrameData.insert( asset.mMorphAnimationBuffer.mFrameData.begin(), morphData, morphData + (numFrames * numMorphWeights) ); - + asset.mMorphAnimationBuffer.mFrameLengthInMs = frameLengthInMs; asset.mMorphAnimationBuffer.mNumMorphWeights = numMorphWeights; + + AnimationStatus& animation = asset.mAnimations[0]; + animation.mDuration = (frameLengthInMs * numFrames) / 1000.0f; + animation.mStart = high_resolution_clock::now(); + animation.mAnimating = true; + asset.mAnimating = true; + Log("set start to %d, dur is %f", + std::chrono::duration_cast(animation.mStart.time_since_epoch()).count(), + animation.mDuration + ); return true; } @@ -524,13 +530,12 @@ void AssetManager::playAnimation(EntityId e, int index, bool loop, bool reverse) return; } auto& asset = _assets[pos->second]; - - Log("prev start %d", std::chrono::duration_cast(asset.mAnimations[index+2].mStart.time_since_epoch()).count()); + asset.mAnimations[index+2].mAnimating = true; asset.mAnimations[index+2].mStart = std::chrono::high_resolution_clock::now(); asset.mAnimations[index+2].mLoop = loop; asset.mAnimations[index+2].mReverse = reverse; - Log("new start %d", std::chrono::duration_cast(asset.mAnimations[index+2].mStart.time_since_epoch()).count()); + // Log("new start %d, dur is %f", std::chrono::duration_cast(asset.mAnimations[index+2].mStart.time_since_epoch()).count(), asset.mAnimations[index+2].mDuration); asset.mAnimating = true; } diff --git a/ios/src/FilamentViewer.cpp b/ios/src/FilamentViewer.cpp index 2b22b6ea..feade990 100644 --- a/ios/src/FilamentViewer.cpp +++ b/ios/src/FilamentViewer.cpp @@ -790,7 +790,7 @@ void FilamentViewer::render(uint64_t frameTimeInNanos) { } if(_frameCount == 60) { - // Log("1 sec average for asset animation update %f", _elapsed); + Log("1 sec average for asset animation update %f", _elapsed); _elapsed = 0; _frameCount = 0; } diff --git a/ios/src/PolyvoxFilamentApi.cpp b/ios/src/PolyvoxFilamentApi.cpp index d607b9b6..e6d3fd76 100644 --- a/ios/src/PolyvoxFilamentApi.cpp +++ b/ios/src/PolyvoxFilamentApi.cpp @@ -5,54 +5,35 @@ #include "Log.hpp" #include "ThreadPool.hpp" #include -#include "dart/dart_api_dl.h" using namespace polyvox; #define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default"))) -static ThreadPool* _tp; -static Dart_Port _port; +// static ThreadPool* _tp; extern "C" { #include "PolyvoxFilamentApi.h" - FLUTTER_PLUGIN_EXPORT intptr_t init_dart_api_dl(void* data) { - return Dart_InitializeApiDL(data); - } - - FLUTTER_PLUGIN_EXPORT void register_filament_port(Dart_Port port) { - _port = port; - } - FLUTTER_PLUGIN_EXPORT void* create_filament_viewer(void* context, ResourceBuffer (*loadResource)(char const*), void (*freeResource)(unsigned int)) { - if(!_tp) { - _tp = new ThreadPool(); - } - std::packaged_task lambda([=]() mutable { + // if(!_tp) { + // _tp = new ThreadPool(); + // } + // //std::packaged_task lambda([=]() mutable { return (void*) new FilamentViewer(context, loadResource, freeResource); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); - return fut.get(); + // }); + // auto fut = _tp->add_task(lambda); + // fut.wait(); + // //return fut.get(); } - // Dart_CObject dart_object; - // dart_object.type = Dart_CObject_kInt64; - // dart_object.value.as_int64 = (size_t)viewer; - - // const bool result = Dart_PostCObject_DL(_port, &dart_object); - // if (!result) { - // std::cout << "TTS: C : Posting message to port failed." << std::endl; - // } - FLUTTER_PLUGIN_EXPORT void create_render_target(void* viewer, uint32_t textureId, uint32_t width, uint32_t height) { - std::packaged_task lambda([=]() mutable { + // //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->createRenderTarget(textureId, width, height); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + // }); + // auto fut = _tp->add_task(lambda); + // fut.wait(); } FLUTTER_PLUGIN_EXPORT void delete_filament_viewer(void* viewer) { @@ -60,270 +41,271 @@ extern "C" { } FLUTTER_PLUGIN_EXPORT void set_background_color(void* viewer, const float r, const float g, const float b, const float a) { - std::packaged_task lambda([=]() mutable { + // //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->setBackgroundColor(r, g, b, a); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + // }); + // auto fut = _tp->add_task(lambda); + // fut.wait(); } FLUTTER_PLUGIN_EXPORT void clear_background_image(void* viewer) { - std::packaged_task lambda([=]() mutable { + // //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->clearBackgroundImage(); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + // }); + // auto fut = _tp->add_task(lambda); + // fut.wait(); } FLUTTER_PLUGIN_EXPORT void set_background_image(void* viewer, const char* path) { - std::packaged_task lambda([=]() mutable { + // //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->setBackgroundImage(path); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void set_background_image_position(void* viewer, float x, float y, bool clamp) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->setBackgroundImagePosition(x, y, clamp); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void load_skybox(void* viewer, const char* skyboxPath) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->loadSkybox(skyboxPath); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void load_ibl(void* viewer, const char* iblPath, float intensity) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->loadIbl(iblPath, intensity); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void remove_skybox(void* viewer) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->removeSkybox(); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void remove_ibl(void* viewer) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->removeIbl(); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT EntityId add_light(void* viewer, uint8_t type, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { return ((FilamentViewer*)viewer)->addLight((LightManager::Type)type, colour, intensity, posX, posY, posZ, dirX, dirY, dirZ, shadows); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); - return fut.get(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); + //return fut.get(); } FLUTTER_PLUGIN_EXPORT void remove_light(void* viewer, int32_t entityId) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->removeLight(entityId); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void clear_lights(void* viewer) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->clearLights(); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT EntityId load_glb(void* assetManager, const char* assetPath, bool unlit) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { return ((AssetManager*)assetManager)->loadGlb(assetPath, unlit); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); - return fut.get(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); + //return fut.get(); } FLUTTER_PLUGIN_EXPORT EntityId load_gltf(void* assetManager, const char* assetPath, const char* relativePath) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { return ((AssetManager*)assetManager)->loadGltf(assetPath, relativePath); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); - return fut.get(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); + //return fut.get(); } FLUTTER_PLUGIN_EXPORT bool set_camera(void* viewer, EntityId asset, const char* nodeName) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { return ((FilamentViewer*)viewer)->setCamera(asset, nodeName); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); - return fut.get(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); + //return fut.get(); } FLUTTER_PLUGIN_EXPORT void set_camera_exposure(void* viewer, float aperture, float shutterSpeed, float sensitivity) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->setCameraExposure(aperture, shutterSpeed, sensitivity); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void set_camera_position(void* viewer, float x, float y, float z) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->setCameraPosition(x, y, z); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void set_camera_rotation(void* viewer, float rads, float x, float y, float z) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->setCameraRotation(rads, x, y, z); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void set_camera_model_matrix(void* viewer, const float* const matrix) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->setCameraModelMatrix(matrix); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void set_camera_focal_length(void* viewer, float focalLength) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->setCameraFocalLength(focalLength); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void render( void* viewer, uint64_t frameTimeInNanos ) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->render(frameTimeInNanos); - }); - _tp->add_task(lambda); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void set_frame_interval( void* viewer, float frameInterval ) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->setFrameInterval(frameInterval); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void destroy_swap_chain(void* viewer) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->destroySwapChain(); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void create_swap_chain(void* viewer, void* surface=nullptr, uint32_t width=0, uint32_t height=0) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->createSwapChain(surface, width, height); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void* get_renderer(void* viewer) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { return ((FilamentViewer*)viewer)->getRenderer(); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); - return fut.get(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); + //return fut.get(); } FLUTTER_PLUGIN_EXPORT void update_viewport_and_camera_projection(void* viewer, int width, int height, float scaleFactor) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { return ((FilamentViewer*)viewer)->updateViewportAndCameraProjection(width, height, scaleFactor); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void scroll_update(void* viewer, float x, float y, float delta) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->scrollUpdate(x, y, delta); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void scroll_begin(void* viewer) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->scrollBegin(); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void scroll_end(void* viewer) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->scrollEnd(); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void grab_begin(void* viewer, float x, float y, bool pan) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->grabBegin(x, y, pan); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void grab_update(void* viewer, float x, float y) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->grabUpdate(x, y); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void grab_end(void* viewer) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->grabEnd(); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void* get_asset_manager(void* viewer) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { return (void*)((FilamentViewer*)viewer)->getAssetManager(); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); - return fut.get(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); + //return fut.get(); } FLUTTER_PLUGIN_EXPORT void apply_weights( @@ -332,14 +314,14 @@ extern "C" { const char* const entityName, float* const weights, int count) { - // std::packaged_task lambda([=]() mutable { + // //std::packaged_task lambda([=]() mutable { // ((AssetManager*)assetManager)->setMorphTargetWeights(asset, entityName, weights, count); // }); // auto fut = _tp->add_task(lambda); // fut.wait(); } - FLUTTER_PLUGIN_EXPORT void set_morph_animation( + FLUTTER_PLUGIN_EXPORT bool set_morph_animation( void* assetManager, EntityId asset, const char* const entityName, @@ -348,8 +330,8 @@ extern "C" { int numFrames, float frameLengthInMs) { - std::packaged_task lambda([=]() mutable { - ((AssetManager*)assetManager)->setMorphAnimationBuffer( + //std::packaged_task lambda([=]() mutable { + return ((AssetManager*)assetManager)->setMorphAnimationBuffer( asset, entityName, morphData, @@ -357,9 +339,9 @@ extern "C" { numFrames, frameLengthInMs ); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void set_bone_animation( @@ -371,7 +353,7 @@ extern "C" { const float* const frameData, int numFrames, float frameLengthInMs) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((AssetManager*)assetManager)->setBoneAnimationBuffer( asset, length, @@ -381,9 +363,9 @@ extern "C" { numFrames, frameLengthInMs ); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } @@ -423,12 +405,12 @@ extern "C" { bool loop, bool reverse) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { std::cout << "Playing animation" << std::endl; ((AssetManager*)assetManager)->playAnimation(asset, index, loop, reverse); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void set_animation_frame( @@ -436,7 +418,7 @@ extern "C" { EntityId asset, int animationIndex, int animationFrame) { - // std::packaged_task lambda([=]() mutable { + // //std::packaged_task lambda([=]() mutable { // ((AssetManager*)assetManager)->setAnimationFrame(asset, animationIndex, animationFrame); // }); // auto fut = _tp->add_task(lambda); @@ -446,13 +428,13 @@ extern "C" { FLUTTER_PLUGIN_EXPORT int get_animation_count( void* assetManager, EntityId asset) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { auto names = ((AssetManager*)assetManager)->getAnimationNames(asset); return names->size(); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); - return fut.get(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); + //return fut.get(); } FLUTTER_PLUGIN_EXPORT void get_animation_name( @@ -461,49 +443,49 @@ extern "C" { char* const outPtr, int index ) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { auto names = ((AssetManager*)assetManager)->getAnimationNames(asset); string name = names->at(index); strcpy(outPtr, name.c_str()); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT int get_morph_target_name_count(void* assetManager, EntityId asset, const char* meshName) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { unique_ptr> names = ((AssetManager*)assetManager)->getMorphTargetNames(asset, meshName); return names->size(); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); - return fut.get(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); + //return fut.get(); } FLUTTER_PLUGIN_EXPORT void get_morph_target_name(void* assetManager, EntityId asset, const char* meshName, char* const outPtr, int index ) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { unique_ptr> names = ((AssetManager*)assetManager)->getMorphTargetNames(asset, meshName); string name = names->at(index); strcpy(outPtr, name.c_str()); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void remove_asset(void* viewer, EntityId asset) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->removeAsset(asset); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void clear_assets(void* viewer) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((FilamentViewer*)viewer)->clearAssets(); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void load_texture(void* assetManager, EntityId asset, const char* assetPath, int renderableIndex) { @@ -515,43 +497,43 @@ extern "C" { } FLUTTER_PLUGIN_EXPORT void transform_to_unit_cube(void* assetManager, EntityId asset) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((AssetManager*)assetManager)->transformToUnitCube(asset); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void set_position(void* assetManager, EntityId asset, float x, float y, float z) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((AssetManager*)assetManager)->setPosition(asset, x, y, z); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void set_rotation(void* assetManager, EntityId asset, float rads, float x, float y, float z) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((AssetManager*)assetManager)->setRotation(asset, rads, x, y, z); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void set_scale(void* assetManager, EntityId asset, float scale) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((AssetManager*)assetManager)->setScale(asset, scale); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } FLUTTER_PLUGIN_EXPORT void stop_animation(void* assetManager, EntityId asset, int index) { - std::packaged_task lambda([=]() mutable { + //std::packaged_task lambda([=]() mutable { ((AssetManager*)assetManager)->stopAnimation(asset, index); - }); - auto fut = _tp->add_task(lambda); - fut.wait(); + //}); +// auto fut = _tp->add_task(lambda); +// fut.wait(); } } diff --git a/lib/animations/animation_builder.dart b/lib/animations/animation_builder.dart index 1cb48b0b..4df2a40a 100644 --- a/lib/animations/animation_builder.dart +++ b/lib/animations/animation_builder.dart @@ -1,14 +1,15 @@ import 'package:polyvox_filament/animations/animations.dart'; +import 'package:polyvox_filament/filament_controller.dart'; import 'package:tuple/tuple.dart'; import 'package:flutter/foundation.dart'; import 'package:vector_math/vector_math.dart'; class AnimationBuilder { + final FilamentController controller; DartBoneAnimation? dartBoneAnimation; double _frameLengthInMs = 0; double _duration = 0; - int _numMorphWeights = 0; double? _interpMorphStart; double? _interpMorphEnd; @@ -17,14 +18,26 @@ class AnimationBuilder { List? _dartBoneAnimations = null; - Tuple2> build( - String meshName, List morphNames) { - if (_numMorphWeights == 0 || _duration == 0 || _frameLengthInMs == 0) + FilamentEntity asset; + String meshName; + late List morphNames; + + AnimationBuilder( + {required this.controller, + required this.asset, + required this.meshName, + required int framerate}) { + _frameLengthInMs = 1000 / framerate; + morphNames = controller.getMorphTargetNames(asset, meshName); + } + + void set() { + if (morphNames.isEmpty == 0 || _duration == 0 || _frameLengthInMs == 0) throw Exception(); int numFrames = _duration * 1000 ~/ _frameLengthInMs; - final morphData = Float32List((numFrames * _numMorphWeights).toInt()); + final morphData = Float32List((numFrames * morphNames.length).toInt()); var frameStart = (_interpMorphStart! * 1000) ~/ _frameLengthInMs; var frameEnd = (_interpMorphEnd! * 1000) ~/ _frameLengthInMs; @@ -34,21 +47,17 @@ class AnimationBuilder { var val = ((1 - linear) * _interpMorphStartValue!) + (linear * _interpMorphEndValue!); - for (int j = 0; j < _numMorphWeights; j++) { - morphData[(i * _numMorphWeights) + j] = val; + for (int j = 0; j < morphNames.length; j++) { + morphData[(i * morphNames.length) + j] = val; } } var morphAnimation = MorphAnimation(meshName, morphData, morphNames, _frameLengthInMs); - - return Tuple2>( - morphAnimation, _dartBoneAnimations!); - } - - AnimationBuilder setFramerate(int framerate) { - _frameLengthInMs = 1000 / framerate; - return this; + print("SETTING!"); + controller.setMorphAnimation(asset, morphAnimation); + // return Tuple2>( + // morphAnimation, _dartBoneAnimations!); } AnimationBuilder setDuration(double secs) { @@ -56,11 +65,6 @@ class AnimationBuilder { return this; } - AnimationBuilder setNumMorphWeights(int numMorphWeights) { - _numMorphWeights = numMorphWeights; - return this; - } - AnimationBuilder interpolateMorphWeights( double start, double end, double startValue, double endValue) { this._interpMorphStart = start; diff --git a/lib/animations/animations.dart b/lib/animations/animations.dart index e9976b01..41d1edd6 100644 --- a/lib/animations/animations.dart +++ b/lib/animations/animations.dart @@ -24,7 +24,9 @@ class MorphAnimation { final Float32List data; MorphAnimation( - this.meshName, this.data, this.morphNames, this.frameLengthInMs); + this.meshName, this.data, this.morphNames, this.frameLengthInMs) { + assert(data.length == morphNames.length * numFrames); + } int get numMorphWeights => morphNames.length; diff --git a/lib/filament_controller.dart b/lib/filament_controller.dart index e9fc3cab..d0d81fb4 100644 --- a/lib/filament_controller.dart +++ b/lib/filament_controller.dart @@ -41,7 +41,8 @@ class FilamentController { bool _rendering = false; - final _port = ReceivePort(); + final TickerProvider _tickerProvider; + Ticker? _ticker; /// /// This now uses an FFI implementation. @@ -49,7 +50,7 @@ class FilamentController { /// All other methods directly invoke the FFI functions defined in PolyvoxFilamentApi.cpp, /// which itself uses a threadpool so that calls are run on a separate thread. /// - FilamentController() { + FilamentController(this._tickerProvider) { _channel.setMethodCallHandler((call) async { throw Exception("Unknown method channel invocation ${call.method}"); }); @@ -75,7 +76,6 @@ class FilamentController { void render() { _nativeLibrary.render(_viewer, 0); - _channel.invokeMethod("onFrameAvailable"); } Future setFrameRate(int framerate) async { @@ -87,12 +87,6 @@ class FilamentController { } Future createViewer(int width, int height) async { - // if (_viewer.address == 0) { - // throw Exception("TODO"); - // } - _nativeLibrary.init_dart_api_dl(NativeApi.initializeApiDLData); - _nativeLibrary.register_filament_port(_port.sendPort.nativePort); - size = ui.Size(width * _pixelRatio, height * _pixelRatio); _textureId = await _channel.invokeMethod("createTexture", [size.width, size.height]); @@ -123,17 +117,32 @@ class FilamentController { _initialized.complete(true); _assetManager = _nativeLibrary.get_asset_manager(_viewer); - await _channel.invokeMethod("setRenderTicker", _viewer.address); + // await _channel.invokeMethod("setRenderTicker", _viewer.address); + + _ticker = _tickerProvider.createTicker((elapsed) async { + _nativeLibrary.render(_viewer, 0); + await _channel.invokeMethod("tick"); + }); + _ticker!.start(); } Future resize(int width, int height, {double contentScaleFactor = 1.0}) async { - size = ui.Size(width * _pixelRatio, height * _pixelRatio); + // await setRendering(false); + // _textureIdController.add(null); + // _nativeLibrary.destroy_swap_chain(_viewer); + // size = ui.Size(width * _pixelRatio, height * _pixelRatio); - _textureId = await _channel.invokeMethod("resize", - [width * _pixelRatio, height * _pixelRatio, contentScaleFactor]); + // _textureId = await _channel.invokeMethod("resize", + // [width * _pixelRatio, height * _pixelRatio, contentScaleFactor]); - _textureIdController.add(_textureId); + // _textureIdController.add(_textureId); + // _nativeLibrary.create_swap_chain(_viewer, nullptr, width, height); + // _nativeLibrary.create_render_target( + // _viewer, await _channel.invokeMethod("getGlTextureId"), width, height); + // _nativeLibrary.update_viewport_and_camera_projection( + // _viewer, width, height, contentScaleFactor); + // await setRendering(true); } void clearBackgroundImage() async { @@ -362,6 +371,7 @@ class FilamentController { void playAnimation(FilamentEntity asset, int index, {bool loop = false, bool reverse = false}) async { + print("LOOP $loop"); _nativeLibrary.play_animation( _assetManager, asset, index, loop ? 1 : 0, reverse ? 1 : 0); } diff --git a/lib/filament_widget.dart b/lib/filament_widget.dart index a01e3df4..b601d8b4 100644 --- a/lib/filament_widget.dart +++ b/lib/filament_widget.dart @@ -103,9 +103,9 @@ class _FilamentWidgetState extends State { width: constraints.maxWidth, child: ResizeObserver( onResized: (Size oldSize, Size newSize) async { - setState(() { - _resizing = true; - }); + // setState(() { + // _resizing = true; + // }); await widget.controller.resize( newSize.width.toInt(), newSize.height.toInt()); diff --git a/lib/generated_bindings.dart b/lib/generated_bindings.dart index db05c9dd..1094dbc3 100644 --- a/lib/generated_bindings.dart +++ b/lib/generated_bindings.dart @@ -33,20 +33,6 @@ class NativeLibrary { late final _init_dart_api_dl = _init_dart_api_dlPtr.asFunction)>(); - void register_filament_port( - int port, - ) { - return _register_filament_port( - port, - ); - } - - late final _register_filament_portPtr = - _lookup>( - 'register_filament_port'); - late final _register_filament_port = - _register_filament_portPtr.asFunction(); - ffi.Pointer create_filament_viewer( ffi.Pointer context, ffi.Pointer< @@ -79,16 +65,6 @@ class NativeLibrary { ResourceBuffer Function(ffi.Pointer)>>, ffi.Pointer>)>(); - renderCallback make_render_callback() { - return _make_render_callback(); - } - - late final _make_render_callbackPtr = - _lookup>( - 'make_render_callback'); - late final _make_render_callback = - _make_render_callbackPtr.asFunction(); - void delete_filament_viewer( ffi.Pointer viewer, ) { @@ -648,7 +624,7 @@ class NativeLibrary { void Function(ffi.Pointer, int, ffi.Pointer, ffi.Pointer, int)>(); - void set_morph_animation( + int set_morph_animation( ffi.Pointer assetManager, int asset, ffi.Pointer entityName, @@ -670,7 +646,7 @@ class NativeLibrary { late final _set_morph_animationPtr = _lookup< ffi.NativeFunction< - ffi.Void Function( + ffi.Int Function( ffi.Pointer, EntityId, ffi.Pointer, @@ -679,7 +655,7 @@ class NativeLibrary { ffi.Int, ffi.Float)>>('set_morph_animation'); late final _set_morph_animation = _set_morph_animationPtr.asFunction< - void Function(ffi.Pointer, int, ffi.Pointer, + int Function(ffi.Pointer, int, ffi.Pointer, ffi.Pointer, int, int, double)>(); void set_bone_animation( @@ -1159,8 +1135,6 @@ class ResourceBuffer extends ffi.Struct { external int id; } -typedef renderCallback = ffi.Pointer< - ffi.NativeFunction viewer)>>; typedef EntityId = ffi.Int32; const int _STDINT_H = 1; diff --git a/linux/polyvox_filament_plugin.cc b/linux/polyvox_filament_plugin.cc index 748ddce1..4b1957ae 100644 --- a/linux/polyvox_filament_plugin.cc +++ b/linux/polyvox_filament_plugin.cc @@ -83,36 +83,16 @@ static FlMethodResponse* _create_texture(PolyvoxFilamentPlugin* self, FlMethodCa return FL_METHOD_RESPONSE(fl_method_success_response_new(result)); } - -// TODO - defer the destruction of the old texture until this call has returned -// otherwise the front-end may render using the destroyed texture ID for a single frame (hopefully no more) static FlMethodResponse* _resize(PolyvoxFilamentPlugin* self, FlMethodCall* method_call) { FlValue* args = fl_method_call_get_args(method_call); const double width = fl_value_get_float(fl_value_get_list_value(args, 0)); const double height = fl_value_get_float(fl_value_get_list_value(args, 1)); - if(!self->resizing && (width != self->width || height != self->height)) { - // self->rendering = false; - // self->resizing = true; - - // // destroy_swap_chain(self->viewer); - - // destroy_filament_texture(self->texture, self->texture_registrar); - - // self->texture = create_filament_texture(uint32_t(width), uint32_t(height), self->texture_registrar); - - // create_swap_chain(self->viewer, nullptr, width, height); - // create_render_target(self->viewer, ((FilamentTextureGL*)self->texture)->texture_id,width,height); - - // update_viewport_and_camera_projection(self->viewer, width, height, 1.0f); - - // std::cout << "Created new texture " << self->texture << std::endl; - - // self->resizing = false; - // self->rendering = true; - } + destroy_filament_texture(self->texture, self->texture_registrar); + self->texture = create_filament_texture(uint32_t(width), uint32_t(height), self->texture_registrar); + g_autoptr(FlValue) result = fl_value_new_int(reinterpret_cast(self->texture)); @@ -130,8 +110,9 @@ static void polyvox_filament_plugin_handle_method_call( const gchar* method = fl_method_call_get_name(method_call); if(strcmp(method, "createTexture") == 0) { - Log("THREAD ID %d", std::this_thread::get_id()); response = _create_texture(self, method_call); + } else if(strcmp(method, "resize") == 0) { + response = _resize(self, method_call); } else if(strcmp(method, "getContext") == 0) { g_autoptr(FlValue) result = fl_value_new_int(reinterpret_cast(glXGetCurrentContext())); @@ -151,6 +132,10 @@ static void polyvox_filament_plugin_handle_method_call( } else if(strcmp(method, "setRendering") == 0) { self->rendering = fl_value_get_bool(fl_method_call_get_args(method_call)); response = FL_METHOD_RESPONSE(fl_method_success_response_new(fl_value_new_string("OK"))); + } else if(strcmp(method, "tick") == 0) { + fl_texture_registrar_mark_texture_frame_available(self->texture_registrar, + self->texture); + response = FL_METHOD_RESPONSE(fl_method_success_response_new(fl_value_new_string("OK"))); } else if(strcmp(method, "setRenderTicker") == 0) { if(self->viewer) { Log("Ticker has already been set, ignoring");