render directly to Texture rather than PlatformView (Android only)
This commit is contained in:
@@ -1,397 +0,0 @@
|
|||||||
package app.polyvox.filament
|
|
||||||
import android.content.res.AssetManager
|
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.graphics.Color
|
|
||||||
import android.graphics.SurfaceTexture
|
|
||||||
import android.graphics.PixelFormat
|
|
||||||
|
|
||||||
import io.flutter.FlutterInjector
|
|
||||||
|
|
||||||
import android.os.CountDownTimer
|
|
||||||
|
|
||||||
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.common.BinaryMessenger
|
|
||||||
import io.flutter.plugin.common.MethodCall
|
|
||||||
import io.flutter.plugin.common.MethodChannel
|
|
||||||
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.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 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.SurfaceHolder
|
|
||||||
|
|
||||||
|
|
||||||
class FilamentView(
|
|
||||||
private val viewId: Int,
|
|
||||||
private val context: Context,
|
|
||||||
private val activity: Activity,
|
|
||||||
private val binaryMessenger: BinaryMessenger,
|
|
||||||
private val creationParams : Map<String?, Any?>?
|
|
||||||
) : DefaultLifecycleObserver,
|
|
||||||
MethodChannel.MethodCallHandler,
|
|
||||||
PlatformView {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val TAG = "FilamentView"
|
|
||||||
}
|
|
||||||
|
|
||||||
private val _view = SurfaceView(context)
|
|
||||||
|
|
||||||
override fun getView(): View {
|
|
||||||
return _view
|
|
||||||
}
|
|
||||||
|
|
||||||
private val _methodChannel: MethodChannel
|
|
||||||
|
|
||||||
private lateinit var _lib : FilamentInterop
|
|
||||||
|
|
||||||
private var _viewer : Pointer? = null
|
|
||||||
|
|
||||||
private lateinit var choreographer: Choreographer
|
|
||||||
|
|
||||||
private val frameScheduler = FrameCallback()
|
|
||||||
|
|
||||||
private lateinit var uiHelper : UiHelper
|
|
||||||
|
|
||||||
private lateinit var assetManager : AssetManager
|
|
||||||
|
|
||||||
|
|
||||||
init {
|
|
||||||
MethodChannel(binaryMessenger, PolyvoxFilamentPlugin.VIEW_TYPE + '_' + viewId).also {
|
|
||||||
_methodChannel = it
|
|
||||||
it.setMethodCallHandler(this)
|
|
||||||
}
|
|
||||||
_lib = Native.loadLibrary("filament_interop", FilamentInterop::class.java, Collections.singletonMap(Library.OPTION_ALLOW_OBJECTS, true))
|
|
||||||
|
|
||||||
_methodChannel.invokeMethod("ready", null)
|
|
||||||
|
|
||||||
choreographer = Choreographer.getInstance()
|
|
||||||
// _view.setAlpha(0)
|
|
||||||
_view.setZOrderOnTop(false)
|
|
||||||
_view.holder.setFormat(PixelFormat.TRANSPARENT)
|
|
||||||
|
|
||||||
_view.holder.addCallback (object : SurfaceHolder.Callback {
|
|
||||||
override fun surfaceChanged(holder:SurfaceHolder, format:Int, width:Int, height:Int) {
|
|
||||||
Log.v(TAG, "SURFACE CHANGED")
|
|
||||||
if(_viewer != null) {
|
|
||||||
_lib.update_viewport_and_camera_projection(_viewer!!, width, height, 1.0f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun surfaceCreated(holder:SurfaceHolder) {
|
|
||||||
Log.v(TAG, "SURFACE CREATED")
|
|
||||||
if(_viewer == null) {
|
|
||||||
_viewer = _lib.filament_viewer_new(
|
|
||||||
_view.holder.surface as Object,
|
|
||||||
JNIEnv.CURRENT,
|
|
||||||
context.assets)
|
|
||||||
|
|
||||||
choreographer.postFrameCallback(frameScheduler)
|
|
||||||
|
|
||||||
activity.window.setFormat(PixelFormat.RGBA_8888)
|
|
||||||
|
|
||||||
uiHelper = UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK)
|
|
||||||
uiHelper.renderCallback = SurfaceCallback()
|
|
||||||
uiHelper.attachTo(_view)
|
|
||||||
}
|
|
||||||
_lib.destroy_swap_chain(_viewer!!)
|
|
||||||
_lib.create_swap_chain(_viewer!!, _view.holder.surface, JNIEnv.CURRENT)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun surfaceDestroyed(holder:SurfaceHolder) {
|
|
||||||
if(_viewer != null) {
|
|
||||||
_lib.destroy_swap_chain(_viewer!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun dispose() {
|
|
||||||
_methodChannel.setMethodCallHandler(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
|
||||||
when (call.method) {
|
|
||||||
"reloadAssets" -> {
|
|
||||||
// context = context.createPackageContext(context.getPackageName(), 0)
|
|
||||||
// val assetManager = context.getAssets()
|
|
||||||
// val flutterJNI = FlutterJNI.Factory.provideFlutterJNI()
|
|
||||||
// flutterJNI.updateJavaAssetManager(assetManager, flutterApplicationInfo.flutterAssetsDir)
|
|
||||||
}
|
|
||||||
"setBackgroundImage" -> {
|
|
||||||
val args = call.arguments as String
|
|
||||||
val loader = FlutterInjector.instance().flutterLoader()
|
|
||||||
_lib.set_background_image(_viewer!!, loader.getLookupKeyForAsset(args))
|
|
||||||
Log.v(TAG, "Package name : ${context.getPackageName()}")
|
|
||||||
result.success("OK");
|
|
||||||
}
|
|
||||||
"loadSkybox" -> {
|
|
||||||
val args = call.arguments as String
|
|
||||||
val loader = FlutterInjector.instance().flutterLoader()
|
|
||||||
_lib.load_skybox(_viewer!!, loader.getLookupKeyForAsset(args))
|
|
||||||
result.success("OK");
|
|
||||||
}
|
|
||||||
"loadIbl" -> {
|
|
||||||
val args = call.arguments as String
|
|
||||||
val loader = FlutterInjector.instance().flutterLoader()
|
|
||||||
|
|
||||||
_lib.load_ibl(_viewer!!, loader.getLookupKeyForAsset(args))
|
|
||||||
result.success("OK");
|
|
||||||
}
|
|
||||||
"removeIbl" -> {
|
|
||||||
_lib.remove_ibl(_viewer!!)
|
|
||||||
result.success(true);
|
|
||||||
}
|
|
||||||
"removeSkybox" -> {
|
|
||||||
_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, context.packageName)
|
|
||||||
val path = loader.findAppBundlePath()
|
|
||||||
|
|
||||||
val assetPtr = _lib.load_glb(
|
|
||||||
_viewer!!,
|
|
||||||
key
|
|
||||||
)
|
|
||||||
result.success(Pointer.nativeValue(assetPtr));
|
|
||||||
}
|
|
||||||
"loadGltf" -> {
|
|
||||||
if (_viewer == null)
|
|
||||||
return;
|
|
||||||
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)
|
|
||||||
)
|
|
||||||
result.success(Pointer.nativeValue(assetPtr));
|
|
||||||
}
|
|
||||||
"transformToUnitCube" -> {
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
"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");
|
|
||||||
}
|
|
||||||
"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");
|
|
||||||
}
|
|
||||||
"setCamera" -> {
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"zoom" -> {
|
|
||||||
if(_viewer == null)
|
|
||||||
return;
|
|
||||||
_lib.scroll(_viewer!!, 0.0f, 0.0f, (call.arguments as Double).toFloat())
|
|
||||||
result.success("OK");
|
|
||||||
}
|
|
||||||
"getTargetNames" -> {
|
|
||||||
if(_viewer == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
val countPtr = IntByReference();
|
|
||||||
val args = call.arguments as ArrayList<*>
|
|
||||||
val namesPtr = _lib.get_target_names(Pointer(args[0] as Long), args[1] as String, countPtr)
|
|
||||||
|
|
||||||
val names = namesPtr.getStringArray(0, countPtr.value);
|
|
||||||
|
|
||||||
for(i in 0..countPtr.value-1) {
|
|
||||||
Log.v(TAG, "Got target names ${names[i]} ${names[i].length}")
|
|
||||||
}
|
|
||||||
|
|
||||||
val namesAsList = names.toCollection(ArrayList())
|
|
||||||
|
|
||||||
_lib.free_pointer(namesPtr, countPtr.getValue())
|
|
||||||
|
|
||||||
result.success(namesAsList)
|
|
||||||
}
|
|
||||||
"getAnimationNames" -> {
|
|
||||||
val assetPtr = Pointer(call.arguments as Long)
|
|
||||||
val countPtr = IntByReference();
|
|
||||||
val arrPtr = _lib.get_animation_names(assetPtr, countPtr)
|
|
||||||
|
|
||||||
val names = arrPtr.getStringArray(0, countPtr.value);
|
|
||||||
|
|
||||||
for(i in 0..countPtr.value-1) {
|
|
||||||
val name = names[i];
|
|
||||||
Log.v(TAG, "Got animation names ${name} ${name.length}")
|
|
||||||
}
|
|
||||||
|
|
||||||
_lib.free_pointer(arrPtr, 1)
|
|
||||||
|
|
||||||
result.success(names.toCollection(ArrayList()))
|
|
||||||
}
|
|
||||||
"applyWeights" -> {
|
|
||||||
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" -> {
|
|
||||||
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" -> {
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
"panEnd" -> {
|
|
||||||
_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");
|
|
||||||
}
|
|
||||||
"rotateUpdate" -> {
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
"grabStart" -> {
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
"grabEnd" -> {
|
|
||||||
_lib.grab_end(_viewer!!)
|
|
||||||
result.success("OK");
|
|
||||||
}
|
|
||||||
"removeAsset" -> {
|
|
||||||
_lib.remove_asset(_viewer!!, Pointer(call.arguments as Long))
|
|
||||||
result.success("OK");
|
|
||||||
}
|
|
||||||
"clearAssets" -> {
|
|
||||||
_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")
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
result.notImplemented()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class SurfaceCallback : UiHelper.RendererCallback {
|
|
||||||
override fun onNativeWindowChanged(surface: Surface) {
|
|
||||||
_lib.destroy_swap_chain(_viewer!!)
|
|
||||||
_lib.create_swap_chain(_viewer!!, surface, JNIEnv.CURRENT)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDetachedFromSurface() {
|
|
||||||
_lib.destroy_swap_chain(_viewer!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResized(width: Int, height: Int) {
|
|
||||||
_lib.update_viewport_and_camera_projection(_viewer!!, width, height, 1.0f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inner class FrameCallback : Choreographer.FrameCallback {
|
|
||||||
private val startTime = System.nanoTime()
|
|
||||||
override fun doFrame(frameTimeNanos: Long) {
|
|
||||||
choreographer.postFrameCallback(this)
|
|
||||||
_lib.render(_viewer!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package app.polyvox.filament
|
|
||||||
|
|
||||||
import io.flutter.plugin.common.BinaryMessenger
|
|
||||||
import android.app.Activity
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.view.View
|
|
||||||
import io.flutter.plugin.common.StandardMessageCodec
|
|
||||||
import io.flutter.plugin.platform.PlatformView
|
|
||||||
import io.flutter.plugin.platform.PlatformViewFactory
|
|
||||||
|
|
||||||
class FilamentViewFactory(
|
|
||||||
private val activity: Activity,
|
|
||||||
private val binaryMessenger: BinaryMessenger
|
|
||||||
) : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
|
|
||||||
override fun create(context: Context?, viewId: Int, args: Any?): PlatformView {
|
|
||||||
val creationParams = args as Map<String?, Any?>?
|
|
||||||
return FilamentView(viewId, context!!, activity, binaryMessenger, creationParams)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,6 +7,7 @@ import androidx.lifecycle.Lifecycle
|
|||||||
import io.flutter.embedding.engine.plugins.activity.ActivityAware
|
import io.flutter.embedding.engine.plugins.activity.ActivityAware
|
||||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
|
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
|
||||||
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||||
|
import io.flutter.plugin.common.BinaryMessenger
|
||||||
import io.flutter.plugin.common.MethodCall
|
import io.flutter.plugin.common.MethodCall
|
||||||
import io.flutter.plugin.common.MethodChannel
|
import io.flutter.plugin.common.MethodChannel
|
||||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||||
@@ -14,12 +15,82 @@ import io.flutter.plugin.common.MethodChannel.Result
|
|||||||
|
|
||||||
import io.flutter.embedding.engine.plugins.lifecycle.HiddenLifecycleReference
|
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.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.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
|
||||||
|
|
||||||
|
|
||||||
/** PolyvoxFilamentPlugin */
|
/** PolyvoxFilamentPlugin */
|
||||||
class PolyvoxFilamentPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
|
class PolyvoxFilamentPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
|
||||||
|
|
||||||
|
inner class FrameCallback : Choreographer.FrameCallback {
|
||||||
|
private val startTime = System.nanoTime()
|
||||||
|
override fun doFrame(frameTimeNanos: Long) {
|
||||||
|
choreographer.postFrameCallback(this)
|
||||||
|
if(!surface.isValid()) {
|
||||||
|
Log.v(TAG, "INVALID")
|
||||||
|
}
|
||||||
|
_lib.render(_viewer!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val VIEW_TYPE = "app.polyvox.filament/filament_view"
|
const val CHANNEL_NAME = "app.polyvox.filament/event"
|
||||||
|
const val TAG = "FilamentPlugin"
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The MethodChannel that will the communication between Flutter and native Android
|
/// The MethodChannel that will the communication between Flutter and native Android
|
||||||
@@ -28,41 +99,307 @@ class PolyvoxFilamentPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
|
|||||||
/// when the Flutter Engine is detached from the Activity
|
/// when the Flutter Engine is detached from the Activity
|
||||||
private lateinit var channel : MethodChannel
|
private lateinit var channel : MethodChannel
|
||||||
|
|
||||||
/// Keep a referene to the plugin binding so we can defer construction of a FilamentViewFactory
|
/// Keep a referene to the plugin binding so we can use the TextureRegistry when initialize is called from the platform channel.
|
||||||
/// until Activity is attached.
|
|
||||||
private lateinit var flutterPluginBinding : FlutterPlugin.FlutterPluginBinding
|
private lateinit var flutterPluginBinding : FlutterPlugin.FlutterPluginBinding
|
||||||
|
|
||||||
private var lifecycle: Lifecycle? = null
|
private var lifecycle: Lifecycle? = null
|
||||||
|
|
||||||
|
private lateinit var _lib : FilamentInterop
|
||||||
|
|
||||||
|
private var _viewer : Pointer? = null
|
||||||
|
|
||||||
|
private lateinit var choreographer: Choreographer
|
||||||
|
|
||||||
|
private val frameCallback = FrameCallback()
|
||||||
|
|
||||||
|
private lateinit var assetManager : AssetManager
|
||||||
|
|
||||||
|
private lateinit var surface: Surface
|
||||||
|
private lateinit var surfaceTexture: SurfaceTexture
|
||||||
|
|
||||||
|
private lateinit var activity:Activity
|
||||||
|
|
||||||
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||||
this.flutterPluginBinding = flutterPluginBinding
|
this.flutterPluginBinding = flutterPluginBinding
|
||||||
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "app.polyvox.filament")
|
channel = MethodChannel(flutterPluginBinding.binaryMessenger, CHANNEL_NAME)
|
||||||
channel.setMethodCallHandler(this)
|
channel.setMethodCallHandler(this)
|
||||||
|
|
||||||
|
_lib = Native.loadLibrary("filament_interop", FilamentInterop::class.java, Collections.singletonMap(Library.OPTION_ALLOW_OBJECTS, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
|
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
|
||||||
lifecycle = (binding.lifecycle as? HiddenLifecycleReference)?.lifecycle
|
lifecycle = (binding.lifecycle as? HiddenLifecycleReference)?.lifecycle
|
||||||
flutterPluginBinding
|
activity = binding.activity
|
||||||
.platformViewRegistry
|
|
||||||
.registerViewFactory(VIEW_TYPE, FilamentViewFactory(binding.activity, flutterPluginBinding.binaryMessenger))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
|
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||||
|
when (call.method) {
|
||||||
|
"initialize" -> {
|
||||||
|
val args = call.arguments as ArrayList<Int>
|
||||||
|
val width = args[0]
|
||||||
|
val height = args[1]
|
||||||
|
|
||||||
|
val entry = flutterPluginBinding.textureRegistry.createSurfaceTexture();
|
||||||
|
|
||||||
if (call.method == "getPlatformVersion") {
|
choreographer = Choreographer.getInstance()
|
||||||
result.success("Android ${android.os.Build.VERSION.RELEASE}")
|
|
||||||
} else {
|
surfaceTexture = entry.surfaceTexture()
|
||||||
result.notImplemented()
|
|
||||||
|
surfaceTexture.setDefaultBufferSize(width, height)
|
||||||
|
|
||||||
|
surface = Surface(surfaceTexture)
|
||||||
|
|
||||||
|
_viewer = _lib.filament_viewer_new(
|
||||||
|
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]
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
"setBackgroundImage" -> {
|
||||||
|
val args = call.arguments as String
|
||||||
|
val loader = FlutterInjector.instance().flutterLoader()
|
||||||
|
_lib.set_background_image(_viewer!!, loader.getLookupKeyForAsset(args))
|
||||||
|
_lib.render(_viewer!!)
|
||||||
|
result.success("OK");
|
||||||
|
}
|
||||||
|
"loadSkybox" -> {
|
||||||
|
val args = call.arguments as String
|
||||||
|
val loader = FlutterInjector.instance().flutterLoader()
|
||||||
|
_lib.load_skybox(_viewer!!, loader.getLookupKeyForAsset(args))
|
||||||
|
result.success("OK");
|
||||||
|
}
|
||||||
|
"loadIbl" -> {
|
||||||
|
val args = call.arguments as String
|
||||||
|
val loader = FlutterInjector.instance().flutterLoader()
|
||||||
|
|
||||||
|
_lib.load_ibl(_viewer!!, loader.getLookupKeyForAsset(args))
|
||||||
|
result.success("OK");
|
||||||
|
}
|
||||||
|
"removeIbl" -> {
|
||||||
|
_lib.remove_ibl(_viewer!!)
|
||||||
|
result.success(true);
|
||||||
|
}
|
||||||
|
"removeSkybox" -> {
|
||||||
|
_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()
|
||||||
|
|
||||||
|
val assetPtr = _lib.load_glb(
|
||||||
|
_viewer!!,
|
||||||
|
key
|
||||||
|
)
|
||||||
|
result.success(Pointer.nativeValue(assetPtr));
|
||||||
|
}
|
||||||
|
"loadGltf" -> {
|
||||||
|
if (_viewer == null)
|
||||||
|
return;
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
result.success(Pointer.nativeValue(assetPtr));
|
||||||
|
}
|
||||||
|
"transformToUnitCube" -> {
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
"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");
|
||||||
|
}
|
||||||
|
"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");
|
||||||
|
}
|
||||||
|
"setCamera" -> {
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"zoom" -> {
|
||||||
|
if(_viewer == null)
|
||||||
|
return;
|
||||||
|
_lib.scroll(_viewer!!, 0.0f, 0.0f, (call.arguments as Double).toFloat())
|
||||||
|
result.success("OK");
|
||||||
|
}
|
||||||
|
"getTargetNames" -> {
|
||||||
|
if(_viewer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
val countPtr = IntByReference();
|
||||||
|
val args = call.arguments as ArrayList<*>
|
||||||
|
val namesPtr = _lib.get_target_names(Pointer(args[0] as Long), args[1] as String, countPtr)
|
||||||
|
|
||||||
|
val names = namesPtr.getStringArray(0, countPtr.value);
|
||||||
|
|
||||||
|
for(i in 0..countPtr.value-1) {
|
||||||
|
Log.v(TAG, "Got target names ${names[i]} ${names[i].length}")
|
||||||
|
}
|
||||||
|
|
||||||
|
val namesAsList = names.toCollection(ArrayList())
|
||||||
|
|
||||||
|
_lib.free_pointer(namesPtr, countPtr.getValue())
|
||||||
|
|
||||||
|
result.success(namesAsList)
|
||||||
|
}
|
||||||
|
"getAnimationNames" -> {
|
||||||
|
val assetPtr = Pointer(call.arguments as Long)
|
||||||
|
val countPtr = IntByReference();
|
||||||
|
val arrPtr = _lib.get_animation_names(assetPtr, countPtr)
|
||||||
|
|
||||||
|
val names = arrPtr.getStringArray(0, countPtr.value);
|
||||||
|
|
||||||
|
for(i in 0..countPtr.value-1) {
|
||||||
|
val name = names[i];
|
||||||
|
Log.v(TAG, "Got animation names ${name} ${name.length}")
|
||||||
|
}
|
||||||
|
|
||||||
|
_lib.free_pointer(arrPtr, 1)
|
||||||
|
|
||||||
|
result.success(names.toCollection(ArrayList()))
|
||||||
|
}
|
||||||
|
"applyWeights" -> {
|
||||||
|
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" -> {
|
||||||
|
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" -> {
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
"panEnd" -> {
|
||||||
|
_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");
|
||||||
|
}
|
||||||
|
"rotateUpdate" -> {
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
"grabStart" -> {
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
"grabEnd" -> {
|
||||||
|
_lib.grab_end(_viewer!!)
|
||||||
|
result.success("OK");
|
||||||
|
}
|
||||||
|
"removeAsset" -> {
|
||||||
|
_lib.remove_asset(_viewer!!, Pointer(call.arguments as Long))
|
||||||
|
result.success("OK");
|
||||||
|
}
|
||||||
|
"clearAssets" -> {
|
||||||
|
_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")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
result.notImplemented()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
|
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
|
||||||
channel.setMethodCallHandler(null)
|
channel.setMethodCallHandler(null)
|
||||||
|
//_lib.destroy_swap_chain(_viewer!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
|
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
|
||||||
onAttachedToActivity(binding)
|
onAttachedToActivity(binding)
|
||||||
|
//_lib.create_swap_chain(_viewer!!, surface, JNIEnv.CURRENT)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDetachedFromActivityForConfigChanges() {
|
override fun onDetachedFromActivityForConfigChanges() {
|
||||||
@@ -70,6 +407,6 @@ class PolyvoxFilamentPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDetachedFromActivity() {
|
override fun onDetachedFromActivity() {
|
||||||
lifecycle = null
|
lifecycle = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,10 +56,15 @@ android {
|
|||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
signingConfig signingConfigs.debug
|
signingConfig signingConfigs.debug
|
||||||
|
minifyEnabled true
|
||||||
|
shrinkResources true
|
||||||
|
proguardFiles getDefaultProguardFile(
|
||||||
|
'proguard-android-optimize.txt'),
|
||||||
|
'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
packagingOptions {
|
packagingOptions {
|
||||||
|
|
||||||
}
|
}
|
||||||
aaptOptions {
|
aaptOptions {
|
||||||
noCompress "ktx"
|
noCompress "ktx"
|
||||||
@@ -71,13 +76,10 @@ flutter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
implementation "com.google.android.filament:filament-android:1.17.0"
|
|
||||||
implementation "com.google.android.filament:filament-utils-android:1.17.0"
|
|
||||||
implementation "com.google.android.filament:gltfio-android:1.17.0"
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2"
|
||||||
implementation "androidx.annotation:annotation:1.3.0"
|
implementation "androidx.annotation:annotation:1.3.0"
|
||||||
implementation "androidx.core:core:1.7.0"
|
implementation "androidx.core:core:1.7.0"
|
||||||
|
compile 'net.java.dev.jna:jna:5.10.0@aar'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:polyvox_filament/filament_controller.dart';
|
import 'package:polyvox_filament/filament_controller.dart';
|
||||||
import 'package:polyvox_filament/view/filament_widget.dart';
|
import 'package:polyvox_filament/filament_widget.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
@@ -23,6 +25,7 @@ class _MyAppState extends State<MyApp> {
|
|||||||
List<String> _targets = [];
|
List<String> _targets = [];
|
||||||
List<String> _animationNames = [];
|
List<String> _animationNames = [];
|
||||||
bool _loop = false;
|
bool _loop = false;
|
||||||
|
bool _vertical = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -32,240 +35,317 @@ class _MyAppState extends State<MyApp> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
|
showPerformanceOverlay: true,
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
body: Column(children: [
|
body: Column(
|
||||||
Expanded(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
child: SizedBox(
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
height: 200,
|
children: [
|
||||||
width: 200,
|
GestureDetector(
|
||||||
child: FilamentWidget(
|
onScaleStart: (d) {
|
||||||
controller: _filamentController,
|
if (d.pointerCount == 1)
|
||||||
))),
|
_filamentController.panStart(0.5, 0.5);
|
||||||
Align(
|
},
|
||||||
alignment: Alignment.bottomLeft,
|
onScaleEnd: (d) {
|
||||||
child: Container(
|
// if(d.pointerCount == 2) {}
|
||||||
color: Colors.white,
|
// _filamentController.panEnd(d. .focalPoint.dx,d.focalPoint.dy);
|
||||||
padding: const EdgeInsets.all(5),
|
},
|
||||||
child: PopupMenuButton<int>(
|
onScaleUpdate: (d) {
|
||||||
child: const Icon(Icons.menu),
|
if (d.pointerCount == 1) {
|
||||||
onSelected: (int item) async {
|
// _filamentController.panUpdate(d.focalPoint.dx,d.focalPoint.dy);
|
||||||
switch (item) {
|
} else {
|
||||||
case 0:
|
_filamentController.zoom(10.0);
|
||||||
await _filamentController.setBackgroundImage(
|
}
|
||||||
'assets/background.png');
|
},
|
||||||
break;
|
child: Container(
|
||||||
case 1:
|
width: _vertical ? 100 : 200, height: _vertical ? 200 : 100,
|
||||||
await _filamentController.loadSkybox(
|
alignment: Alignment.center,
|
||||||
'assets/default_env/default_env_skybox.ktx');
|
child: SizedBox(
|
||||||
await _filamentController.loadSkybox(
|
child: FilamentWidget(
|
||||||
'assets/default_env/default_env_ibl.ktx');
|
controller: _filamentController,
|
||||||
break;
|
)),
|
||||||
case 2:
|
)),
|
||||||
await _filamentController.removeSkybox();
|
Align(
|
||||||
break;
|
alignment: Alignment.bottomLeft,
|
||||||
case 3:
|
child: Container(
|
||||||
_cube = await _filamentController
|
color: Colors.white,
|
||||||
.loadGlb('assets/cube.glb');
|
margin: const EdgeInsets.all(15),
|
||||||
|
child: PopupMenuButton<int>(
|
||||||
|
padding: EdgeInsets.all(50),
|
||||||
|
iconSize: 36,
|
||||||
|
child: const Icon(Icons.menu),
|
||||||
|
onSelected: (int item) async {
|
||||||
|
switch (item) {
|
||||||
|
case 0:
|
||||||
|
await _filamentController
|
||||||
|
.setBackgroundImage(
|
||||||
|
'assets/background.png');
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
await _filamentController.loadSkybox(
|
||||||
|
'assets/default_env/default_env_skybox.ktx');
|
||||||
|
await _filamentController.loadSkybox(
|
||||||
|
'assets/default_env/default_env_ibl.ktx');
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
await _filamentController.removeSkybox();
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
_cube = await _filamentController
|
||||||
|
.loadGlb('assets/cube.glb');
|
||||||
|
|
||||||
_animationNames = await _filamentController
|
_animationNames = await _filamentController
|
||||||
.getAnimationNames(_cube!);
|
.getAnimationNames(_cube!);
|
||||||
break;
|
break;
|
||||||
case 4:
|
|
||||||
if (_cube != null) {
|
case 4:
|
||||||
await _filamentController.removeAsset(_cube!);
|
if (_cube != null) {
|
||||||
|
await _filamentController
|
||||||
|
.removeAsset(_cube!);
|
||||||
|
}
|
||||||
|
_cube = await _filamentController.loadGltf(
|
||||||
|
'assets/cube.gltf', 'assets');
|
||||||
|
print(await _filamentController
|
||||||
|
.getAnimationNames(_cube!));
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
if (_flightHelmet == null) {
|
||||||
|
_flightHelmet =
|
||||||
|
await _filamentController.loadGltf(
|
||||||
|
'assets/FlightHelmet/FlightHelmet.gltf',
|
||||||
|
'assets/FlightHelmet');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
await _filamentController
|
||||||
|
.removeAsset(_cube!);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 7:
|
||||||
|
await _filamentController.applyWeights(
|
||||||
|
_cube!, List.filled(8, 1.0));
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
await _filamentController.applyWeights(
|
||||||
|
_cube!, List.filled(8, 0));
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
_filamentController.playAnimations(
|
||||||
|
_cube!,
|
||||||
|
List.generate(
|
||||||
|
_animationNames.length, (i) => i),
|
||||||
|
loop: _loop);
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
_filamentController.stopAnimation(_cube!);
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
setState(() {
|
||||||
|
_loop = !_loop;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
_filamentController.zoom(-1.0);
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
_filamentController.zoom(1.0);
|
||||||
|
break;
|
||||||
|
case 14:
|
||||||
|
_filamentController.setCamera(
|
||||||
|
_cube!, "Camera_Orientation");
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
final framerate = 30;
|
||||||
|
final totalSecs = 5;
|
||||||
|
final numWeights = 8;
|
||||||
|
final totalFrames = framerate * totalSecs;
|
||||||
|
final frames = List.generate(
|
||||||
|
totalFrames,
|
||||||
|
(frame) => List.filled(
|
||||||
|
numWeights, frame / totalFrames));
|
||||||
|
|
||||||
|
_filamentController.animate(
|
||||||
|
_cube!,
|
||||||
|
frames.reduce((a, b) => a + b),
|
||||||
|
numWeights,
|
||||||
|
totalFrames,
|
||||||
|
1000 / framerate.toDouble());
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
final names = await _filamentController
|
||||||
|
.getTargetNames(_cube!, "Cube");
|
||||||
|
|
||||||
|
await showDialog(
|
||||||
|
builder: (ctx) {
|
||||||
|
return Container(
|
||||||
|
color: Colors.white,
|
||||||
|
height: 200,
|
||||||
|
width: 200,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize:
|
||||||
|
MainAxisSize.min,
|
||||||
|
children: names
|
||||||
|
.map((name) =>
|
||||||
|
Text(name))
|
||||||
|
.cast<Widget>()
|
||||||
|
.toList() +
|
||||||
|
<Widget>[
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () =>
|
||||||
|
Navigator.of(
|
||||||
|
ctx)
|
||||||
|
.pop(),
|
||||||
|
child:
|
||||||
|
Text("Close"))
|
||||||
|
]));
|
||||||
|
},
|
||||||
|
context: context);
|
||||||
|
break;
|
||||||
|
case 17:
|
||||||
|
final names = await _filamentController
|
||||||
|
.getAnimationNames(_cube!);
|
||||||
|
|
||||||
|
await showDialog(
|
||||||
|
builder: (ctx) {
|
||||||
|
return Container(
|
||||||
|
color: Colors.white,
|
||||||
|
height: 200,
|
||||||
|
width: 200,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize:
|
||||||
|
MainAxisSize.min,
|
||||||
|
children: names
|
||||||
|
.map((name) =>
|
||||||
|
Text(name))
|
||||||
|
.cast<Widget>()
|
||||||
|
.toList() +
|
||||||
|
<Widget>[
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () =>
|
||||||
|
Navigator.of(
|
||||||
|
ctx)
|
||||||
|
.pop(),
|
||||||
|
child:
|
||||||
|
Text("Close"))
|
||||||
|
]));
|
||||||
|
},
|
||||||
|
context: context);
|
||||||
|
break;
|
||||||
|
case 18:
|
||||||
|
await _filamentController.panStart(1, 1);
|
||||||
|
await _filamentController.panUpdate(1, 2);
|
||||||
|
await _filamentController.panEnd();
|
||||||
|
break;
|
||||||
|
case 19:
|
||||||
|
await _filamentController.panStart(1, 1);
|
||||||
|
await _filamentController.panUpdate(0, 0);
|
||||||
|
await _filamentController.panEnd();
|
||||||
|
break;
|
||||||
|
case 20:
|
||||||
|
await _filamentController.clearAssets();
|
||||||
|
break;
|
||||||
|
case 21:
|
||||||
|
await _filamentController.setTexture(
|
||||||
|
_cube!, "assets/background.png");
|
||||||
|
break;
|
||||||
|
case 22:
|
||||||
|
await _filamentController
|
||||||
|
.transformToUnitCube(_cube!);
|
||||||
|
break;
|
||||||
|
case 23:
|
||||||
|
await _filamentController.setPosition(
|
||||||
|
_cube!, 1.0, 1.0, -1.0);
|
||||||
|
break;
|
||||||
|
case 24:
|
||||||
|
await _filamentController.setRotation(
|
||||||
|
_cube!, pi / 2, 0.0, 1.0, 0.0);
|
||||||
|
break;
|
||||||
|
case 25:
|
||||||
|
setState(() {
|
||||||
|
_vertical = !_vertical;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
_cube = await _filamentController.loadGltf(
|
},
|
||||||
'assets/cube.gltf', 'assets');
|
itemBuilder: (BuildContext context) =>
|
||||||
print(await _filamentController
|
<PopupMenuEntry<int>>[
|
||||||
.getAnimationNames(_cube!));
|
const PopupMenuItem(
|
||||||
break;
|
value: 0,
|
||||||
case 5:
|
child: Text("load background image")),
|
||||||
if (_flightHelmet == null) {
|
const PopupMenuItem(
|
||||||
_flightHelmet =
|
value: 1,
|
||||||
await _filamentController.loadGltf(
|
child: Text('load skybox'),
|
||||||
'assets/FlightHelmet/FlightHelmet.gltf',
|
),
|
||||||
'assets/FlightHelmet');
|
const PopupMenuItem(
|
||||||
}
|
value: 2,
|
||||||
break;
|
child: Text('remove skybox'),
|
||||||
case 6:
|
),
|
||||||
await _filamentController.removeAsset(_cube!);
|
const PopupMenuItem(
|
||||||
break;
|
value: 3, child: Text('load cube GLB')),
|
||||||
|
const PopupMenuItem(
|
||||||
case 7:
|
value: 4,
|
||||||
await _filamentController.applyWeights(
|
child: Text('load cube GLTF')),
|
||||||
_cube!, List.filled(8, 1.0));
|
const PopupMenuItem(
|
||||||
break;
|
value: 21,
|
||||||
case 8:
|
child: Text('swap cube texture')),
|
||||||
await _filamentController.applyWeights(
|
const PopupMenuItem(
|
||||||
_cube!, List.filled(8, 0));
|
value: 22,
|
||||||
break;
|
child: Text('transform to unit cube')),
|
||||||
case 9:
|
const PopupMenuItem(
|
||||||
_filamentController.playAnimations(
|
value: 23,
|
||||||
_cube!,
|
child:
|
||||||
List.generate(
|
Text('set position to 1, 1, -1')),
|
||||||
_animationNames.length, (i) => i),
|
const PopupMenuItem(
|
||||||
loop: _loop);
|
value: 24,
|
||||||
break;
|
child:
|
||||||
case 10:
|
Text('rotate by pi around Y axis')),
|
||||||
_filamentController.stopAnimation(_cube!);
|
const PopupMenuItem(
|
||||||
break;
|
value: 5,
|
||||||
case 11:
|
child: Text('load flight helmet')),
|
||||||
setState(() {
|
const PopupMenuItem(
|
||||||
_loop = !_loop;
|
value: 6, child: Text('remove cube')),
|
||||||
});
|
const PopupMenuItem(
|
||||||
break;
|
value: 20,
|
||||||
case 12:
|
child: Text('remove all assets')),
|
||||||
_filamentController.zoom(-1.0);
|
const PopupMenuItem(
|
||||||
break;
|
value: 7,
|
||||||
case 13:
|
child: Text('set all weights to 1')),
|
||||||
_filamentController.zoom(1.0);
|
const PopupMenuItem(
|
||||||
break;
|
value: 8,
|
||||||
case 14:
|
child: Text('set all weights to 0')),
|
||||||
_filamentController.setCamera(
|
const PopupMenuItem(
|
||||||
_cube!, "Camera_Orientation");
|
value: 9,
|
||||||
break;
|
child: Text('play all animations')),
|
||||||
case 15:
|
const PopupMenuItem(
|
||||||
final framerate = 30;
|
value: 10,
|
||||||
final totalSecs = 5;
|
child: Text('stop animations')),
|
||||||
final numWeights = 8;
|
PopupMenuItem(
|
||||||
final totalFrames = framerate * totalSecs;
|
value: 11,
|
||||||
final frames = List.generate(
|
child: Text(
|
||||||
totalFrames,
|
_loop ? "don't loop animation" : "loop animation")),
|
||||||
(frame) => List.filled(
|
const PopupMenuItem(
|
||||||
numWeights, frame / totalFrames));
|
value: 12, child: Text('zoom in')),
|
||||||
|
const PopupMenuItem(
|
||||||
_filamentController.animate(
|
value: 13, child: Text('zoom out')),
|
||||||
_cube!,
|
const PopupMenuItem(
|
||||||
frames.reduce((a, b) => a + b),
|
value: 14, child: Text('set camera')),
|
||||||
numWeights,
|
const PopupMenuItem(
|
||||||
totalFrames,
|
value: 15,
|
||||||
1000 / framerate.toDouble());
|
child: Text('animate weights')),
|
||||||
break;
|
const PopupMenuItem(
|
||||||
case 16:
|
value: 16,
|
||||||
final names = await _filamentController
|
child: Text('get target names')),
|
||||||
.getTargetNames(_cube!, "Cube");
|
const PopupMenuItem(
|
||||||
|
value: 17,
|
||||||
await showDialog(
|
child: Text('get animation names')),
|
||||||
builder: (ctx) {
|
const PopupMenuItem(
|
||||||
return Container(
|
value: 18, child: Text('pan left')),
|
||||||
color: Colors.white,
|
const PopupMenuItem(
|
||||||
height: 200,
|
value: 19, child: Text('pan right')),
|
||||||
width: 200,
|
PopupMenuItem(
|
||||||
child: Column(
|
value: 25, child: Text(_vertical ? 'set horizontal' : 'set vertical')),
|
||||||
mainAxisSize: MainAxisSize.min,
|
])))
|
||||||
children: names
|
])));
|
||||||
.map((name) => Text(name))
|
|
||||||
.cast<Widget>()
|
|
||||||
.toList() +
|
|
||||||
<Widget>[
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () =>
|
|
||||||
Navigator.of(ctx)
|
|
||||||
.pop(),
|
|
||||||
child: Text("Close"))
|
|
||||||
]));
|
|
||||||
},
|
|
||||||
context: context);
|
|
||||||
break;
|
|
||||||
case 17:
|
|
||||||
final names = await _filamentController
|
|
||||||
.getAnimationNames(_cube!);
|
|
||||||
|
|
||||||
await showDialog(
|
|
||||||
builder: (ctx) {
|
|
||||||
return Container(
|
|
||||||
color: Colors.white,
|
|
||||||
height: 200,
|
|
||||||
width: 200,
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: names
|
|
||||||
.map((name) => Text(name))
|
|
||||||
.cast<Widget>()
|
|
||||||
.toList() +
|
|
||||||
<Widget>[
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () =>
|
|
||||||
Navigator.of(ctx)
|
|
||||||
.pop(),
|
|
||||||
child: Text("Close"))
|
|
||||||
]));
|
|
||||||
},
|
|
||||||
context: context);
|
|
||||||
break;
|
|
||||||
case 18:
|
|
||||||
await _filamentController.panStart(1, 1);
|
|
||||||
await _filamentController.panUpdate(1, 2);
|
|
||||||
await _filamentController.panEnd();
|
|
||||||
break;
|
|
||||||
case 19:
|
|
||||||
await _filamentController.panStart(1, 1);
|
|
||||||
await _filamentController.panUpdate(0, 0);
|
|
||||||
await _filamentController.panEnd();
|
|
||||||
break;
|
|
||||||
case 20:
|
|
||||||
await _filamentController.clearAssets();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
itemBuilder: (BuildContext context) =>
|
|
||||||
<PopupMenuEntry<int>>[
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 0,
|
|
||||||
child: Text("load background image")),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 1,
|
|
||||||
child: Text('load skybox'),
|
|
||||||
),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 2,
|
|
||||||
child: Text('remove skybox'),
|
|
||||||
),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 3, child: Text('load cube GLB')),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 4, child: Text('load cube GLTF')),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 5,
|
|
||||||
child: Text('load flight helmet')),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 6, child: Text('remove cube')),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 20, child: Text('remove all assets')),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 7,
|
|
||||||
child: Text('set all weights to 1')),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 8,
|
|
||||||
child: Text('set all weights to 0')),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 9,
|
|
||||||
child: Text('play all animations')),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 10, child: Text('stop animations')),
|
|
||||||
PopupMenuItem(
|
|
||||||
value: 11,
|
|
||||||
child: Text(
|
|
||||||
"toggle animation loop (currently $_loop!)")),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 12, child: Text('zoom in')),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 13, child: Text('zoom out')),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 14, child: Text('set camera')),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 15, child: Text('animate weights')),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 16, child: Text('get target names')),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 17,
|
|
||||||
child: Text('get animation names')),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 18, child: Text('pan left')),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 19, child: Text('pan right')),
|
|
||||||
])))
|
|
||||||
])));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ import 'package:flutter/services.dart';
|
|||||||
typedef FilamentAsset = int;
|
typedef FilamentAsset = int;
|
||||||
|
|
||||||
abstract class FilamentController {
|
abstract class FilamentController {
|
||||||
void onFilamentViewCreated(int id);
|
|
||||||
|
late int textureId;
|
||||||
|
Future initialize(int width, int height);
|
||||||
|
Future resize(int width, int height);
|
||||||
Future setBackgroundImage(String path);
|
Future setBackgroundImage(String path);
|
||||||
Future loadSkybox(String skyboxPath);
|
Future loadSkybox(String skyboxPath);
|
||||||
Future removeSkybox();
|
Future removeSkybox();
|
||||||
@@ -45,28 +48,24 @@ abstract class FilamentController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class PolyvoxFilamentController extends FilamentController {
|
class PolyvoxFilamentController extends FilamentController {
|
||||||
late int _id;
|
|
||||||
late MethodChannel _channel;
|
|
||||||
|
|
||||||
final Function(int id)? onFilamentViewCreatedHandler;
|
late MethodChannel _channel = MethodChannel("app.polyvox.filament/event");
|
||||||
|
|
||||||
PolyvoxFilamentController({this.onFilamentViewCreatedHandler});
|
PolyvoxFilamentController() {
|
||||||
|
|
||||||
@override
|
|
||||||
void onFilamentViewCreated(int id) async {
|
|
||||||
_id = id;
|
|
||||||
_channel = MethodChannel("app.polyvox.filament/filament_view_$id");
|
|
||||||
_channel.setMethodCallHandler((call) async {
|
_channel.setMethodCallHandler((call) async {
|
||||||
print("Received Filament method channel call : ${call.method}");
|
print("Received Filament method channel call : ${call.method}");
|
||||||
if (call.method == "ready") {
|
throw Exception("Unknown method channel invocation ${call.method}");
|
||||||
onFilamentViewCreatedHandler?.call(_id);
|
|
||||||
return Future.value(true);
|
|
||||||
} else {
|
|
||||||
throw Exception("Unknown method channel invocation ${call.method}");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future initialize(int width, int height) async {
|
||||||
|
textureId = await _channel.invokeMethod("initialize", [width, height]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future resize(int width, int height) async {
|
||||||
|
await _channel.invokeMethod("resize", [width, height]);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future setBackgroundImage(String path) async {
|
Future setBackgroundImage(String path) async {
|
||||||
await _channel.invokeMethod("setBackgroundImage", path);
|
await _channel.invokeMethod("setBackgroundImage", path);
|
||||||
|
|||||||
88
lib/filament_widget.dart
Normal file
88
lib/filament_widget.dart
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'filament_controller.dart';
|
||||||
|
|
||||||
|
|
||||||
|
typedef ResizeCallback = void Function(Size oldSize, Size newSize);
|
||||||
|
|
||||||
|
class ResizeObserver extends SingleChildRenderObjectWidget {
|
||||||
|
final ResizeCallback onResized;
|
||||||
|
|
||||||
|
const ResizeObserver({
|
||||||
|
Key? key,
|
||||||
|
required this.onResized,
|
||||||
|
Widget? child,
|
||||||
|
}) : super(
|
||||||
|
key: key,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
RenderObject createRenderObject(BuildContext context) =>
|
||||||
|
_RenderResizeObserver(onLayoutChangedCallback: onResized);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RenderResizeObserver extends RenderProxyBox {
|
||||||
|
final ResizeCallback onLayoutChangedCallback;
|
||||||
|
|
||||||
|
_RenderResizeObserver({
|
||||||
|
RenderBox? child,
|
||||||
|
required this.onLayoutChangedCallback,
|
||||||
|
}) : super(child);
|
||||||
|
|
||||||
|
late var _oldSize = size;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void performLayout() {
|
||||||
|
super.performLayout();
|
||||||
|
if (size != _oldSize) {
|
||||||
|
onLayoutChangedCallback(_oldSize, size);
|
||||||
|
_oldSize = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FilamentWidget extends StatefulWidget {
|
||||||
|
|
||||||
|
final FilamentController controller;
|
||||||
|
|
||||||
|
const FilamentWidget({Key? key, required this.controller}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_FilamentWidgetState createState() => _FilamentWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FilamentWidgetState extends State<FilamentWidget> {
|
||||||
|
|
||||||
|
bool _ready = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||||
|
var size = ((context.findRenderObject()) as RenderBox).size;
|
||||||
|
print("Requesting texture creation for Filament of size $size");
|
||||||
|
await widget.controller.initialize(size.width.toInt(), size.height.toInt());
|
||||||
|
print("Filament texture available");
|
||||||
|
setState(() {
|
||||||
|
_ready = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if(!_ready) {
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
return ResizeObserver(
|
||||||
|
onResized: (Size oldSize, Size newSize) async {
|
||||||
|
await widget.controller.resize(newSize.width.toInt(), newSize.height.toInt());
|
||||||
|
},
|
||||||
|
child:Texture(
|
||||||
|
textureId: widget.controller.textureId,
|
||||||
|
filterQuality: FilterQuality.none,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
import 'filament_controller.dart';
|
import 'filament_controller.dart';
|
||||||
import 'view/filament_widget.dart';
|
import 'filament_widget.dart';
|
||||||
|
|
||||||
class GestureDetectingFilamentView extends StatefulWidget {
|
class GestureDetectingFilamentView extends StatefulWidget {
|
||||||
final FilamentController controller;
|
final FilamentController controller;
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/gestures.dart';
|
|
||||||
import 'package:flutter/rendering.dart';
|
|
||||||
import 'package:flutter/widgets.dart';
|
|
||||||
|
|
||||||
import 'filament_view_platform.dart';
|
|
||||||
|
|
||||||
class FilamentView extends FilamentViewPlatform {
|
|
||||||
static const FILAMENT_VIEW_ID = 'app.polyvox.filament/filament_view';
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget buildView(
|
|
||||||
int creationId,
|
|
||||||
FilamentViewCreatedCallback onFilamentViewCreated,
|
|
||||||
) {
|
|
||||||
switch (defaultTargetPlatform) {
|
|
||||||
case TargetPlatform.android:
|
|
||||||
return AndroidView(
|
|
||||||
viewType: FILAMENT_VIEW_ID,
|
|
||||||
gestureRecognizers: <Factory<OneSequenceGestureRecognizer>>{},
|
|
||||||
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
|
|
||||||
onPlatformViewCreated: (id) {
|
|
||||||
onFilamentViewCreated(id);
|
|
||||||
});
|
|
||||||
case TargetPlatform.iOS:
|
|
||||||
return UiKitView(
|
|
||||||
viewType: FILAMENT_VIEW_ID,
|
|
||||||
onPlatformViewCreated: (int id) {
|
|
||||||
onFilamentViewCreated(id);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
case TargetPlatform.windows:
|
|
||||||
return Text("Flutter doesn't support platform view on Windows yet.");
|
|
||||||
default:
|
|
||||||
return Text(
|
|
||||||
'$defaultTargetPlatform is not yet implemented by Filament plugin.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import 'package:flutter/widgets.dart';
|
|
||||||
import 'package:polyvox_filament/view/filament_view.dart';
|
|
||||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
|
||||||
|
|
||||||
typedef FilamentViewCreatedCallback = void Function(int id);
|
|
||||||
|
|
||||||
abstract class FilamentViewPlatform extends PlatformInterface {
|
|
||||||
FilamentViewPlatform() : super(token: _token);
|
|
||||||
|
|
||||||
static final Object _token = Object();
|
|
||||||
static final FilamentViewPlatform _instance = FilamentView();
|
|
||||||
|
|
||||||
static FilamentViewPlatform get instance => _instance;
|
|
||||||
|
|
||||||
Widget buildView(
|
|
||||||
int creationId,
|
|
||||||
FilamentViewCreatedCallback onFilamentViewCreated,
|
|
||||||
) {
|
|
||||||
throw UnimplementedError('buildView() has not been implemented.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/gestures.dart';
|
|
||||||
import 'package:flutter/rendering.dart';
|
|
||||||
import 'package:flutter/widgets.dart';
|
|
||||||
import 'package:polyvox_filament/view/filament_view.dart';
|
|
||||||
|
|
||||||
import '../filament_controller.dart';
|
|
||||||
import 'filament_view_platform.dart';
|
|
||||||
|
|
||||||
int _nextFilamentCreationId = 0;
|
|
||||||
|
|
||||||
class FilamentWidget extends StatefulWidget {
|
|
||||||
final FilamentController controller;
|
|
||||||
|
|
||||||
const FilamentWidget({Key? key, required this.controller}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_FilamentWidgetState createState() => _FilamentWidgetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _FilamentWidgetState extends State<FilamentWidget> {
|
|
||||||
final _viewId = _nextFilamentCreationId++;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return FilamentViewPlatform.instance
|
|
||||||
.buildView(_viewId, widget.controller.onFilamentViewCreated);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user