diff --git a/android/src/main/cpp/filament_android.cpp b/android/src/main/cpp/filament_android.cpp index 51dab44f..55340c27 100644 --- a/android/src/main/cpp/filament_android.cpp +++ b/android/src/main/cpp/filament_android.cpp @@ -7,44 +7,69 @@ #include #include +#include + using namespace polyvox; using namespace std; static AAssetManager* am; -static vector _assets; -uint64_t id = -1; +static map _apk_assets; +static map _file_assets; +static uint32_t _i = 0; static ResourceBuffer loadResource(const char* name) { - id++; - - AAsset *asset = AAssetManager_open(am, name, AASSET_MODE_BUFFER); - if(asset == nullptr) { - __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Couldn't locate asset [ %s ]", name); - return ResourceBuffer(nullptr, 0, 0); + + string name_str(name); + auto id = _i++; + + if (name_str.rfind("file://", 0) == 0) { + streampos length; + ifstream is(name_str.substr(7), ios::binary); + is.seekg (0, ios::end); + length = is.tellg(); + char * buffer; + buffer = new char [length]; + is.seekg (0, ios::beg); + is.read (buffer, length); + is.close(); + _file_assets[id] = buffer; + return ResourceBuffer(buffer, length, id); + } else { + AAsset *asset = AAssetManager_open(am, name, AASSET_MODE_BUFFER); + if(asset == nullptr) { + __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Couldn't locate asset [ %s ]", name); + return ResourceBuffer(nullptr, 0, 0); + } + __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Loading asset [ %s ]", name); + + off_t length = AAsset_getLength(asset); + const void * buffer = AAsset_getBuffer(asset); + + __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Read [ %lu ] bytes into buffer", length); + + _apk_assets[id] = asset; + __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Loaded asset [ %s ] of length %zu at index %d", name, length, id); + return ResourceBuffer(buffer, length, id); } - __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Loading asset [ %s ]", name); - off_t length = AAsset_getLength(asset); - const void * buffer = AAsset_getBuffer(asset); - - uint8_t *buf = new uint8_t[length ]; - memcpy(buf,buffer, length); - __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Read [ %lu ] bytes into buffer", length); - _assets.push_back(asset); - __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Loaded asset [ %s ] of length %zu", name, length); - return ResourceBuffer(buf, length, id); - } static void freeResource(uint32_t id) { __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Freeing loaded resource at index [ %d ] ", id); - AAsset* asset = _assets[id]; - if(asset) { - AAsset_close(asset); + auto apk_it = _apk_assets.find(id); + if (apk_it != _apk_assets.end()) { + AAsset_close(apk_it->second); + __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Closed Android asset"); } else { - __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Attempting to free resource at index [ %d ] that has already been released.", id); + auto file_it = _file_assets.find(id); + if (file_it != _file_assets.end()) { + free(file_it->second); + __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Freed asset from filesystem."); + } else { + __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "FATAL - could not find Android or filesystem (hot reload) asset under id %d", id); + } } - _assets[id] = nullptr; + } extern "C" { diff --git a/android/src/main/kotlin/app/polyvox/filament/FilamentInterop.kt b/android/src/main/kotlin/app/polyvox/filament/FilamentInterop.kt index 156117f3..3bd72b54 100644 --- a/android/src/main/kotlin/app/polyvox/filament/FilamentInterop.kt +++ b/android/src/main/kotlin/app/polyvox/filament/FilamentInterop.kt @@ -78,7 +78,8 @@ interface FilamentInterop : Library { fun set_background_image(viewer:Pointer, path:String); - fun set_texture(asset:Pointer, path:String, renderableIndex:Int); + fun load_texture(asset:Pointer, path:String, renderableIndex:Int); + fun set_texture(asset:Pointer); fun transform_to_unit_cube(asset:Pointer); diff --git a/android/src/main/kotlin/app/polyvox/filament/HotReloadPathHelper.kt b/android/src/main/kotlin/app/polyvox/filament/HotReloadPathHelper.kt new file mode 100644 index 00000000..05889d83 --- /dev/null +++ b/android/src/main/kotlin/app/polyvox/filament/HotReloadPathHelper.kt @@ -0,0 +1,35 @@ +import java.io.* +import java.nio.file.FileSystems +import java.nio.file.Files +import java.nio.file.Path +import kotlin.io.path.absolutePathString +import kotlin.io.path.listDirectoryEntries +import android.util.Log + + +class HotReloadPathHelper { + companion object { + fun getAssetPath(path: String, packageName: String): String? { + // iterate over evr + val shortName = packageName.split(".").last().split("_").last() + val packagePath = "/data/user/0/${packageName}/code_cache/" + Log.v("FFI", "Looking for shortName ${shortName} under package path ${packagePath}") + val files = File(packagePath).listFiles().filter { + it.path.split("/").last().startsWith(shortName) + }.map { + val f = File(it.path + "/${shortName}/build/${path}") + Log.v("FFI", "Looking for ${f.path.toString()}") + f + }.filter { + it.exists() + }.sortedBy { + Log.v("FFI", it.path.toString()) + it.lastModified() + } + Log.v("FFI", files.size.toString()) + if(files.size > 0) + return files.first().path; + return null; + } + } +} \ No newline at end of file diff --git a/android/src/main/kotlin/app/polyvox/filament/PolyvoxFilamentPlugin.kt b/android/src/main/kotlin/app/polyvox/filament/PolyvoxFilamentPlugin.kt index f5603f49..1dc483c1 100644 --- a/android/src/main/kotlin/app/polyvox/filament/PolyvoxFilamentPlugin.kt +++ b/android/src/main/kotlin/app/polyvox/filament/PolyvoxFilamentPlugin.kt @@ -4,6 +4,7 @@ import androidx.annotation.NonNull import androidx.lifecycle.Lifecycle +import io.flutter.embedding.engine.FlutterJNI import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding import io.flutter.embedding.engine.plugins.FlutterPlugin @@ -12,6 +13,8 @@ import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result +import io.flutter.embedding.engine.loader.FlutterApplicationInfo +import io.flutter.embedding.engine.loader.ApplicationInfoLoader import io.flutter.embedding.engine.plugins.lifecycle.HiddenLifecycleReference @@ -25,6 +28,7 @@ import android.content.pm.PackageManager import io.flutter.FlutterInjector import android.os.CountDownTimer +import android.os.Handler import android.opengl.GLU import javax.microedition.khronos.egl.EGLConfig @@ -74,6 +78,10 @@ import android.view.Surface.CHANGE_FRAME_RATE_ALWAYS import android.view.Surface.FRAME_RATE_COMPATIBILITY_DEFAULT import android.view.SurfaceHolder +import java.util.Timer +import java.util.concurrent.Executor +import java.util.concurrent.Executors + /** PolyvoxFilamentPlugin */ class PolyvoxFilamentPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { @@ -84,7 +92,7 @@ class PolyvoxFilamentPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { private val startTime = System.nanoTime() override fun doFrame(frameTimeNanos: Long) { choreographer.postFrameCallback(this) - synchronized(lock) { + executor.execute { if(_viewer != null) { if(!surface.isValid()) { Log.v(TAG, "INVALID") @@ -126,6 +134,8 @@ class PolyvoxFilamentPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { private lateinit var activity:Activity + private val executor = Executors.newFixedThreadPool(1); + override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { this.flutterPluginBinding = flutterPluginBinding channel = MethodChannel(flutterPluginBinding.binaryMessenger, CHANNEL_NAME) @@ -137,144 +147,162 @@ class PolyvoxFilamentPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { override fun onAttachedToActivity(binding: ActivityPluginBinding) { lifecycle = (binding.lifecycle as? HiddenLifecycleReference)?.lifecycle activity = binding.activity + choreographer = Choreographer.getInstance() + choreographer.postFrameCallback(frameCallback) + activity.window.setFormat(PixelFormat.RGBA_8888) + + } + + fun getAssetPath(path:String) : String { + val loader = FlutterInjector.instance().flutterLoader() + val key = loader.getLookupKeyForAsset(path) + val hotReloadPath = HotReloadPathHelper.getAssetPath(key, activity.getPackageName()) + if(hotReloadPath != null) { + return "file://" + hotReloadPath; + } + return key } override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { "initialize" -> { - if(_viewer != null) { - synchronized(lock) { - print("Deleting existing viewer") - _lib.filament_viewer_delete(_viewer!!); - _viewer = null; - } - } - if(surfaceTexture != null) { - print("Releasing existing texture") - surfaceTexture!!.release() - surfaceTexture = null; - } - val args = call.arguments as ArrayList - val width = args[0] - val height = args[1] - + print("Initializing") + val entry = flutterPluginBinding.textureRegistry.createSurfaceTexture(); + executor.execute { + if(_viewer != null) { + synchronized(lock) { + print("Deleting existing viewer") + _lib.filament_viewer_delete(_viewer!!); + print("Deleted viewer") + _viewer = null; + } + } + if(surfaceTexture != null) { + print("Releasing existing texture") + surfaceTexture!!.release() + surfaceTexture = null; + } + val args = call.arguments as ArrayList + val width = args[0] + val height = args[1] + + + surfaceTexture = entry.surfaceTexture() + + surfaceTexture!!.setDefaultBufferSize(width, height) + + surface = Surface(surfaceTexture!!) + + _viewer = _lib.filament_viewer_new_android( + surface as Object, + JNIEnv.CURRENT, + (activity as Context).assets) + + + + _lib.update_viewport_and_camera_projection(_viewer!!, width, height, 1.0f); + + result.success(entry.id().toInt()) - choreographer = Choreographer.getInstance() - - surfaceTexture = entry.surfaceTexture() - - surfaceTexture!!.setDefaultBufferSize(width, height) - - surface = Surface(surfaceTexture!!) - - _viewer = _lib.filament_viewer_new_android( - surface as Object, - JNIEnv.CURRENT, - (activity as Context).assets) - - choreographer.postFrameCallback(frameCallback) - - activity.window.setFormat(PixelFormat.RGBA_8888) - - _lib.update_viewport_and_camera_projection(_viewer!!, width, height, 1.0f); - - result.success(entry.id().toInt()) + } } "resize" -> { - val args = call.arguments as ArrayList - val width = args[0] - val height = args[1] + executor.execute { + val args = call.arguments as ArrayList + val width = args[0] + val height = args[1] - surfaceTexture!!.setDefaultBufferSize(width, height) - _lib.update_viewport_and_camera_projection(_viewer!!, width, height, 1.0f); - result.success(null) - } - "reloadAssets" -> { - // context = context.createPackageContext(context.getPackageName(), 0) - // val assetManager = context.getAssets() - // val flutterJNI = FlutterJNI.Factory.provideFlutterJNI() - // flutterJNI.updateJavaAssetManager(assetManager, flutterApplicationInfo.flutterAssetsDir) + surfaceTexture!!.setDefaultBufferSize(width, height) + _lib.update_viewport_and_camera_projection(_viewer!!, width, height, 1.0f); + result.success(null) } + } "setBackgroundImage" -> { - val args = call.arguments as String - val loader = FlutterInjector.instance().flutterLoader() - _lib.set_background_image(_viewer!!, loader.getLookupKeyForAsset(args)) - _lib.render(_viewer!!) + executor.execute { + _lib.set_background_image(_viewer!!, getAssetPath(call.arguments as String)) result.success("OK"); + } } "loadSkybox" -> { - val args = call.arguments as String - val loader = FlutterInjector.instance().flutterLoader() - _lib.load_skybox(_viewer!!, loader.getLookupKeyForAsset(args)) + executor.execute { + _lib.load_skybox(_viewer!!, getAssetPath(call.arguments as String)) result.success("OK"); + } } "loadIbl" -> { - val args = call.arguments as String - val loader = FlutterInjector.instance().flutterLoader() - - _lib.load_ibl(_viewer!!, loader.getLookupKeyForAsset(args)) + executor.execute { + _lib.load_ibl(_viewer!!, getAssetPath(call.arguments as String)) result.success("OK"); + } } "removeIbl" -> { - _lib.remove_ibl(_viewer!!) - result.success(true); + executor.execute { + _lib.remove_ibl(_viewer!!) + result.success(true); + } } "removeSkybox" -> { - _lib.remove_skybox(_viewer!!) - result.success(true); + executor.execute { + _lib.remove_skybox(_viewer!!) + result.success(true); + } } "loadGlb" -> { - if (_viewer == null) - return; - val loader = FlutterInjector.instance().flutterLoader() - val key = loader.getLookupKeyForAsset(call.arguments as String) - val key2 = loader.getLookupKeyForAsset(call.arguments as String, (activity as Context).packageName) - val path = loader.findAppBundlePath() - + executor.execute { val assetPtr = _lib.load_glb( _viewer!!, - key + getAssetPath(call.arguments as String) ) result.success(Pointer.nativeValue(assetPtr)); + } } "loadGltf" -> { - if (_viewer == null) - return; + executor.execute { val args = call.arguments as ArrayList - val loader = FlutterInjector.instance().flutterLoader() val assetPtr = _lib.load_gltf( _viewer!!, - loader.getLookupKeyForAsset(args[0] as String), - loader.getLookupKeyForAsset(args[1] as String) + getAssetPath(args[0] as String), + getAssetPath(args[1] as String) ) result.success(Pointer.nativeValue(assetPtr)); + } } "transformToUnitCube" -> { - val assetPtr = Pointer(call.arguments as Long); - _lib.transform_to_unit_cube(assetPtr) - result.success("OK"); + executor.execute { + val assetPtr = Pointer(call.arguments as Long); + _lib.transform_to_unit_cube(assetPtr) + result.success("OK"); + } } "setPosition" -> { - val args = call.arguments as ArrayList<*> - val assetPtr = Pointer(args[0] as Long) - _lib.set_position(assetPtr, (args[1] as Double).toFloat(), (args[2] as Double).toFloat(), (args[3] as Double).toFloat()) - result.success("OK"); + executor.execute { + val args = call.arguments as ArrayList<*> + val assetPtr = Pointer(args[0] as Long) + _lib.set_position(assetPtr, (args[1] as Double).toFloat(), (args[2] as Double).toFloat(), (args[3] as Double).toFloat()) + result.success("OK"); + } } "setRotation" -> { - val args = call.arguments as ArrayList<*> - val assetPtr = Pointer(args[0] as Long) - _lib.set_rotation(assetPtr, (args[1] as Double).toFloat(), (args[2] as Double).toFloat(), (args[3] as Double).toFloat(), (args[4] as Double).toFloat()) - result.success("OK"); + executor.execute { + val args = call.arguments as ArrayList<*> + val assetPtr = Pointer(args[0] as Long) + _lib.set_rotation(assetPtr, (args[1] as Double).toFloat(), (args[2] as Double).toFloat(), (args[3] as Double).toFloat(), (args[4] as Double).toFloat()) + result.success("OK"); + } } "setTexture" -> { - val args = call.arguments as ArrayList<*> - val loader = FlutterInjector.instance().flutterLoader() - val assetPtr = Pointer(args[0] as Long); - _lib.set_texture(assetPtr, loader.getLookupKeyForAsset(args[1] as String), args[2] as Int) - result.success("OK"); + executor.execute { + val args = call.arguments as ArrayList<*> + val assetPtr = Pointer(args[0] as Long); + _lib.load_texture(assetPtr, getAssetPath(args[1] as String), args[2] as Int) + print("Texture loaded") + result.success("OK"); + } + } "setCamera" -> { + executor.execute { val args = call.arguments as ArrayList<*> val success = _lib.set_camera( _viewer!!, @@ -286,110 +314,143 @@ class PolyvoxFilamentPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { } else { result.error("failed","failed", "Failed to set camera") } + } } "zoom" -> { - if(_viewer == null) - return; - _lib.scroll(_viewer!!, 0.0f, 0.0f, (call.arguments as Double).toFloat()) - result.success("OK"); + executor.execute { + _lib.scroll(_viewer!!, 0.0f, 0.0f, (call.arguments as Double).toFloat()) + result.success("OK"); + } } "getTargetNames" -> { - val args = call.arguments as ArrayList<*> - val assetPtr = Pointer(args[0] as Long) - val meshName = args[1] as String - val names = mutableListOf() - val outPtr = Memory(256) - for(i in 0.._lib.get_target_name_count(assetPtr, meshName) - 1) { - _lib.get_target_name(assetPtr, meshName, outPtr, i) - val name = outPtr.getString(0) - names.add(name) + executor.execute { + val args = call.arguments as ArrayList<*> + val assetPtr = Pointer(args[0] as Long) + val meshName = args[1] as String + val names = mutableListOf() + val outPtr = Memory(256) + for(i in 0.._lib.get_target_name_count(assetPtr, meshName) - 1) { + _lib.get_target_name(assetPtr, meshName, outPtr, i) + val name = outPtr.getString(0) + names.add(name) + } + result.success(names) } - result.success(names) } "getAnimationNames" -> { - val assetPtr = Pointer(call.arguments as Long) - val names = mutableListOf() - val outPtr = Memory(256) - for(i in 0.._lib.get_animation_count(assetPtr) - 1) { - _lib.get_animation_name(assetPtr, outPtr, i) - val name = outPtr.getString(0) - names.add(name) + executor.execute { + val assetPtr = Pointer(call.arguments as Long) + val names = mutableListOf() + val outPtr = Memory(256) + for(i in 0.._lib.get_animation_count(assetPtr) - 1) { + _lib.get_animation_name(assetPtr, outPtr, i) + val name = outPtr.getString(0) + names.add(name) + } + result.success(names) } - result.success(names) } "applyWeights" -> { - val args = call.arguments as ArrayList<*> - val assetPtr = Pointer(args[0] as Long) - val weights = args[1] as ArrayList; + executor.execute { + val args = call.arguments as ArrayList<*> + val assetPtr = Pointer(args[0] as Long) + val weights = args[1] as ArrayList; - _lib.apply_weights(assetPtr, weights.toFloatArray(), weights.size) - result.success("OK"); + _lib.apply_weights(assetPtr, weights.toFloatArray(), weights.size) + result.success("OK"); + } } "animateWeights" -> { - val args = call.arguments as ArrayList - val assetPtr = Pointer(args[0] as Long) - val frames = args[1] as ArrayList; - val numWeights = args[2] as Int - val numFrames = args[3] as Int - val frameLenInMs = args[4] as Double + executor.execute { + val args = call.arguments as ArrayList + val assetPtr = Pointer(args[0] as Long) + val frames = args[1] as ArrayList; + val numWeights = args[2] as Int + val numFrames = args[3] as Int + val frameLenInMs = args[4] as Double - _lib.animate_weights(assetPtr, frames.toFloatArray(), numWeights, numFrames, frameLenInMs.toFloat()) - result.success("OK"); + _lib.animate_weights(assetPtr, frames.toFloatArray(), numWeights, numFrames, frameLenInMs.toFloat()) + result.success("OK"); + } } "panStart" -> { - val args = call.arguments as ArrayList - _lib.grab_begin(_viewer!!, args[0] as Int, args[1] as Int, true) - result.success("OK"); + executor.execute { + val args = call.arguments as ArrayList + _lib.grab_begin(_viewer!!, args[0] as Int, args[1] as Int, true) + result.success("OK"); + } } "panUpdate" -> { - val args = call.arguments as ArrayList - _lib.grab_update(_viewer!!, args[0] as Int, args[1] as Int) - result.success("OK"); + executor.execute { + val args = call.arguments as ArrayList + _lib.grab_update(_viewer!!, args[0] as Int, args[1] as Int) + result.success("OK"); + } } "panEnd" -> { - _lib.grab_end(_viewer!!) - result.success("OK"); + executor.execute { + _lib.grab_end(_viewer!!) + result.success("OK"); + } } "rotateStart" -> { - val args = call.arguments as ArrayList - _lib.grab_begin(_viewer!!, args[0] as Int, args[1] as Int, false) - result.success("OK"); + executor.execute { + val args = call.arguments as ArrayList + _lib.grab_begin(_viewer!!, args[0] as Int, args[1] as Int, false) + result.success("OK"); + } } "rotateUpdate" -> { - val args = call.arguments as ArrayList - _lib.grab_update(_viewer!!, args[0] as Int, args[1] as Int) - result.success("OK"); + executor.execute { + val args = call.arguments as ArrayList + _lib.grab_update(_viewer!!, args[0] as Int, args[1] as Int) + result.success("OK"); + } } "rotateEnd" -> { - _lib.grab_end(_viewer!!) - result.success("OK"); + executor.execute { + _lib.grab_end(_viewer!!) + result.success("OK"); + } } "grabStart" -> { - val args = call.arguments as ArrayList - _lib.grab_begin(_viewer!!, args[0] as Int, args[1] as Int, true) - result.success("OK"); + executor.execute { + val args = call.arguments as ArrayList + _lib.grab_begin(_viewer!!, args[0] as Int, args[1] as Int, true) + result.success("OK"); + } } "grabUpdate" -> { - val args = call.arguments as ArrayList - _lib.grab_update(_viewer!!, args[0] as Int, args[1] as Int) - result.success("OK"); + executor.execute { + val args = call.arguments as ArrayList + _lib.grab_update(_viewer!!, args[0] as Int, args[1] as Int) + result.success("OK"); + } } "grabEnd" -> { - _lib.grab_end(_viewer!!) - result.success("OK"); + executor.execute { + _lib.grab_end(_viewer!!) + result.success("OK"); + } } "removeAsset" -> { - _lib.remove_asset(_viewer!!, Pointer(call.arguments as Long)) - result.success("OK"); + executor.execute { + _lib.remove_asset(_viewer!!, Pointer(call.arguments as Long)) + result.success("OK"); + } } "clearAssets" -> { - _lib.clear_assets(_viewer!!) - result.success("OK"); + executor.execute { + _lib.clear_assets(_viewer!!) + result.success("OK"); + } } "playAnimation" -> { - val args = call.arguments as ArrayList - _lib.play_animation(Pointer(args[0] as Long), args[1] as Int, args[2] as Boolean) - result.success("OK") + executor.execute { + val args = call.arguments as ArrayList + _lib.play_animation(Pointer(args[0] as Long), args[1] as Int, args[2] as Boolean) + result.success("OK") + } } else -> { result.notImplemented() diff --git a/example/lib/main.dart b/example/lib/main.dart index 36b650a5..10823e2f 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:polyvox_filament/filament_controller.dart'; import 'package:polyvox_filament/filament_widget.dart'; + void main() { runApp(const MyApp()); } @@ -223,6 +224,9 @@ class _MyAppState extends State { setState(() { _vertical = !_vertical; }); + break; + case 26: + await _filamentController.reload(); } }, itemBuilder: (BuildContext context) => @@ -302,6 +306,9 @@ class _MyAppState extends State { value: 19, child: Text('pan right')), PopupMenuItem( value: 25, child: Text(_vertical ? 'set horizontal' : 'set vertical')), + PopupMenuItem( + value: 26, child: Text( + "reload native assets")), ]))) ]))); } diff --git a/ios/src/FilamentViewer.cpp b/ios/src/FilamentViewer.cpp index 4b0fd190..92742945 100644 --- a/ios/src/FilamentViewer.cpp +++ b/ios/src/FilamentViewer.cpp @@ -111,7 +111,7 @@ FilamentViewer::FilamentViewer(void *layer, LoadResource loadResource, .presentationDeadlineNanos = (uint64_t)0, .vsyncOffsetNanos = (uint64_t)0}); Renderer::FrameRateOptions fro; - fro.interval = 30; + fro.interval = 60; _renderer->setFrameRateOptions(fro); _scene = _engine->createScene(); @@ -119,6 +119,7 @@ FilamentViewer::FilamentViewer(void *layer, LoadResource loadResource, Log("Scene created"); Entity camera = EntityManager::get().create(); + _mainCamera = _engine->createCamera(camera); Log("Main camera created"); _view = _engine->createView(); @@ -185,14 +186,15 @@ FilamentViewer::FilamentViewer(void *layer, LoadResource loadResource, _scene->addEntity(_sun); Log("Added sun"); - + _sceneAssetLoader = new SceneAssetLoader(_loadResource, _freeResource, _assetLoader, _resourceLoader, _ncm, _engine, - _scene); + _scene, + tp); } static constexpr float4 sFullScreenTriangleVertices[3] = { @@ -336,23 +338,20 @@ void FilamentViewer::setBackgroundImage(const char *resourcePath) { FilamentViewer::~FilamentViewer() { clearAssets(); - _resourceLoader->asyncCancelLoad(); - Log("c1"); - _materialProvider->destroyMaterials(); - Log("c2"); - AssetLoader::destroy(&_assetLoader); - Log("c3"); + Log("Deleting SceneAssetLoader"); delete _sceneAssetLoader; + _resourceLoader->asyncCancelLoad(); + _materialProvider->destroyMaterials(); + AssetLoader::destroy(&_assetLoader); + _engine->destroy(_sun); + _engine->destroyCameraComponent(_mainCamera->getEntity()); + _mainCamera = nullptr; _engine->destroy(_view); - Log("c4"); _engine->destroy(_scene); - Log("c5"); _engine->destroy(_renderer); - Log("c6"); _engine->destroy(_swapChain); - Log("c7"); + Engine::destroy(&_engine); // clears engine* - Log("c8"); } Renderer *FilamentViewer::getRenderer() { return _renderer; } @@ -401,8 +400,11 @@ SceneAsset *FilamentViewer::loadGltf(const char *const uri, void FilamentViewer::clearAssets() { Log("Clearing all assets"); - mtx.lock(); - _view->setCamera(_mainCamera); + // mtx.lock(); + if(_mainCamera) { + _view->setCamera(_mainCamera); + } + int i = 0; for (auto asset : _assets) { _sceneAssetLoader->remove(asset); @@ -410,7 +412,8 @@ void FilamentViewer::clearAssets() { i++; } _assets.clear(); - mtx.unlock(); + // mtx.unlock(); + Log("Cleared all assets"); } void FilamentViewer::removeAsset(SceneAsset *asset) { @@ -555,7 +558,7 @@ void FilamentViewer::render() { return; } - mtx.lock(); + // mtx.lock(); for (auto &asset : _assets) { asset->updateAnimations(); } @@ -569,7 +572,7 @@ void FilamentViewer::render() { _renderer->render(_view); _renderer->endFrame(); } - mtx.unlock(); + // mtx.unlock(); } void FilamentViewer::updateViewportAndCameraProjection( diff --git a/ios/src/FilamentViewer.hpp b/ios/src/FilamentViewer.hpp index 4fef66d4..6b726047 100644 --- a/ios/src/FilamentViewer.hpp +++ b/ios/src/FilamentViewer.hpp @@ -127,7 +127,6 @@ namespace polyvox { Material* _imageMaterial = nullptr; TextureSampler _imageSampler; ColorGrading *colorGrading = nullptr; - }; diff --git a/ios/src/PolyvoxFilamentApi.cpp b/ios/src/PolyvoxFilamentApi.cpp index 80477590..0b4ca5f9 100644 --- a/ios/src/PolyvoxFilamentApi.cpp +++ b/ios/src/PolyvoxFilamentApi.cpp @@ -15,7 +15,6 @@ extern "C" { void* filament_viewer_new(void* texture, ResourceBuffer (*loadResource)(const char*), void (*freeResource)(uint32_t)) { return nullptr; } - void filament_viewer_delete(void* viewer) { delete((FilamentViewer*)viewer); @@ -135,8 +134,12 @@ extern "C" { ((FilamentViewer*)viewer)->clearAssets(); } - void set_texture(void* asset, const char* assetPath, int renderableIndex) { - ((SceneAsset*)asset)->setTexture(assetPath, renderableIndex); + void load_texture(void* asset, const char* assetPath, int renderableIndex) { + ((SceneAsset*)asset)->loadTexture(assetPath, renderableIndex); + } + + void set_texture(void* asset) { + ((SceneAsset*)asset)->setTexture(); } void transform_to_unit_cube(void* asset) { diff --git a/ios/src/PolyvoxFilamentApi.hpp b/ios/src/PolyvoxFilamentApi.hpp index 98a76204..a5710f29 100644 --- a/ios/src/PolyvoxFilamentApi.hpp +++ b/ios/src/PolyvoxFilamentApi.hpp @@ -46,7 +46,8 @@ void remove_asset(void* viewer, void* asset); void clear_assets(void* viewer); -void set_texture(void* asset, const char* assetPath, int renderableIndex); +void load_texture(void* asset, const char* assetPath, int renderableIndex); +void set_texture(void* asset); void transform_to_unit_cube(void* asset); diff --git a/ios/src/SceneAsset.cpp b/ios/src/SceneAsset.cpp index b85e6e6d..4756fcda 100644 --- a/ios/src/SceneAsset.cpp +++ b/ios/src/SceneAsset.cpp @@ -41,7 +41,11 @@ SceneAsset::SceneAsset(FilamentAsset *asset, Engine *engine, } SceneAsset::~SceneAsset() { - // we defer all destructor work to SceneAssetLoader so we don't need to do anything here + // most other destructor work is deferred to SceneAssetLoader so we don't need to do anything here + if(_texture) { + _engine->destroy(_texture); + _texture = nullptr; + } } void SceneAsset::applyWeights(float *weights, int count) { @@ -121,26 +125,35 @@ void SceneAsset::stopAnimation(int index) { _embeddedAnimationStatus[index].started = false; } -void SceneAsset::setTexture(const char* resourcePath, int renderableIndex) { - Log("Setting texture to %s for renderableIndex %d", resourcePath, renderableIndex); - ResourceBuffer imageResource = _loadResource(resourcePath); +void SceneAsset::loadTexture(const char* resourcePath, int renderableIndex) { - polyvox::StreamBufferAdapter sb((char *)imageResource.data, (char *)imageResource.data + imageResource.size); + Log("Loading texture at %s for renderableIndex %d", resourcePath, renderableIndex); - std::istream *inputStream = new std::istream(&sb); + string rp("flutter_assets/assets/background.png"); + + if(_texture) { + _engine->destroy(_texture); + _texture = nullptr; + } + + ResourceBuffer imageResource = _loadResource(rp.c_str()); + + StreamBufferAdapter sb((char *)imageResource.data, (char *)imageResource.data + imageResource.size); + + istream *inputStream = new std::istream(&sb); LinearImage *image = new LinearImage(ImageDecoder::decode( - *inputStream, resourcePath, ImageDecoder::ColorSpace::SRGB)); + *inputStream, rp.c_str(), ImageDecoder::ColorSpace::SRGB)); if (!image->isValid()) { - Log("Invalid image : %s", resourcePath); + Log("Invalid image : %s", rp.c_str()); return; } uint32_t channels = image->getChannels(); uint32_t w = image->getWidth(); uint32_t h = image->getHeight(); - auto texture = Texture::Builder() + _texture = Texture::Builder() .width(w) .height(h) .levels(0xff) @@ -149,8 +162,10 @@ void SceneAsset::setTexture(const char* resourcePath, int renderableIndex) { .sampler(Texture::Sampler::SAMPLER_2D) .build(*_engine); + Log("build texture"); + Texture::PixelBufferDescriptor::Callback freeCallback = [](void *buf, size_t, - void *data) { + void *data) { delete reinterpret_cast(data); }; @@ -159,20 +174,26 @@ void SceneAsset::setTexture(const char* resourcePath, int renderableIndex) { channels == 3 ? Texture::Format::RGB : Texture::Format::RGBA, Texture::Type::FLOAT, freeCallback); - texture->setImage(*_engine, 0, std::move(buffer)); + _texture->setImage(*_engine, 0, std::move(buffer)); + Log("set image"); + setTexture(); + delete inputStream; - size_t mic = _asset->getMaterialInstanceCount(); + _freeResource(imageResource.id); + +} + +void SceneAsset::setTexture() { + MaterialInstance* const* inst = _asset->getMaterialInstances(); + size_t mic = _asset->getMaterialInstanceCount(); Log("Material instance count : %d", mic); RenderableManager &rm = _engine->getRenderableManager(); auto sampler = TextureSampler(); inst[0]->setParameter("baseColorIndex",0); - inst[0]->setParameter("baseColorMap",texture,sampler); + inst[0]->setParameter("baseColorMap",_texture,sampler); - delete inputStream; - - _freeResource(imageResource.id); } void SceneAsset::updateEmbeddedAnimations() { diff --git a/ios/src/SceneAsset.hpp b/ios/src/SceneAsset.hpp index c1be2d2f..a0692662 100644 --- a/ios/src/SceneAsset.hpp +++ b/ios/src/SceneAsset.hpp @@ -4,16 +4,17 @@ #include #include #include +#include #include #include #include + #include #include "SceneResources.hpp" - namespace polyvox { using namespace filament; using namespace filament::gltfio; @@ -32,7 +33,8 @@ namespace polyvox { /// /// /// - void setTexture(const char* resourcePath, int renderableIndex); + void loadTexture(const char* resourcePath, int renderableIndex); + void setTexture(); /// /// Update the bone/morph target animations to reflect the current frame (if applicable). @@ -79,6 +81,7 @@ namespace polyvox { private: + FilamentAsset* _asset = nullptr; Engine* _engine = nullptr; NameComponentManager* _ncm; @@ -86,6 +89,7 @@ namespace polyvox { void updateMorphAnimation(); void updateEmbeddedAnimations(); + Animator* _animator; // animation flags; @@ -95,5 +99,8 @@ namespace polyvox { LoadResource _loadResource; FreeResource _freeResource; + // a slot to preload textures + filament::Texture* _texture = nullptr; + }; } \ No newline at end of file diff --git a/ios/src/SceneAssetLoader.hpp b/ios/src/SceneAssetLoader.hpp index 2b62e3dc..5d84afc5 100644 --- a/ios/src/SceneAssetLoader.hpp +++ b/ios/src/SceneAssetLoader.hpp @@ -24,7 +24,8 @@ namespace polyvox { ResourceLoader* resourceLoader, NameComponentManager* ncm, Engine* engine, - Scene* scene); + Scene* scene, + workqueue::threadpool& threadpool); SceneAsset* fromGltf(const char* uri, const char* relativeResourcePath); SceneAsset* fromGlb(const char* uri); void remove(SceneAsset* asset); @@ -37,6 +38,7 @@ namespace polyvox { NameComponentManager* _ncm; Scene* _scene; Engine* _engine; + workqueue::threadpool& _threadpool; };