update example project
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="app.polyvox.polyvox_filament_example">
|
||||
package="app.polyvox.filament_example">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package app.polyvox.polyvox_filament_example
|
||||
package app.polyvox.filament_example
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
class MainActivity: FlutterActivity() {
|
||||
|
||||
}
|
||||
@@ -1,6 +1,429 @@
|
||||
package app.polyvox.polyvox_filament_example
|
||||
package app.polyvox.filament_example
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
// import com.google.android.filament.gltf.*
|
||||
import com.google.android.filament.utils.*
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
import android.view.GestureDetector
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import com.google.android.filament.Fence
|
||||
import com.google.android.filament.IndirectLight
|
||||
import com.google.android.filament.Skybox
|
||||
import com.google.android.filament.View
|
||||
import com.google.android.filament.utils.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.RandomAccessFile
|
||||
import java.nio.Buffer
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
class MainActivity: FlutterActivity() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
|
||||
|
||||
// import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
// class MainActivity : FlutterActivity() {
|
||||
|
||||
// // companion object {
|
||||
// // // // Load the library for the utility layer, which in turn loads gltfio and the Filament core.
|
||||
// // // init { Utils.init() }
|
||||
// // private const val TAG = "gltf-viewer"
|
||||
// // }
|
||||
|
||||
// // private lateinit var surfaceView : SurfaceView
|
||||
// // private lateinit var choreographer: Choreographer
|
||||
// // private val frameScheduler = FrameCallback()
|
||||
// // private lateinit var modelViewer: ModelViewer
|
||||
// // private lateinit var titlebarHint: TextView
|
||||
// // private val doubleTapListener = DoubleTapListener()
|
||||
// // private lateinit var doubleTapDetector: GestureDetector
|
||||
// // private var remoteServer: RemoteServer? = null
|
||||
// // private var statusToast: Toast? = null
|
||||
// // private var statusText: String? = null
|
||||
// // private var latestDownload: String? = null
|
||||
// // private val automation = AutomationEngine()
|
||||
// // private var loadStartTime = 0L
|
||||
// // private var loadStartFence: Fence? = null
|
||||
// // private val viewerContent = AutomationEngine.ViewerContent()
|
||||
|
||||
// // @SuppressLint("ClickableViewAccessibility")
|
||||
// // override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// // super.onCreate(savedInstanceState)
|
||||
// // surfaceView = SurfaceView(this)
|
||||
// // setContentView( surfaceView)
|
||||
// // // window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
|
||||
// // // // titlebarHint = findViewById(R.id.user_hint)
|
||||
// // // // surfaceView = findViewById(R.id.main_sv)
|
||||
// // // choreographer = Choreographer.getInstance()
|
||||
|
||||
// // // doubleTapDetector = GestureDetector(applicationContext, doubleTapListener)
|
||||
|
||||
// // // modelViewer = ModelViewer(surfaceView)
|
||||
// // // viewerContent.view = modelViewer.view
|
||||
// // // viewerContent.sunlight = modelViewer.light
|
||||
// // // viewerContent.lightManager = modelViewer.engine.lightManager
|
||||
// // // viewerContent.scene = modelViewer.scene
|
||||
// // // viewerContent.renderer = modelViewer.renderer
|
||||
|
||||
// // // surfaceView.setOnTouchListener { _, event ->
|
||||
// // // modelViewer.onTouchEvent(event)
|
||||
// // // doubleTapDetector.onTouchEvent(event)
|
||||
// // // true
|
||||
// // // }
|
||||
|
||||
// // // createDefaultRenderables()
|
||||
// // // createIndirectLight()
|
||||
|
||||
// // // setStatusText("To load a new model, go to the above URL on your host machine.")
|
||||
|
||||
// // // val view = modelViewer.view
|
||||
|
||||
// // // /*
|
||||
// // // * Note: The settings below are overriden when connecting to the remote UI.
|
||||
// // // */
|
||||
|
||||
// // // // on mobile, better use lower quality color buffer
|
||||
// // // view.renderQuality = view.renderQuality.apply {
|
||||
// // // hdrColorBuffer = View.QualityLevel.MEDIUM
|
||||
// // // }
|
||||
|
||||
// // // // dynamic resolution often helps a lot
|
||||
// // // view.dynamicResolutionOptions = view.dynamicResolutionOptions.apply {
|
||||
// // // enabled = true
|
||||
// // // quality = View.QualityLevel.MEDIUM
|
||||
// // // }
|
||||
|
||||
// // // // MSAA is needed with dynamic resolution MEDIUM
|
||||
// // // view.multiSampleAntiAliasingOptions = view.multiSampleAntiAliasingOptions.apply {
|
||||
// // // enabled = true
|
||||
// // // }
|
||||
|
||||
// // // // FXAA is pretty cheap and helps a lot
|
||||
// // // view.antiAliasing = View.AntiAliasing.FXAA;
|
||||
|
||||
// // // // ambient occlusion is the cheapest effect that adds a lot of quality
|
||||
// // // view.ambientOcclusionOptions = view.ambientOcclusionOptions.apply {
|
||||
// // // enabled = true
|
||||
// // // }
|
||||
|
||||
// // // // bloom is pretty expensive but adds a fair amount of realism
|
||||
// // // view.bloomOptions = view.bloomOptions.apply {
|
||||
// // // enabled = true
|
||||
// // // }
|
||||
|
||||
// // // remoteServer = RemoteServer(8082)
|
||||
// // }
|
||||
|
||||
// // private fun createDefaultRenderables() {
|
||||
// // val buffer = assets.open("models/scene.gltf").use { input ->
|
||||
// // val bytes = ByteArray(input.available())
|
||||
// // input.read(bytes)
|
||||
// // ByteBuffer.wrap(bytes)
|
||||
// // }
|
||||
|
||||
// // modelViewer.loadModelGltfAsync(buffer) { uri -> readCompressedAsset("models/$uri") }
|
||||
// // updateRootTransform()
|
||||
// // }
|
||||
|
||||
// // private fun createIndirectLight() {
|
||||
// // val engine = modelViewer.engine
|
||||
// // val scene = modelViewer.scene
|
||||
// // val ibl = "default_env"
|
||||
// // readCompressedAsset("envs/$ibl/${ibl}_ibl.ktx").let {
|
||||
// // scene.indirectLight = KTXLoader.createIndirectLight(engine, it)
|
||||
// // scene.indirectLight!!.intensity = 30_000.0f
|
||||
// // viewerContent.indirectLight = modelViewer.scene.indirectLight
|
||||
// // }
|
||||
// // readCompressedAsset("envs/$ibl/${ibl}_skybox.ktx").let {
|
||||
// // scene.skybox = KTXLoader.createSkybox(engine, it)
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// // private fun readCompressedAsset(assetName: String): ByteBuffer {
|
||||
// // val input = assets.open(assetName)
|
||||
// // val bytes = ByteArray(input.available())
|
||||
// // input.read(bytes)
|
||||
// // return ByteBuffer.wrap(bytes)
|
||||
// // }
|
||||
|
||||
// // private fun clearStatusText() {
|
||||
// // statusToast?.let {
|
||||
// // it.cancel()
|
||||
// // statusText = null
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// // private fun setStatusText(text: String) {
|
||||
// // runOnUiThread {
|
||||
// // if (statusToast == null || statusText != text) {
|
||||
// // statusText = text
|
||||
// // statusToast = Toast.makeText(applicationContext, text, Toast.LENGTH_SHORT)
|
||||
// // statusToast!!.show()
|
||||
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// // private suspend fun loadGlb(message: RemoteServer.ReceivedMessage) {
|
||||
// // withContext(Dispatchers.Main) {
|
||||
// // modelViewer.destroyModel()
|
||||
// // modelViewer.loadModelGlb(message.buffer)
|
||||
// // updateRootTransform()
|
||||
// // loadStartTime = System.nanoTime()
|
||||
// // loadStartFence = modelViewer.engine.createFence()
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// // private suspend fun loadHdr(message: RemoteServer.ReceivedMessage) {
|
||||
// // withContext(Dispatchers.Main) {
|
||||
// // val engine = modelViewer.engine
|
||||
// // val equirect = HDRLoader.createTexture(engine, message.buffer)
|
||||
// // if (equirect == null) {
|
||||
// // setStatusText("Could not decode HDR file.")
|
||||
// // } else {
|
||||
// // setStatusText("Successfully decoded HDR file.")
|
||||
|
||||
// // val context = IBLPrefilterContext(engine)
|
||||
// // val equirectToCubemap = IBLPrefilterContext.EquirectangularToCubemap(context)
|
||||
// // val skyboxTexture = equirectToCubemap.run(equirect)!!
|
||||
// // engine.destroyTexture(equirect)
|
||||
|
||||
// // val specularFilter = IBLPrefilterContext.SpecularFilter(context)
|
||||
// // val reflections = specularFilter.run(skyboxTexture)
|
||||
|
||||
// // val ibl = IndirectLight.Builder()
|
||||
// // .reflections(reflections)
|
||||
// // .intensity(30000.0f)
|
||||
// // .build(engine)
|
||||
|
||||
// // val sky = Skybox.Builder().environment(skyboxTexture).build(engine)
|
||||
|
||||
// // specularFilter.destroy();
|
||||
// // equirectToCubemap.destroy();
|
||||
// // context.destroy();
|
||||
|
||||
// // // destroy the previous IBl
|
||||
// // engine.destroyIndirectLight(modelViewer.scene.indirectLight!!);
|
||||
// // engine.destroySkybox(modelViewer.scene.skybox!!);
|
||||
|
||||
// // modelViewer.scene.skybox = sky
|
||||
// // modelViewer.scene.indirectLight = ibl
|
||||
// // viewerContent.indirectLight = ibl
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// // private suspend fun loadZip(message: RemoteServer.ReceivedMessage) {
|
||||
// // // To alleviate memory pressure, remove the old model before deflating the zip.
|
||||
// // withContext(Dispatchers.Main) {
|
||||
// // modelViewer.destroyModel()
|
||||
// // }
|
||||
|
||||
// // // Large zip files should first be written to a file to prevent OOM.
|
||||
// // // It is also crucial that we null out the message "buffer" field.
|
||||
// // val (zipStream, zipFile) = withContext(Dispatchers.IO) {
|
||||
// // val file = File.createTempFile("incoming", "zip", cacheDir)
|
||||
// // val raf = RandomAccessFile(file, "rw")
|
||||
// // raf.getChannel().write(message.buffer);
|
||||
// // message.buffer = null
|
||||
// // raf.seek(0)
|
||||
// // Pair(FileInputStream(file), file)
|
||||
// // }
|
||||
|
||||
// // // Deflate each resource using the IO dispatcher, one by one.
|
||||
// // var gltfPath: String? = null
|
||||
// // var outOfMemory: String? = null
|
||||
// // val pathToBufferMapping = withContext(Dispatchers.IO) {
|
||||
// // val deflater = ZipInputStream(zipStream)
|
||||
// // val mapping = HashMap<String, Buffer>()
|
||||
// // while (true) {
|
||||
// // val entry = deflater.nextEntry ?: break
|
||||
// // if (entry.isDirectory) continue
|
||||
|
||||
// // // This isn't strictly required, but as an optimization
|
||||
// // // we ignore common junk that often pollutes ZIP files.
|
||||
// // if (entry.name.startsWith("__MACOSX")) continue
|
||||
// // if (entry.name.startsWith(".DS_Store")) continue
|
||||
|
||||
// // val uri = entry.name
|
||||
// // val byteArray: ByteArray? = try {
|
||||
// // deflater.readBytes()
|
||||
// // }
|
||||
// // catch (e: OutOfMemoryError) {
|
||||
// // outOfMemory = uri
|
||||
// // break
|
||||
// // }
|
||||
// // Log.i(TAG, "Deflated ${byteArray!!.size} bytes from $uri")
|
||||
// // val buffer = ByteBuffer.wrap(byteArray)
|
||||
// // mapping[uri] = buffer
|
||||
// // if (uri.endsWith(".gltf") || uri.endsWith(".glb")) {
|
||||
// // gltfPath = uri
|
||||
// // }
|
||||
// // }
|
||||
// // mapping
|
||||
// // }
|
||||
|
||||
// // zipFile.delete()
|
||||
|
||||
// // if (gltfPath == null) {
|
||||
// // setStatusText("Could not find .gltf or .glb in the zip.")
|
||||
// // return
|
||||
// // }
|
||||
|
||||
// // if (outOfMemory != null) {
|
||||
// // setStatusText("Out of memory while deflating $outOfMemory")
|
||||
// // return
|
||||
// // }
|
||||
|
||||
// // val gltfBuffer = pathToBufferMapping[gltfPath]!!
|
||||
|
||||
// // // The gltf is often not at the root level (e.g. if a folder is zipped) so
|
||||
// // // we need to extract its path in order to resolve the embedded uri strings.
|
||||
// // var gltfPrefix = gltfPath!!.substringBeforeLast('/', "")
|
||||
// // if (gltfPrefix.isNotEmpty()) {
|
||||
// // gltfPrefix += "/"
|
||||
// // }
|
||||
|
||||
// // withContext(Dispatchers.Main) {
|
||||
// // if (gltfPath!!.endsWith(".glb")) {
|
||||
// // modelViewer.loadModelGlb(gltfBuffer)
|
||||
// // } else {
|
||||
// // modelViewer.loadModelGltf(gltfBuffer) { uri ->
|
||||
// // val path = gltfPrefix + uri
|
||||
// // if (!pathToBufferMapping.contains(path)) {
|
||||
// // Log.e(TAG, "Could not find $path in the zip.")
|
||||
// // setStatusText("Zip is missing $path")
|
||||
// // }
|
||||
// // pathToBufferMapping[path]
|
||||
// // }
|
||||
// // }
|
||||
// // updateRootTransform()
|
||||
// // loadStartTime = System.nanoTime()
|
||||
// // loadStartFence = modelViewer.engine.createFence()
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// // override fun onResume() {
|
||||
// // super.onResume()
|
||||
// // choreographer.postFrameCallback(frameScheduler)
|
||||
// // }
|
||||
|
||||
// // override fun onPause() {
|
||||
// // super.onPause()
|
||||
// // choreographer.removeFrameCallback(frameScheduler)
|
||||
// // }
|
||||
|
||||
// // override fun onDestroy() {
|
||||
// // super.onDestroy()
|
||||
// // choreographer.removeFrameCallback(frameScheduler)
|
||||
// // remoteServer?.close()
|
||||
// // }
|
||||
|
||||
// // fun loadModelData(message: RemoteServer.ReceivedMessage) {
|
||||
// // Log.i(TAG, "Downloaded model ${message.label} (${message.buffer.capacity()} bytes)")
|
||||
// // clearStatusText()
|
||||
// // // titlebarHint.text = message.label
|
||||
// // CoroutineScope(Dispatchers.IO).launch {
|
||||
// // if (message.label.endsWith(".zip")) {
|
||||
// // loadZip(message)
|
||||
// // } else if (message.label.endsWith(".hdr")) {
|
||||
// // loadHdr(message)
|
||||
// // } else {
|
||||
// // loadGlb(message)
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// // fun loadSettings(message: RemoteServer.ReceivedMessage) {
|
||||
// // val json = StandardCharsets.UTF_8.decode(message.buffer).toString()
|
||||
// // viewerContent.assetLights = modelViewer.asset?.lightEntities
|
||||
// // automation.applySettings(json, viewerContent)
|
||||
// // modelViewer.view.colorGrading = automation.getColorGrading(modelViewer.engine)
|
||||
// // modelViewer.cameraFocalLength = automation.viewerOptions.cameraFocalLength
|
||||
// // updateRootTransform()
|
||||
// // }
|
||||
|
||||
// // private fun updateRootTransform() {
|
||||
// // if (automation.viewerOptions.autoScaleEnabled) {
|
||||
// // modelViewer.transformToUnitCube()
|
||||
// // } else {
|
||||
// // modelViewer.clearRootTransform()
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// // inner class FrameCallback : Choreographer.FrameCallback {
|
||||
// // private val startTime = System.nanoTime()
|
||||
// // override fun doFrame(frameTimeNanos: Long) {
|
||||
// // choreographer.postFrameCallback(this)
|
||||
|
||||
// // loadStartFence?.let {
|
||||
// // if (it.wait(Fence.Mode.FLUSH, 0) == Fence.FenceStatus.CONDITION_SATISFIED) {
|
||||
// // val end = System.nanoTime()
|
||||
// // val total = (end - loadStartTime) / 1_000_000
|
||||
// // Log.i(TAG, "The Filament backend took $total ms to load the model geometry.")
|
||||
// // modelViewer.engine.destroyFence(it)
|
||||
// // loadStartFence = null
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// // modelViewer.animator?.apply {
|
||||
// // if (animationCount > 0) {
|
||||
// // val elapsedTimeSeconds = (frameTimeNanos - startTime).toDouble() / 1_000_000_000
|
||||
// // applyAnimation(0, elapsedTimeSeconds.toFloat())
|
||||
// // }
|
||||
// // updateBoneMatrices()
|
||||
// // }
|
||||
|
||||
// // modelViewer.render(frameTimeNanos)
|
||||
|
||||
// // // Check if a new download is in progress. If so, let the user know with toast.
|
||||
// // val currentDownload = remoteServer?.peekIncomingLabel()
|
||||
// // if (RemoteServer.isBinary(currentDownload) && currentDownload != latestDownload) {
|
||||
// // latestDownload = currentDownload
|
||||
// // Log.i(TAG, "Downloading $currentDownload")
|
||||
// // setStatusText("Downloading $currentDownload")
|
||||
// // }
|
||||
|
||||
// // // Check if a new message has been fully received from the client.
|
||||
// // val message = remoteServer?.acquireReceivedMessage()
|
||||
// // if (message != null) {
|
||||
// // if (message.label == latestDownload) {
|
||||
// // latestDownload = null
|
||||
// // }
|
||||
// // if (RemoteServer.isJson(message.label)) {
|
||||
// // loadSettings(message)
|
||||
// // } else {
|
||||
// // loadModelData(message)
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// // // Just for testing purposes, this releases the current model and reloads the default model.
|
||||
// // inner class DoubleTapListener : GestureDetector.SimpleOnGestureListener() {
|
||||
// // override fun onDoubleTap(e: MotionEvent?): Boolean {
|
||||
// // modelViewer.destroyModel()
|
||||
// // createDefaultRenderables()
|
||||
// // return super.onDoubleTap(e)
|
||||
// // }
|
||||
// // }
|
||||
// }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="app.polyvox.polyvox_filament_example">
|
||||
package="app.polyvox.filament_example">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.3.50'
|
||||
ext.kotlin_version = '1.6.0'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
@@ -8,6 +8,7 @@ buildscript {
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
include ':app'
|
||||
|
||||
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
|
||||
def properties = new Properties()
|
||||
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
|
||||
|
||||
assert localPropertiesFile.exists()
|
||||
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
|
||||
def plugins = new Properties()
|
||||
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
|
||||
if (pluginsFile.exists()) {
|
||||
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
|
||||
}
|
||||
|
||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
|
||||
plugins.each { name, path ->
|
||||
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
|
||||
include ":$name"
|
||||
project(":$name").projectDir = pluginDirectory
|
||||
}
|
||||
BIN
example/assets/cube.bin
Normal file
BIN
example/assets/cube.bin
Normal file
Binary file not shown.
772
example/assets/cube.gltf
Normal file
772
example/assets/cube.gltf
Normal file
@@ -0,0 +1,772 @@
|
||||
{
|
||||
"asset" : {
|
||||
"generator" : "Khronos glTF Blender I/O v1.6.16",
|
||||
"version" : "2.0"
|
||||
},
|
||||
"scene" : 0,
|
||||
"scenes" : [
|
||||
{
|
||||
"name" : "Scene",
|
||||
"nodes" : [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes" : [
|
||||
{
|
||||
"mesh" : 0,
|
||||
"name" : "Cube"
|
||||
},
|
||||
{
|
||||
"name" : "Light",
|
||||
"rotation" : [
|
||||
0.16907575726509094,
|
||||
0.7558803558349609,
|
||||
-0.27217137813568115,
|
||||
0.570947527885437
|
||||
],
|
||||
"translation" : [
|
||||
4.076245307922363,
|
||||
5.903861999511719,
|
||||
-1.0054539442062378
|
||||
]
|
||||
},
|
||||
{
|
||||
"name" : "Camera",
|
||||
"rotation" : [
|
||||
0.483536034822464,
|
||||
0.33687159419059753,
|
||||
-0.20870360732078552,
|
||||
0.7804827094078064
|
||||
],
|
||||
"translation" : [
|
||||
7.358891487121582,
|
||||
4.958309173583984,
|
||||
6.925790786743164
|
||||
]
|
||||
},
|
||||
{
|
||||
"mesh" : 1,
|
||||
"name" : "Cone",
|
||||
"translation" : [
|
||||
0,
|
||||
0,
|
||||
-4.3612141609191895
|
||||
]
|
||||
}
|
||||
],
|
||||
"materials" : [
|
||||
{
|
||||
"doubleSided" : true,
|
||||
"name" : "Material",
|
||||
"pbrMetallicRoughness" : {
|
||||
"baseColorFactor" : [
|
||||
0.800000011920929,
|
||||
0.800000011920929,
|
||||
0.800000011920929,
|
||||
1
|
||||
],
|
||||
"metallicFactor" : 0,
|
||||
"roughnessFactor" : 0.4000000059604645
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes" : [
|
||||
{
|
||||
"extras" : {
|
||||
"targetNames" : [
|
||||
"Key 1",
|
||||
"Key 2",
|
||||
"Key 3",
|
||||
"Key 4",
|
||||
"Key 5",
|
||||
"Key 6",
|
||||
"Key 7",
|
||||
"Key 8"
|
||||
]
|
||||
},
|
||||
"name" : "Cube",
|
||||
"primitives" : [
|
||||
{
|
||||
"attributes" : {
|
||||
"POSITION" : 0,
|
||||
"NORMAL" : 1,
|
||||
"TEXCOORD_0" : 2
|
||||
},
|
||||
"indices" : 3,
|
||||
"material" : 0,
|
||||
"targets" : [
|
||||
{
|
||||
"POSITION" : 4,
|
||||
"NORMAL" : 5
|
||||
},
|
||||
{
|
||||
"POSITION" : 6,
|
||||
"NORMAL" : 7
|
||||
},
|
||||
{
|
||||
"POSITION" : 8,
|
||||
"NORMAL" : 9
|
||||
},
|
||||
{
|
||||
"POSITION" : 10,
|
||||
"NORMAL" : 11
|
||||
},
|
||||
{
|
||||
"POSITION" : 12,
|
||||
"NORMAL" : 13
|
||||
},
|
||||
{
|
||||
"POSITION" : 14,
|
||||
"NORMAL" : 15
|
||||
},
|
||||
{
|
||||
"POSITION" : 16,
|
||||
"NORMAL" : 17
|
||||
},
|
||||
{
|
||||
"POSITION" : 18,
|
||||
"NORMAL" : 19
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"weights" : [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"extras" : {
|
||||
"targetNames" : [
|
||||
"Key 1",
|
||||
"Key 2",
|
||||
"Key 3",
|
||||
"Key 4",
|
||||
"Key 5",
|
||||
"Key 6"
|
||||
]
|
||||
},
|
||||
"name" : "Cone",
|
||||
"primitives" : [
|
||||
{
|
||||
"attributes" : {
|
||||
"POSITION" : 20,
|
||||
"NORMAL" : 21,
|
||||
"TEXCOORD_0" : 22
|
||||
},
|
||||
"indices" : 23,
|
||||
"targets" : [
|
||||
{
|
||||
"POSITION" : 24,
|
||||
"NORMAL" : 25
|
||||
},
|
||||
{
|
||||
"POSITION" : 26,
|
||||
"NORMAL" : 27
|
||||
},
|
||||
{
|
||||
"POSITION" : 28,
|
||||
"NORMAL" : 29
|
||||
},
|
||||
{
|
||||
"POSITION" : 30,
|
||||
"NORMAL" : 31
|
||||
},
|
||||
{
|
||||
"POSITION" : 32,
|
||||
"NORMAL" : 33
|
||||
},
|
||||
{
|
||||
"POSITION" : 34,
|
||||
"NORMAL" : 35
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"weights" : [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"accessors" : [
|
||||
{
|
||||
"bufferView" : 0,
|
||||
"componentType" : 5126,
|
||||
"count" : 264,
|
||||
"max" : [
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"min" : [
|
||||
-1,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 1,
|
||||
"componentType" : 5126,
|
||||
"count" : 264,
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 2,
|
||||
"componentType" : 5126,
|
||||
"count" : 264,
|
||||
"type" : "VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView" : 3,
|
||||
"componentType" : 5123,
|
||||
"count" : 276,
|
||||
"type" : "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView" : 4,
|
||||
"componentType" : 5126,
|
||||
"count" : 264,
|
||||
"max" : [
|
||||
0.14205700159072876,
|
||||
0.36317670345306396,
|
||||
2.1620800495147705
|
||||
],
|
||||
"min" : [
|
||||
-2.325753688812256,
|
||||
-0.12704896926879883,
|
||||
-0.1493830382823944
|
||||
],
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 5,
|
||||
"componentType" : 5126,
|
||||
"count" : 264,
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 6,
|
||||
"componentType" : 5126,
|
||||
"count" : 264,
|
||||
"max" : [
|
||||
0.14205700159072876,
|
||||
0.6298741102218628,
|
||||
0.14937379956245422
|
||||
],
|
||||
"min" : [
|
||||
-0.5040037631988525,
|
||||
-0.12704896926879883,
|
||||
-0.7224071025848389
|
||||
],
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 7,
|
||||
"componentType" : 5126,
|
||||
"count" : 264,
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 8,
|
||||
"componentType" : 5126,
|
||||
"count" : 264,
|
||||
"max" : [
|
||||
1.1911587715148926,
|
||||
0.7055504322052002,
|
||||
0.14937379956245422
|
||||
],
|
||||
"min" : [
|
||||
-0.1420515477657318,
|
||||
-0.12704896926879883,
|
||||
-0.9040634632110596
|
||||
],
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 9,
|
||||
"componentType" : 5126,
|
||||
"count" : 264,
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 10,
|
||||
"componentType" : 5126,
|
||||
"count" : 264,
|
||||
"max" : [
|
||||
1.0930120944976807,
|
||||
0.12705659866333008,
|
||||
0.14937379956245422
|
||||
],
|
||||
"min" : [
|
||||
-0.1420515477657318,
|
||||
-1.7105424404144287,
|
||||
-0.3054158687591553
|
||||
],
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 11,
|
||||
"componentType" : 5126,
|
||||
"count" : 264,
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 12,
|
||||
"componentType" : 5126,
|
||||
"count" : 264,
|
||||
"max" : [
|
||||
1.0931031703948975,
|
||||
7.137829303741455,
|
||||
1.149359941482544
|
||||
],
|
||||
"min" : [
|
||||
-1.0931031703948975,
|
||||
-0.284426212310791,
|
||||
-1.149359941482544
|
||||
],
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 13,
|
||||
"componentType" : 5126,
|
||||
"count" : 264,
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 14,
|
||||
"componentType" : 5126,
|
||||
"count" : 264,
|
||||
"max" : [
|
||||
0.7663934230804443,
|
||||
0.12705659866333008,
|
||||
0.14937379956245422
|
||||
],
|
||||
"min" : [
|
||||
-0.1420515477657318,
|
||||
-1.3416147232055664,
|
||||
-1.2583342790603638
|
||||
],
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 15,
|
||||
"componentType" : 5126,
|
||||
"count" : 264,
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 16,
|
||||
"componentType" : 5126,
|
||||
"count" : 264,
|
||||
"max" : [
|
||||
1.0939359664916992,
|
||||
0.12705659866333008,
|
||||
0.14937379956245422
|
||||
],
|
||||
"min" : [
|
||||
-0.1420515477657318,
|
||||
-1.257744312286377,
|
||||
-1.9783165454864502
|
||||
],
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 17,
|
||||
"componentType" : 5126,
|
||||
"count" : 264,
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 18,
|
||||
"componentType" : 5126,
|
||||
"count" : 264,
|
||||
"max" : [
|
||||
0.9689376950263977,
|
||||
0.12705659866333008,
|
||||
0.14937379956245422
|
||||
],
|
||||
"min" : [
|
||||
-0.1420515477657318,
|
||||
-1.4092154502868652,
|
||||
-1.7750303745269775
|
||||
],
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 19,
|
||||
"componentType" : 5126,
|
||||
"count" : 264,
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 20,
|
||||
"componentType" : 5126,
|
||||
"count" : 128,
|
||||
"max" : [
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"min" : [
|
||||
-1,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 21,
|
||||
"componentType" : 5126,
|
||||
"count" : 128,
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 22,
|
||||
"componentType" : 5126,
|
||||
"count" : 128,
|
||||
"type" : "VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView" : 23,
|
||||
"componentType" : 5123,
|
||||
"count" : 186,
|
||||
"type" : "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView" : 24,
|
||||
"componentType" : 5126,
|
||||
"count" : 128,
|
||||
"max" : [
|
||||
0,
|
||||
0.5201449394226074,
|
||||
0
|
||||
],
|
||||
"min" : [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 25,
|
||||
"componentType" : 5126,
|
||||
"count" : 128,
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 26,
|
||||
"componentType" : 5126,
|
||||
"count" : 128,
|
||||
"max" : [
|
||||
5.200448989868164,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"min" : [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 27,
|
||||
"componentType" : 5126,
|
||||
"count" : 128,
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 28,
|
||||
"componentType" : 5126,
|
||||
"count" : 128,
|
||||
"max" : [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"min" : [
|
||||
0,
|
||||
0,
|
||||
-1.9945937395095825
|
||||
],
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 29,
|
||||
"componentType" : 5126,
|
||||
"count" : 128,
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 30,
|
||||
"componentType" : 5126,
|
||||
"count" : 128,
|
||||
"max" : [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"min" : [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 31,
|
||||
"componentType" : 5126,
|
||||
"count" : 128,
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 32,
|
||||
"componentType" : 5126,
|
||||
"count" : 128,
|
||||
"max" : [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"min" : [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 33,
|
||||
"componentType" : 5126,
|
||||
"count" : 128,
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 34,
|
||||
"componentType" : 5126,
|
||||
"count" : 128,
|
||||
"max" : [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"min" : [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 35,
|
||||
"componentType" : 5126,
|
||||
"count" : 128,
|
||||
"type" : "VEC3"
|
||||
}
|
||||
],
|
||||
"bufferViews" : [
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 3168,
|
||||
"byteOffset" : 0
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 3168,
|
||||
"byteOffset" : 3168
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 2112,
|
||||
"byteOffset" : 6336
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 552,
|
||||
"byteOffset" : 8448
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 3168,
|
||||
"byteOffset" : 9000
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 3168,
|
||||
"byteOffset" : 12168
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 3168,
|
||||
"byteOffset" : 15336
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 3168,
|
||||
"byteOffset" : 18504
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 3168,
|
||||
"byteOffset" : 21672
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 3168,
|
||||
"byteOffset" : 24840
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 3168,
|
||||
"byteOffset" : 28008
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 3168,
|
||||
"byteOffset" : 31176
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 3168,
|
||||
"byteOffset" : 34344
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 3168,
|
||||
"byteOffset" : 37512
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 3168,
|
||||
"byteOffset" : 40680
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 3168,
|
||||
"byteOffset" : 43848
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 3168,
|
||||
"byteOffset" : 47016
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 3168,
|
||||
"byteOffset" : 50184
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 3168,
|
||||
"byteOffset" : 53352
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 3168,
|
||||
"byteOffset" : 56520
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 1536,
|
||||
"byteOffset" : 59688
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 1536,
|
||||
"byteOffset" : 61224
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 1024,
|
||||
"byteOffset" : 62760
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 372,
|
||||
"byteOffset" : 63784
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 1536,
|
||||
"byteOffset" : 64156
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 1536,
|
||||
"byteOffset" : 65692
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 1536,
|
||||
"byteOffset" : 67228
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 1536,
|
||||
"byteOffset" : 68764
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 1536,
|
||||
"byteOffset" : 70300
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 1536,
|
||||
"byteOffset" : 71836
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 1536,
|
||||
"byteOffset" : 73372
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 1536,
|
||||
"byteOffset" : 74908
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 1536,
|
||||
"byteOffset" : 76444
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 1536,
|
||||
"byteOffset" : 77980
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 1536,
|
||||
"byteOffset" : 79516
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 1536,
|
||||
"byteOffset" : 81052
|
||||
}
|
||||
],
|
||||
"buffers" : [
|
||||
{
|
||||
"byteLength" : 82588,
|
||||
"uri" : "cube.bin"
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -3,6 +3,7 @@ import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:polyvox_filament/filament_controller.dart';
|
||||
import 'package:polyvox_filament/gesture_detecting_filament_view.dart';
|
||||
import 'package:polyvox_filament/view/filament_view.dart';
|
||||
import 'package:polyvox_filament/view/filament_widget.dart';
|
||||
|
||||
void main() {
|
||||
@@ -36,9 +37,14 @@ class _MyAppState extends State<MyApp> {
|
||||
title: const Text('Plugin example app'),
|
||||
),
|
||||
body: Stack(children: [
|
||||
GestureDetectingFilamentView(
|
||||
controller: _filamentController,
|
||||
),
|
||||
Center(
|
||||
child: Container(
|
||||
width: 400,
|
||||
height: 400,
|
||||
child: FilamentWidget(
|
||||
controller: _filamentController,
|
||||
),
|
||||
)),
|
||||
Positioned.fill(
|
||||
child: Wrap(
|
||||
alignment: WrapAlignment.end,
|
||||
@@ -56,35 +62,72 @@ class _MyAppState extends State<MyApp> {
|
||||
onPressed: () async {
|
||||
await _filamentController.loadGltf(
|
||||
'assets/cube.gltf', 'assets');
|
||||
await _filamentController.createMorpher('Cube', [0]);
|
||||
}),
|
||||
ElevatedButton(
|
||||
child: const Text('stretch'),
|
||||
child: const Text('set all weights to 1'),
|
||||
onPressed: () async {
|
||||
await _filamentController
|
||||
.applyWeights(List.filled(8, 1.0));
|
||||
}),
|
||||
ElevatedButton(
|
||||
child: const Text('squeeze'),
|
||||
child: const Text('set all weights to 0'),
|
||||
onPressed: () async {
|
||||
await _filamentController
|
||||
.applyWeights(List.filled(8, 0));
|
||||
}),
|
||||
ElevatedButton(
|
||||
child: const Text('load caleb'),
|
||||
onPressed: () async {
|
||||
await _filamentController.loadGltf(
|
||||
'assets/caleb_mouth_morph_target.gltf', 'assets');
|
||||
_targets = await _filamentController
|
||||
.getTargetNames('CC_Base_Body');
|
||||
setState(() {});
|
||||
|
||||
_filamentController
|
||||
.createMorpher('CC_Base_Body', [1, 7, 8]);
|
||||
}),
|
||||
ElevatedButton(
|
||||
onPressed: () => _filamentController.playAnimation(0),
|
||||
child: const Text('Play'))
|
||||
child: const Text('play animation')),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
_filamentController.zoom(-1.0);
|
||||
},
|
||||
child: const Text('zoom in')),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
_filamentController.zoom(1.0);
|
||||
},
|
||||
child: const Text('zoom out')),
|
||||
Builder(builder:(innerCtx) => ElevatedButton(
|
||||
onPressed: () async {
|
||||
final names = await _filamentController
|
||||
.getTargetNames("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: innerCtx);
|
||||
},
|
||||
child: const Text('get target names'))),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
await _filamentController.panStart(1, 1);
|
||||
await _filamentController.panUpdate(1, 2);
|
||||
await _filamentController.panEnd();
|
||||
},
|
||||
child: Text("Pan left")),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
await _filamentController.panStart(1, 1);
|
||||
await _filamentController.panUpdate(0, 0);
|
||||
await _filamentController.panEnd();
|
||||
},
|
||||
child: Text("Pan right"))
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -137,3 +180,8 @@ class _MyAppState extends State<MyApp> {
|
||||
// .values
|
||||
// .toList(),
|
||||
// )
|
||||
// ElevatedButton(
|
||||
// child: const Text('init'),
|
||||
// onPressed: () async {
|
||||
// await _filamentController.initialize();
|
||||
// }),
|
||||
@@ -41,7 +41,7 @@ flutter:
|
||||
- assets/BusterDrone/
|
||||
- assets/BusterDrone/textures/
|
||||
- assets/FlightHelmet/
|
||||
- assets/FlightHelmet/textures/
|
||||
- assets/textures/
|
||||
#- assets/FlightHelmet/textures/
|
||||
#- assets/textures/
|
||||
|
||||
|
||||
Reference in New Issue
Block a user