run all Android work on separate thread, add HotReloadPathHelper, separate loadTexture/setTexture

This commit is contained in:
Nick Fisher
2022-08-25 17:09:54 +10:00
parent f182af21b2
commit ad25014981
12 changed files with 392 additions and 227 deletions

View File

@@ -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);

View File

@@ -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;
}
}
}

View File

@@ -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<Int>
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<Int>
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<Int>
val width = args[0]
val height = args[1]
executor.execute {
val args = call.arguments as ArrayList<Int>
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<Any?>
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<String>()
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<String>()
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<String>()
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<String>()
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<Float>;
executor.execute {
val args = call.arguments as ArrayList<*>
val assetPtr = Pointer(args[0] as Long)
val weights = args[1] as ArrayList<Float>;
_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<Any?>
val assetPtr = Pointer(args[0] as Long)
val frames = args[1] as ArrayList<Float>;
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<Any?>
val assetPtr = Pointer(args[0] as Long)
val frames = args[1] as ArrayList<Float>;
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<Any?>
_lib.grab_begin(_viewer!!, args[0] as Int, args[1] as Int, true)
result.success("OK");
executor.execute {
val args = call.arguments as ArrayList<Any?>
_lib.grab_begin(_viewer!!, args[0] as Int, args[1] as Int, true)
result.success("OK");
}
}
"panUpdate" -> {
val args = call.arguments as ArrayList<Any?>
_lib.grab_update(_viewer!!, args[0] as Int, args[1] as Int)
result.success("OK");
executor.execute {
val args = call.arguments as ArrayList<Any?>
_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<Any?>
_lib.grab_begin(_viewer!!, args[0] as Int, args[1] as Int, false)
result.success("OK");
executor.execute {
val args = call.arguments as ArrayList<Any?>
_lib.grab_begin(_viewer!!, args[0] as Int, args[1] as Int, false)
result.success("OK");
}
}
"rotateUpdate" -> {
val args = call.arguments as ArrayList<Any?>
_lib.grab_update(_viewer!!, args[0] as Int, args[1] as Int)
result.success("OK");
executor.execute {
val args = call.arguments as ArrayList<Any?>
_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<Any?>
_lib.grab_begin(_viewer!!, args[0] as Int, args[1] as Int, true)
result.success("OK");
executor.execute {
val args = call.arguments as ArrayList<Any?>
_lib.grab_begin(_viewer!!, args[0] as Int, args[1] as Int, true)
result.success("OK");
}
}
"grabUpdate" -> {
val args = call.arguments as ArrayList<Any?>
_lib.grab_update(_viewer!!, args[0] as Int, args[1] as Int)
result.success("OK");
executor.execute {
val args = call.arguments as ArrayList<Any?>
_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<Any?>
_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<Any?>
_lib.play_animation(Pointer(args[0] as Long), args[1] as Int, args[2] as Boolean)
result.success("OK")
}
}
else -> {
result.notImplemented()