Files
cup_edit/android/src/main/kotlin/app/polyvox/filament/PolyvoxFilamentPlugin.kt

590 lines
19 KiB
Kotlin

package app.polyvox.filament
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
import io.flutter.plugin.common.BinaryMessenger
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
import android.content.res.AssetManager
import android.Manifest
import android.app.Activity
import android.content.Context
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
import javax.microedition.khronos.opengles.GL10
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import android.hardware.Camera
import android.opengl.GLSurfaceView
import android.view.SurfaceView
import android.view.TextureView
import android.view.View
import android.view.Surface
import android.widget.TextView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.DefaultLifecycleObserver
import io.flutter.plugin.platform.PlatformView
import java.io.IOException
import android.util.Log
import com.sun.jna.Library
import com.sun.jna.Native
import com.sun.jna.Pointer
import com.sun.jna.Memory
import com.sun.jna.ptr.PointerByReference
import com.sun.jna.ptr.IntByReference
import com.sun.jna.Structure
import com.sun.jna.NativeLibrary
import com.sun.jna.StringArray
import com.sun.jna.JNIEnv
import android.R.attr.path
import android.graphics.*
import java.util.Collections;
import android.hardware.display.DisplayManager
import com.google.android.filament.android.*
import com.google.android.filament.*
import android.view.Choreographer
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 {
private val lock = Object()
inner class FrameCallback : Choreographer.FrameCallback {
private val startTime = System.nanoTime()
override fun doFrame(frameTimeNanos: Long) {
choreographer.postFrameCallback(this)
executor.execute {
if(_viewer == null || !_render) {
} else if(!surface.isValid()) {
Log.v(TAG, "INVALID")
} else {
_lib.render(_viewer!!, frameTimeNanos)
}
}
}
}
companion object {
const val CHANNEL_NAME = "app.polyvox.filament/event"
const val TAG = "FilamentPlugin"
}
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private lateinit var channel : MethodChannel
/// Keep a reference to the plugin binding so we can use the TextureRegistry when initialize is called from the platform channel.
private lateinit var flutterPluginBinding : FlutterPlugin.FlutterPluginBinding
private var lifecycle: Lifecycle? = null
private lateinit var _lib : FilamentInterop
private var _viewer : Pointer? = null
private var _render : Boolean = true
private lateinit var choreographer: Choreographer
private val frameCallback = FrameCallback()
private lateinit var assetManager : AssetManager
private lateinit var surface: Surface
private var surfaceTexture: SurfaceTexture? = null
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)
channel.setMethodCallHandler(this)
_lib = Native.loadLibrary("filament_interop", FilamentInterop::class.java, Collections.singletonMap(Library.OPTION_ALLOW_OBJECTS, true))
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
lifecycle = (binding.lifecycle as? HiddenLifecycleReference)?.lifecycle
activity = binding.activity
activity.window.setFormat(PixelFormat.RGBA_8888)
choreographer = Choreographer.getInstance()
choreographer.postFrameCallback(frameCallback)
}
fun getAssetPath(path:String) : String {
if(path.startsWith("file://")) {
return path
}
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" -> {
val entry = flutterPluginBinding.textureRegistry.createSurfaceTexture();
executor.execute {
if(_viewer != null) {
_lib.filament_viewer_delete(_viewer!!);
_viewer = null;
}
if(surfaceTexture != null) {
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())
}
}
"resize" -> {
executor.execute {
val args = call.arguments as ArrayList<Int>
val width = args[0]
val height = args[1]
val scale = if(args.size > 2) (args[2] as Double).toFloat() else 1.0f
surfaceTexture!!.setDefaultBufferSize(width, height)
_lib.update_viewport_and_camera_projection(_viewer!!, width, height, scale);
result.success(null)
}
}
"render" -> {
executor.execute {
_lib.render(_viewer!!, 0)
result.success(null)
}
}
"setRendering" -> {
_render = call.arguments as Boolean
Log.v(TAG, "Set rendering to ${_render}")
result.success(null)
}
"setFrameInterval" -> {
executor.execute {
_lib.set_frame_interval(_viewer!!, (call.arguments as Double).toFloat());
result.success(null)
}
}
"setBackgroundImage" -> {
executor.execute {
_lib.set_background_image(_viewer!!, getAssetPath(call.arguments as String))
result.success("OK");
}
}
"setBackgroundImagePosition" -> {
executor.execute {
val args = call.arguments as ArrayList<Any?>
_lib.set_background_image_position(_viewer!!, (args[0] as Double).toFloat(), (args[1] as Double).toFloat(), (args[2] as Boolean))
result.success("OK");
}
}
"loadSkybox" -> {
executor.execute {
_lib.load_skybox(_viewer!!, getAssetPath(call.arguments as String))
result.success("OK");
}
}
"loadIbl" -> {
executor.execute {
_lib.load_ibl(_viewer!!, getAssetPath(call.arguments as String))
result.success("OK");
}
}
"removeIbl" -> {
executor.execute {
_lib.remove_ibl(_viewer!!)
result.success(true);
}
}
"removeSkybox" -> {
executor.execute {
_lib.remove_skybox(_viewer!!)
result.success(true);
}
}
"addLight" -> {
executor.execute {
val args = call.arguments as ArrayList<Any?>
val entity = _lib.add_light(
_viewer!!,
args[0] as Int,
(args[1] as Double).toFloat(),
(args[2] as Double).toFloat(),
(args[3] as Double).toFloat(),
(args[4] as Double).toFloat(),
(args[5] as Double).toFloat(),
(args[6] as Double).toFloat(),
(args[7] as Double).toFloat(),
(args[8] as Double).toFloat(),
(args[9] as Boolean))
result.success(entity);
}
}
"removeLight" -> {
executor.execute {
_lib.remove_light(
_viewer!!,
call.arguments as Int)
result.success(true);
}
}
"clearLights" -> {
executor.execute {
_lib.clear_lights(
_viewer!!
)
result.success(true);
}
}
"loadGlb" -> {
executor.execute {
val assetPtr = _lib.load_glb(
_viewer!!,
getAssetPath(call.arguments as String)
)
result.success(Pointer.nativeValue(assetPtr));
}
}
"loadGltf" -> {
executor.execute {
val args = call.arguments as ArrayList<Any?>
val assetPtr = _lib.load_gltf(
_viewer!!,
getAssetPath(args[0] as String),
getAssetPath(args[1] as String)
)
result.success(Pointer.nativeValue(assetPtr));
}
}
"transformToUnitCube" -> {
executor.execute {
val assetPtr = Pointer(call.arguments as Long);
_lib.transform_to_unit_cube(assetPtr)
result.success("OK");
}
}
"setPosition" -> {
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");
}
}
"setScale" -> {
executor.execute {
val args = call.arguments as ArrayList<*>
val assetPtr = Pointer(args[0] as Long)
_lib.set_scale(assetPtr, (args[1] as Double).toFloat())
result.success("OK");
}
}
"setRotation" -> {
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");
}
}
"setCameraPosition" -> {
executor.execute {
val args = call.arguments as ArrayList<*>
_lib.set_camera_position(_viewer!!, (args[0] as Double).toFloat(), (args[1] as Double).toFloat(), (args[2] as Double).toFloat())
result.success("OK");
}
}
"setCameraRotation" -> {
executor.execute {
val args = call.arguments as ArrayList<*>
_lib.set_camera_rotation(_viewer!!, (args[0] as Double).toFloat(), (args[1] as Double).toFloat(), (args[2] as Double).toFloat(), (args[3] as Double).toFloat())
result.success("OK");
}
}
"setCameraFocalLength" -> {
executor.execute {
_lib.set_camera_focal_length(_viewer!!, (call.arguments as Double).toFloat())
result.success("OK");
}
}
"setCameraFocusDistance" -> {
executor.execute {
_lib.set_camera_focus_distance(_viewer!!, (call.arguments as Double).toFloat())
result.success("OK");
}
}
"setTexture" -> {
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!!,
Pointer(args[0] as Long),
args[1] as String,
)
if(success) {
result.success("OK");
} else {
result.error("failed","failed", "Failed to set camera")
}
}
}
"zoomBegin" -> {
executor.execute {
_lib.scroll_begin(_viewer!!)
result.success("OK");
}
}
"zoomUpdate" -> {
executor.execute {
val args = call.arguments as ArrayList<*>
_lib.scroll_update(_viewer!!, (args[0] as Double).toFloat(), (args[1] as Double).toFloat(), (args[2] as Double).toFloat())
result.success("OK");
}
}
"zoomEnd" -> {
executor.execute {
_lib.scroll_end(_viewer!!)
result.success("OK");
}
}
"getTargetNames" -> {
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)
}
}
"getAnimationNames" -> {
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)
}
}
"applyWeights" -> {
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");
}
}
"animateWeights" -> {
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");
}
}
"panStart" -> {
executor.execute {
val args = call.arguments as ArrayList<Any?>
_lib.grab_begin(_viewer!!, (args[0] as Double).toFloat(), (args[1] as Double).toFloat(), true)
result.success("OK");
}
}
"panUpdate" -> {
executor.execute {
val args = call.arguments as ArrayList<Any?>
val x = (args[0] as Double).toFloat()
val y = (args[1] as Double).toFloat()
_lib.grab_update(_viewer!!, x, y)
result.success("OK");
}
}
"panEnd" -> {
executor.execute {
_lib.grab_end(_viewer!!)
result.success("OK");
}
}
"rotateStart" -> {
executor.execute {
val args = call.arguments as ArrayList<Any?>
_lib.grab_begin(_viewer!!, (args[0] as Double).toFloat(), (args[1] as Double).toFloat(), false)
result.success("OK");
}
}
"rotateUpdate" -> {
executor.execute {
val args = call.arguments as ArrayList<Any?>
_lib.grab_update(_viewer!!, (args[0] as Double).toFloat(), (args[1] as Double).toFloat())
result.success("OK");
}
}
"rotateEnd" -> {
executor.execute {
_lib.grab_end(_viewer!!)
result.success("OK");
}
}
"grabStart" -> {
executor.execute {
val args = call.arguments as ArrayList<Any?>
_lib.grab_begin(_viewer!!, (args[0] as Double).toFloat(), (args[1] as Double).toFloat(), true)
result.success("OK");
}
}
"grabUpdate" -> {
executor.execute {
val args = call.arguments as ArrayList<Any?>
_lib.grab_update(_viewer!!, (args[0] as Double).toFloat(), (args[1] as Double).toFloat())
result.success("OK");
}
}
"grabEnd" -> {
executor.execute {
_lib.grab_end(_viewer!!)
result.success("OK");
}
}
"removeAsset" -> {
executor.execute {
_lib.remove_asset(_viewer!!, Pointer(call.arguments as Long))
result.success("OK");
}
}
"clearAssets" -> {
executor.execute {
_lib.clear_assets(_viewer!!)
result.success("OK");
}
}
"playAnimation" -> {
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, args[3] as Boolean)
result.success("OK")
}
}
"stopAnimation" -> {
executor.execute {
val args = call.arguments as ArrayList<Any?>
_lib.stop_animation(Pointer(args[0] as Long), args[1] as Int)
result.success("OK")
}
}
else -> {
result.notImplemented()
}
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
_lib.destroy_swap_chain(_viewer!!)
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
onAttachedToActivity(binding)
_lib.create_swap_chain(_viewer!!, surface, JNIEnv.CURRENT)
}
override fun onDetachedFromActivityForConfigChanges() {
onDetachedFromActivity()
}
override fun onDetachedFromActivity() {
lifecycle = null
}
}