move flutter_filament plugin to federated structure
This commit is contained in:
8
flutter_filament_federated/flutter_filament/android/.gitignore
vendored
Normal file
8
flutter_filament_federated/flutter_filament/android/.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
@@ -0,0 +1,61 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
include_directories(../ios/src)
|
||||
include_directories(../ios/include)
|
||||
include_directories(../ios/include/filament)
|
||||
include_directories(src/main/cpp)
|
||||
link_directories(src/main/jniLibs/${ANDROID_ABI})
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
|
||||
|
||||
add_library(flutter_filament_android SHARED
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../ios/src/FlutterFilamentApi.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../ios/src/FlutterFilamentFFIApi.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/FilamentAndroid.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../ios/src/SceneManager.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../ios/src/FilamentViewer.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../ios/src/StreamBufferAdapter.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../ios/src/TimeIt.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../ios/src/camutils/Manipulator.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../ios/src/camutils/Bookmark.cpp"
|
||||
)
|
||||
|
||||
add_library(FILAMENT_SHADERS SHARED
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../ios/include/material/image.c"
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
flutter_filament_android
|
||||
FILAMENT_SHADERS
|
||||
-landroid
|
||||
-llog
|
||||
-lgltfio_core
|
||||
-lfilament
|
||||
-lbackend
|
||||
-lgeometry
|
||||
-lfilameshio
|
||||
-lfilamat
|
||||
-lfilabridge
|
||||
-lcamutils
|
||||
-lfilaflat
|
||||
-ldracodec
|
||||
-libl
|
||||
-lktxreader
|
||||
-limageio
|
||||
-limage
|
||||
-lutils
|
||||
-ltinyexr
|
||||
-lstb
|
||||
-lbluevk
|
||||
-lvkshaders
|
||||
-luberzlib
|
||||
-lsmol-v
|
||||
-luberarchive
|
||||
-lmeshoptimizer
|
||||
-lgeometry
|
||||
-lbasis_transcoder
|
||||
-lGLESv3
|
||||
-lEGL
|
||||
-lpng
|
||||
-lz
|
||||
-lzstd
|
||||
)
|
||||
@@ -0,0 +1,63 @@
|
||||
group 'app.polyvox.filament'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.7.10'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.3.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
compileSdkVersion 33
|
||||
ndkVersion '25.2.9519653'
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 22
|
||||
ndk {
|
||||
abiFilters 'arm64-v8a' // 'x86_64' 'armeabi-v7a'
|
||||
}
|
||||
}
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path "CMakeLists.txt"
|
||||
version "3.18.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation 'net.java.dev.jna:jna:5.10.0@aar'
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
org.gradle.jvmargs=-Xmx1536M
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
5
flutter_filament_federated/flutter_filament/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
flutter_filament_federated/flutter_filament/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
|
||||
@@ -0,0 +1 @@
|
||||
rootProject.name = 'flutter_filament'
|
||||
@@ -0,0 +1,3 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="app.polyvox.filament">
|
||||
</manifest>
|
||||
@@ -0,0 +1,21 @@
|
||||
#include <android/native_window_jni.h>
|
||||
#include <android/native_activity.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include "FlutterFilamentFFIApi.h"
|
||||
|
||||
void* get_native_window_from_surface(
|
||||
jobject surface,
|
||||
JNIEnv* env
|
||||
) {
|
||||
void* window = ANativeWindow_fromSurface(env, surface);
|
||||
return window;
|
||||
}
|
||||
|
||||
// this does nothing, but we need it for JNA to return the correct pointer
|
||||
FilamentRenderCallback make_render_callback_fn_pointer(FilamentRenderCallback callback) {
|
||||
return callback;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package app.polyvox.filament
|
||||
|
||||
import com.sun.jna.ptr.PointerByReference
|
||||
import com.sun.jna.ptr.IntByReference
|
||||
|
||||
import android.view.Surface
|
||||
|
||||
import android.content.res.AssetManager
|
||||
import com.sun.jna.*
|
||||
|
||||
open class ResourceBuffer: Structure(), Structure.ByValue {
|
||||
@JvmField var data: Pointer = Pointer(0);
|
||||
@JvmField var size: Int = 0;
|
||||
@JvmField var id: Int = 0;
|
||||
override fun getFieldOrder(): List<String> {
|
||||
return listOf("data", "size", "id")
|
||||
}
|
||||
}
|
||||
|
||||
interface LoadFilamentResourceFromOwner : Callback {
|
||||
fun loadResourceFromOwner(resourceName: String?, owner: Pointer?): ResourceBuffer
|
||||
}
|
||||
|
||||
interface FreeFilamentResourceFromOwner : Callback {
|
||||
fun freeResourceFromOwner(rb: ResourceBuffer, owner: Pointer?)
|
||||
}
|
||||
|
||||
interface RenderCallback : Callback {
|
||||
fun renderCallback(owner:Pointer?)
|
||||
}
|
||||
|
||||
interface FilamentInterop : Library {
|
||||
|
||||
fun get_native_window_from_surface(surface:Object, env:JNIEnv) : Pointer?;
|
||||
fun make_render_callback_fn_pointer(renderCallback:RenderCallback) : Pointer
|
||||
fun make_resource_loader(loadResourceFromOwner: LoadFilamentResourceFromOwner, freeResource: FreeFilamentResourceFromOwner, owner:Pointer?) : Pointer;
|
||||
fun create_filament_viewer_ffi(context:Pointer, platform:Pointer, loader:Pointer, rc:Pointer, rco:Pointer) : Pointer;
|
||||
fun create_swap_chain_ffi(vieer:Pointer?, surface:Pointer?, width:Int, height:Int)
|
||||
fun set_background_color_ffi(viewer: Pointer?, r: Float, g: Float, b: Float, a: Float)
|
||||
fun update_viewport_and_camera_projection_ffi(viewer: Pointer?, width: Int, height: Int, scale_factor: Float)
|
||||
fun render_ffi(viewer: Pointer?)
|
||||
fun create_filament_viewer(context:Pointer?, platform:Pointer?, loader:Pointer?) : Pointer;
|
||||
fun create_swap_chain(vieer:Pointer?, surface:Pointer?, width:Int, height:Int)
|
||||
fun set_background_color(viewer: Pointer?, r: Float, g: Float, b: Float, a: Float)
|
||||
fun update_viewport_and_camera_projection(viewer: Pointer?, width: Int, height: Int, scale_factor: Float)
|
||||
fun render(viewer: Pointer?, u:Long, a:Pointer?, b:Pointer?, c:Pointer?)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
package app.polyvox.filament
|
||||
|
||||
|
||||
import HotReloadPathHelper
|
||||
import android.app.Activity
|
||||
import android.content.res.AssetManager
|
||||
import android.graphics.*
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.view.Choreographer
|
||||
import android.view.Surface
|
||||
import androidx.annotation.NonNull
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import com.sun.jna.*
|
||||
import io.flutter.FlutterInjector
|
||||
import io.flutter.embedding.engine.FlutterJNI
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityAware
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
|
||||
import io.flutter.embedding.engine.plugins.lifecycle.HiddenLifecycleReference
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||
import io.flutter.view.TextureRegistry.SurfaceTextureEntry
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
class LoadFilamentResourceFromOwnerImpl(plugin:FlutterFilamentPlugin) : LoadFilamentResourceFromOwner {
|
||||
var plugin = plugin
|
||||
override fun loadResourceFromOwner(path: String?, owner: Pointer?): ResourceBuffer {
|
||||
return plugin.loadResourceFromOwner(path, owner)
|
||||
}
|
||||
}
|
||||
|
||||
class FreeFilamentResourceFromOwnerImpl(plugin:FlutterFilamentPlugin) : FreeFilamentResourceFromOwner {
|
||||
var plugin = plugin
|
||||
override fun freeResourceFromOwner(rb: ResourceBuffer, owner: Pointer?) {
|
||||
plugin.freeResourceFromOwner(rb, owner)
|
||||
}
|
||||
}
|
||||
|
||||
class RenderCallbackImpl(plugin:FlutterFilamentPlugin) : RenderCallback {
|
||||
var plugin = plugin
|
||||
override fun renderCallback(owner:Pointer?) {
|
||||
plugin.renderCallback();
|
||||
|
||||
if(!plugin._surface!!.isValid) {
|
||||
Log.e("ERR", "ERR", null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** FlutterFilamentPlugin */
|
||||
class FlutterFilamentPlugin: FlutterPlugin, MethodCallHandler, ActivityAware, LoadFilamentResourceFromOwner, FreeFilamentResourceFromOwner {
|
||||
|
||||
companion object {
|
||||
const val CHANNEL_NAME = "app.polyvox.filament/event"
|
||||
const val TAG = "FilamentPlugin"
|
||||
}
|
||||
|
||||
private lateinit var channel : MethodChannel
|
||||
|
||||
private lateinit var flutterPluginBinding : FlutterPlugin.FlutterPluginBinding
|
||||
|
||||
private var lifecycle: Lifecycle? = null
|
||||
|
||||
private lateinit var _lib : FilamentInterop
|
||||
|
||||
var _surfaceTexture: SurfaceTexture? = null
|
||||
private var _surfaceTextureEntry: SurfaceTextureEntry? = null
|
||||
var _surface: Surface? = null
|
||||
|
||||
private lateinit var activity:Activity
|
||||
|
||||
private var loadResourceWrapper:LoadFilamentResourceFromOwnerImpl = LoadFilamentResourceFromOwnerImpl(this)
|
||||
private var freeResourceWrapper:FreeFilamentResourceFromOwnerImpl = FreeFilamentResourceFromOwnerImpl(this)
|
||||
|
||||
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||
this.flutterPluginBinding = flutterPluginBinding
|
||||
channel = MethodChannel(flutterPluginBinding.binaryMessenger, CHANNEL_NAME)
|
||||
channel.setMethodCallHandler(this)
|
||||
_lib = Native.loadLibrary("flutter_filament_android", 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)
|
||||
}
|
||||
|
||||
val _resources:MutableMap<ResourceBuffer,Memory> = mutableMapOf();
|
||||
var _lastId = 1
|
||||
|
||||
override fun loadResourceFromOwner(path: String?, owner: Pointer?): ResourceBuffer {
|
||||
Log.i("flutter_filament", "Loading resource from path $path")
|
||||
var data:ByteArray? = null
|
||||
if(path!!.startsWith("file://")) {
|
||||
data = File(path!!.substring(6)).readBytes()
|
||||
} else {
|
||||
var assetPath = path
|
||||
if(assetPath.startsWith("asset://")) {
|
||||
assetPath = assetPath!!.substring(8)
|
||||
}
|
||||
val loader = FlutterInjector.instance().flutterLoader()
|
||||
val key = loader.getLookupKeyForAsset(assetPath)
|
||||
val hotReloadPath = HotReloadPathHelper.getAssetPath(key, activity.getPackageName())
|
||||
if (hotReloadPath != null) {
|
||||
data = File(hotReloadPath).readBytes()
|
||||
} else {
|
||||
Log.i("flutter_filament", "Loading resource from main asset bundle at ${assetPath}")
|
||||
|
||||
val assetManager: AssetManager = activity.assets
|
||||
try {
|
||||
data = assetManager.open(key).readBytes()
|
||||
Log.i("flutter_filament", "Loaded ${data.size} bytes")
|
||||
} catch (e:Exception) {
|
||||
Log.e("flutter_filament", "Failed to open asset at ${assetPath}", null)
|
||||
}
|
||||
}
|
||||
}
|
||||
val rb = ResourceBuffer();
|
||||
try {
|
||||
if (data != null) {
|
||||
val dataPtr = Memory(data.size.toLong())
|
||||
dataPtr.write(0, data, 0, data.size)
|
||||
rb.data = dataPtr
|
||||
rb.size = data.size
|
||||
rb.id = _lastId
|
||||
_resources[rb] = dataPtr;
|
||||
_lastId++
|
||||
} else {
|
||||
rb.id = 0
|
||||
rb.size = 0
|
||||
rb.data = Pointer(0)
|
||||
}
|
||||
} catch(e:Exception) {
|
||||
Log.e("flutter_filament", "Error setting resource buffer : $e", null);
|
||||
}
|
||||
rb.write();
|
||||
return rb;
|
||||
|
||||
}
|
||||
|
||||
override fun freeResourceFromOwner(rb: ResourceBuffer, owner: Pointer?) {
|
||||
_resources.remove(rb)
|
||||
}
|
||||
|
||||
fun renderCallback() {
|
||||
// noop, log or check surface.valid() is you want
|
||||
}
|
||||
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||
Log.e("flutter_filament", call.method, null)
|
||||
when (call.method) {
|
||||
"createTexture" -> {
|
||||
if(_surfaceTextureEntry != null) {
|
||||
result.error("TEXTURE_EXISTS", "Texture already exist. Make sure you call destroyTexture first", null)
|
||||
return
|
||||
}
|
||||
val args = call.arguments as List<*>
|
||||
val width = args[0] as Double
|
||||
val height = args[1] as Double
|
||||
if(width <1 || height < 1) {
|
||||
result.error("DIMENSION_MISMATCH","Both dimensions must be greater than zero (you provided $width x $height)", null);
|
||||
return;
|
||||
}
|
||||
Log.i("flutter_filament", "Creating Surface Texture of size ${width}x${height}");
|
||||
|
||||
_surfaceTextureEntry = flutterPluginBinding.textureRegistry.createSurfaceTexture()
|
||||
_surfaceTexture = _surfaceTextureEntry!!.surfaceTexture();
|
||||
_surfaceTexture!!.setDefaultBufferSize(width.toInt(), height.toInt())
|
||||
|
||||
_surface = Surface(_surfaceTexture)
|
||||
|
||||
if(!_surface!!.isValid) {
|
||||
Log.e("ERR", "ERR", null)
|
||||
}
|
||||
|
||||
val nativeWindow = _lib.get_native_window_from_surface(_surface!! as Object, JNIEnv.CURRENT)
|
||||
|
||||
val resultList = listOf(_surfaceTextureEntry!!.id(), Pointer.nativeValue(nativeWindow), null, null )
|
||||
|
||||
result.success(resultList)
|
||||
}
|
||||
"getResourceLoaderWrapper" -> {
|
||||
val resourceLoader = _lib.make_resource_loader(loadResourceWrapper, freeResourceWrapper, Pointer(0))
|
||||
result.success(Pointer.nativeValue(resourceLoader))
|
||||
}
|
||||
"getRenderCallback" -> {
|
||||
val renderCallbackFnPointer = _lib.make_render_callback_fn_pointer(RenderCallbackImpl(this))
|
||||
result.success(listOf(Pointer.nativeValue(renderCallbackFnPointer), 0))
|
||||
}
|
||||
"destroyTexture" -> {
|
||||
_surface!!.release();
|
||||
_surfaceTextureEntry!!.release();
|
||||
_surface = null
|
||||
_surfaceTextureEntry = null
|
||||
result.success(true)
|
||||
}
|
||||
else -> {
|
||||
result.notImplemented()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import java.io.*
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.absolutePathString
|
||||
import kotlin.io.path.listDirectoryEntries
|
||||
import android.util.Log
|
||||
|
||||
|
||||
class HotReloadPathHelper {
|
||||
companion object {
|
||||
fun getAssetPath(path: String, packageName: String): String? {
|
||||
val packagePath = "/data/user/0/${packageName}/code_cache/"
|
||||
Log.v("flutter_filament", "Looking for hot reloaded asset ${path} under package path ${packagePath}")
|
||||
val files = File(packagePath).walkBottomUp().filter {
|
||||
it.path.endsWith(path)
|
||||
}.sortedBy {
|
||||
it.lastModified()
|
||||
}.toList()
|
||||
if(files.size > 0) {
|
||||
Log.v("flutter_filament", "Using hot reloaded asset at ${files.last().path}")
|
||||
return files.last().path;
|
||||
}
|
||||
Log.v("flutter_filament", "No hot reloaded asset found.")
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user