add android integration & make iOS API consistent

add pubspec android plugin

update android CMakeLists

add filament android libs for linking
This commit is contained in:
Nick Fisher
2022-02-06 13:45:45 +08:00
parent 0701932466
commit abb43d351c
38 changed files with 1046 additions and 6646 deletions

View File

@@ -0,0 +1,146 @@
#include "FilamentViewer.hpp"
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <android/native_window_jni.h>
#include <android/log.h>
#include <android/native_activity.h>
using namespace polyvox;
static AAssetManager* am;
std::vector<AAsset*> _assets;
uint64_t id = -1;
static polyvox::ResourceBuffer loadResource(const char* name) {
id++;
AAsset *asset = AAssetManager_open(am, name, AASSET_MODE_BUFFER);
if(asset == nullptr) {
__android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Couldn't locate asset [ %s ]", name);
return polyvox::ResourceBuffer(nullptr, 0, 0);
}
__android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Loading asset [ %s ]", name);
off_t length = AAsset_getLength(asset);
const void * buffer = AAsset_getBuffer(asset);
__android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Read [ %lu ] bytes into buffer", length);
_assets.push_back(asset);
__android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Loaded asset [ %s ] of length %zu", name, length);
return ResourceBuffer(buffer, length, id);
}
static void freeResource(ResourceBuffer rb) {
AAsset_close(_assets.at(rb.id));
}
extern "C" {
void load_skybox(void* viewer, const char* skyboxPath, const char* iblPath) {
((FilamentViewer*)viewer)->loadSkybox(skyboxPath, iblPath, am);
}
void load_glb(void* viewer, const char* assetPath) {
((FilamentViewer*)viewer)->loadGlb(assetPath);
}
void load_gltf(void* viewer, const char* assetPath, const char* relativePath) {
((FilamentViewer*)viewer)->loadGltf(assetPath, relativePath);
}
void set_camera(void* viewer, const char* nodeName) {
((FilamentViewer*)viewer)->setCamera(nodeName);
}
void* filament_viewer_new(
jobject surface,
const char* opaqueShaderPath,
const char* fadeShaderPath,
JNIEnv* env,
jobject assetManager
) {
ANativeWindow* layer = ANativeWindow_fromSurface(env, surface);
am = AAssetManager_fromJava(env, assetManager);
return new FilamentViewer((void*)layer, opaqueShaderPath, fadeShaderPath, loadResource, freeResource);
}
void render(
void* viewer
) {
((FilamentViewer*)viewer)->render();
}
void destroy_swap_chain(void* viewer) {
((FilamentViewer*)viewer)->destroySwapChain();
}
void create_swap_chain(void* viewer, jobject surface, JNIEnv* env) {
ANativeWindow* layer = ANativeWindow_fromSurface(env, surface);
if(!layer) {
__android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Couldn't get native window from surface");
return;
}
((FilamentViewer*)viewer)->createSwapChain(layer);
}
void* get_renderer(void* viewer) {
return ((FilamentViewer*)viewer)->getRenderer();
}
void update_viewport_and_camera_projection(void* viewer, int width, int height, float scaleFactor) {
return ((FilamentViewer*)viewer)->updateViewportAndCameraProjection(width, height, scaleFactor);
}
void scroll(void* viewer, float x, float y , float z) {
return ((FilamentViewer*)viewer)->manipulator->scroll(x, y, z);
}
void grab_begin(void* viewer, int x, int y, bool pan) {
__android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Grab begin at %d %d %d", x, y, pan);
((FilamentViewer*)viewer)->manipulator->grabBegin(x, y, pan);
}
void grab_update(void* viewer, int x, int y) {
__android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Grab update at %d %d %d", x, y);
((FilamentViewer*)viewer)->manipulator->grabUpdate(x, y);
}
void grab_end(void* viewer) {
((FilamentViewer*)viewer)->manipulator->grabEnd();
}
void apply_weights(void* viewer, float* weights, int count) {
((FilamentViewer*)viewer)->applyWeights(weights, count);
}
void get_target_names(void* viewer, char* meshName, char*** outPtr, int* countPtr ) {
StringList names = ((FilamentViewer*)viewer)->getTargetNames(meshName);
*countPtr = names.count;
*outPtr = (char**)malloc(sizeof(char*) * names.count);
__android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Got %d names", names.count);
for(int i = 0; i < names.count; i++) {
std::string as_str(names.strings[i]);
(*outPtr)[i] = (char*)malloc(sizeof(char) * as_str.length());
strcpy((*outPtr)[i], as_str.c_str());
}
}
void free_pointer(void** ptr, int size) {
for(int i = 0; i < size; i++) {
free(ptr[i]);
}
}
void release_source_assets(void* viewer) {
((FilamentViewer*)viewer)->releaseSourceAssets();
}
}

View File

@@ -0,0 +1,15 @@
#include "FilamentViewer.hpp"
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <android/native_window_jni.h>
#include <android/log.h>
void load_skybox(void* viewer, const char* skyboxPath, const char* iblPath);
void* filament_viewer_new(
void* layer,
const char* opaqueShaderPath,
const char* fadeShaderPath,
void* assetManager
);
}

View File

@@ -0,0 +1,61 @@
package app.polyvox.filament
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.view.Surface
import android.content.res.AssetManager
import java.nio.ByteBuffer
interface FilamentInterop : Library {
fun filament_viewer_new(
layer:Object,
opaqueShaderPath:String,
fadeShaderPath:String,
env:JNIEnv,
am:AssetManager
) : Pointer;
fun load_skybox(viewer:Pointer, skyboxPath:String, iblPath:String) : Pointer;
fun load_glb(viewer:Pointer, uri:String) : Pointer;
fun load_gltf(viewer:Pointer, uri:String, relativeResourcePath:String) : Pointer;
fun set_camera(viewer:Pointer, nodeName:String) : Pointer;
fun render(viewer:Pointer);
fun create_swap_chain(viewer:Pointer, surface:Surface, env:JNIEnv);
fun destroy_swap_chain(viewer:Pointer);
fun update_viewport_and_camera_projection(viewer:Pointer, width:Int, height:Int, scaleFactor:Float);
fun scroll(viewer:Pointer, x:Float, y:Float, z:Float);
fun grab_begin(viewer:Pointer, x:Int, y:Int, pan:Boolean)
fun grab_update(viewer:Pointer, x:Int, y:Int)
fun grab_end(viewer:Pointer)
fun apply_weights(viewer:Pointer, weights:FloatArray, size:Int);
fun get_target_names(viewer:Pointer, meshName:String, outPtr:PointerByReference, outLen:IntByReference);
fun free_pointer(ptr:Pointer, size:Int)
fun release_source_assets(viewer:Pointer)
}

View File

@@ -0,0 +1,298 @@
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.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 com.google.android.filament.android.DisplayHelper
import android.hardware.display.DisplayManager
import com.google.android.filament.android.*
import com.google.android.filament.*
import com.google.android.filament.SwapChain
import com.google.android.filament.utils.*
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 lateinit var displayHelper : DisplayHelper
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))
choreographer = Choreographer.getInstance()
_view.setZOrderOnTop(false)
_view.holder.setFormat(PixelFormat.OPAQUE)
_view.holder.addCallback (object : SurfaceHolder.Callback {
override fun surfaceChanged(holder:SurfaceHolder, format:Int, width:Int, height:Int) {
_lib.update_viewport_and_camera_projection(_viewer!!, width, height, 1.0f);
}
override fun surfaceCreated(holder:SurfaceHolder) {
_lib.destroy_swap_chain(_viewer!!)
_lib.create_swap_chain(_viewer!!, _view.holder.surface, JNIEnv.CURRENT)
}
override fun surfaceDestroyed(holder:SurfaceHolder) {
_lib.destroy_swap_chain(_viewer!!)
}
})
assetManager = context.assets
_viewer = _lib.filament_viewer_new(
_view.holder.surface as Object,
"unused",
"unused",
JNIEnv.CURRENT,
assetManager)
choreographer.postFrameCallback(frameScheduler)
val mDisplayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
activity.window.setFormat(PixelFormat.RGBA_8888)
uiHelper = UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK)
uiHelper.renderCallback = SurfaceCallback()
uiHelper.attachTo(_view)
}
override fun onFlutterViewAttached(flutterView:View) {
}
override fun dispose() {
_methodChannel.setMethodCallHandler(null)
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"loadSkybox" -> {
val args = call.arguments as ArrayList<Any?>
val loader = FlutterInjector.instance().flutterLoader()
_lib.load_skybox(_viewer!!, loader.getLookupKeyForAsset(args[0] as String), loader.getLookupKeyForAsset(args[1] as String))
result.success("OK");
}
"loadGlb" -> {
if (_viewer == null)
return;
val loader = FlutterInjector.instance().flutterLoader()
_lib.load_glb(
_viewer!!,
loader.getLookupKeyForAsset(call.arguments as String)
)
result.success("OK");
}
"loadGltf" -> {
if (_viewer == null)
return;
val args = call.arguments as ArrayList<Any?>
val loader = FlutterInjector.instance().flutterLoader()
_lib.load_gltf(
_viewer!!,
loader.getLookupKeyForAsset(args[0] as String),
loader.getLookupKeyForAsset(args[1] as String)
)
result.success("OK");
}
"setCamera" -> {
if (_viewer == null)
return;
_lib.set_camera(
_viewer!!,
call.arguments as String
)
result.success("OK");
}
"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 arrPtr = PointerByReference();
val countPtr = IntByReference();
_lib.get_target_names(_viewer!!, call.arguments as String, arrPtr, countPtr)
val names = arrPtr.value.getStringArray(0, countPtr.value);
_lib.free_pointer(arrPtr.value, countPtr.getValue())
val namesAsList = names.toCollection(ArrayList())
result.success(namesAsList)
}
"applyWeights" -> {
if(_viewer == null)
return;
val weights = call.arguments as ArrayList<Float>;
_lib.apply_weights(_viewer!!, weights.toFloatArray(), weights.size)
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");
}
"releaseSourceAssets" -> {
_lib.release_source_assets(_viewer!!)
result.success("OK");
}
}
}
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)
// modelViewer.animator?.apply {
// if (animationCount > 0) {
// val elapsedTimeSeconds = (frameTimeNanos - startTime).toDouble() / 1_000_000_000
// applyAnimation(0, elapsedTimeSeconds.toFloat())
// }
// updateBoneMatrices()
// }
_lib.render(_viewer!!)
}
}
}

View File

@@ -0,0 +1,20 @@
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)
}
}

View File

@@ -2,26 +2,53 @@ package app.polyvox.filament
import androidx.annotation.NonNull
import androidx.lifecycle.Lifecycle
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.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.plugins.lifecycle.HiddenLifecycleReference
/** PolyvoxFilamentPlugin */
class PolyvoxFilamentPlugin: FlutterPlugin, MethodCallHandler {
class PolyvoxFilamentPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
companion object {
const val VIEW_TYPE = "app.polyvox.filament/filament_view"
}
/// 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 referene to the plugin binding so we can defer construction of a FilamentViewFactory
/// until Activity is attached.
private lateinit var flutterPluginBinding : FlutterPlugin.FlutterPluginBinding
private var lifecycle: Lifecycle? = null
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "polyvox_filament")
this.flutterPluginBinding = flutterPluginBinding
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "app.polyvox.filament")
channel.setMethodCallHandler(this)
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
lifecycle = (binding.lifecycle as? HiddenLifecycleReference)?.lifecycle
flutterPluginBinding
.platformViewRegistry
.registerViewFactory(VIEW_TYPE, FilamentViewFactory(binding.activity, flutterPluginBinding.binaryMessenger))
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
} else {
@@ -32,4 +59,17 @@ class PolyvoxFilamentPlugin: FlutterPlugin, MethodCallHandler {
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
onAttachedToActivity(binding)
}
override fun onDetachedFromActivityForConfigChanges() {
onDetachedFromActivity()
}
override fun onDetachedFromActivity() {
lifecycle = null
}
}

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a8f24f2cb744814f1502a1a29164ca9d08c7f844a83e9d077f3831a1fd13c67a
size 1723112

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8cbdbc57e41114b428fbd6e417a4cafbffd83be8994ea0da44b2cdad6abc4e4f
size 430944

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1d2c3b8bf5dae7441fc7d16a345fd7a3fd170e0bcd0f0f054b16ba4e64a289d0
size 2152000

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fa2185681699dc2f72ed79bf0f3b227c8a936047e1d0f35edf2854e2dbb0319d
size 8758184

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:16fbe91a5f417dab44046090e918e3f3fb119cc7aa7ce72e68b1692687915c90
size 1378344

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8cf27e32b857a98c8b8a0862ec55d8b4eb06d2f22d6d7502865f8ed5dd21db78
size 344268

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:30734d9a58740c04ee282843c2a0db455d4daa239a5d15bf2f5d8c3ef04f21c8
size 2346424

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b1498fff038d1bcdb7e52d4369b61e71ea33154ebf769934dbcadafb0bbb814b
size 8463116

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:134ff007e621892b60c01be75173b94fd02be5b94f6818f76c0e639d85003446
size 1880488

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a1699bf6ab28d55e49d0a8d4d7ace8043b9477036ae8760f14c4c14deeba94d6
size 461552

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bc2e220862a8179129fbfd5fe944e725eec57335c7014bea7ba538d62f6d93de
size 2327272

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8a82bd46c8f0e3ed5970e873c92f219cdbed2d02d75855ffb889ac3821cf53c8
size 8846488

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e227eefd03b2ef8b79366fc443efe11ec87768c284f33dcfdc8906e3985fc7a6
size 1840928

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:43fa88beba41cce0ffa2f25418cee021946818882b91ad6da63194e19b34fca6
size 469208

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f5c0abca686c763b814af96ada136cb872b9d776ebad063b8293bb20488c4a13
size 2141136

View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:39eaf4b8d74194db66401e754527fe3d01a457f829de559ef2503e76b8ffd9e6
size 8859584