diff --git a/README.md b/README.md
index 22db321d..4a12ba61 100644
--- a/README.md
+++ b/README.md
@@ -20,3 +20,23 @@ iOS:
Extract and move both lib/ and include/ to ./ios
+# Running
+
+## Android
+
+- MainActivity.kt must have the following:
+```
+class MainActivity: FlutterActivity() {
+ override fun getTransparencyMode(): TransparencyMode {
+ return TransparencyMode.transparent
+ }
+}
+```
+and theme must have the following in `styles.xml`
+```
+
+```
+
+
diff --git a/android/src/main/cpp/filament_android.cpp b/android/src/main/cpp/filament_android.cpp
index 35f19094..823c2608 100644
--- a/android/src/main/cpp/filament_android.cpp
+++ b/android/src/main/cpp/filament_android.cpp
@@ -169,5 +169,9 @@ extern "C" {
void release_source_assets(void* viewer) {
((FilamentViewer*)viewer)->releaseSourceAssets();
}
+
+ void remove_asset(void* viewer) {
+ ((FilamentViewer*)viewer)->removeAsset();
+ }
}
diff --git a/android/src/main/kotlin/app/polyvox/filament/FilamentInterop.kt b/android/src/main/kotlin/app/polyvox/filament/FilamentInterop.kt
index 149b9045..8c2b9509 100644
--- a/android/src/main/kotlin/app/polyvox/filament/FilamentInterop.kt
+++ b/android/src/main/kotlin/app/polyvox/filament/FilamentInterop.kt
@@ -62,4 +62,7 @@ interface FilamentInterop : Library {
fun release_source_assets(viewer:Pointer);
+ fun remove_asset(viewer:Pointer);
+
+
}
diff --git a/android/src/main/kotlin/app/polyvox/filament/FilamentView.kt b/android/src/main/kotlin/app/polyvox/filament/FilamentView.kt
index c2e8d4a5..8789ba53 100644
--- a/android/src/main/kotlin/app/polyvox/filament/FilamentView.kt
+++ b/android/src/main/kotlin/app/polyvox/filament/FilamentView.kt
@@ -104,8 +104,8 @@ PlatformView {
_methodChannel.invokeMethod("ready", null)
choreographer = Choreographer.getInstance()
-
- _view.setZOrderOnTop(true)
+
+ _view.setZOrderOnTop(false)
_view.holder.setFormat(PixelFormat.OPAQUE)
_view.holder.addCallback (object : SurfaceHolder.Callback {
@@ -301,6 +301,10 @@ PlatformView {
_lib.release_source_assets(_viewer!!)
result.success("OK");
}
+ "removeAsset" -> {
+ _lib.remove_asset(_viewer!!)
+ result.success("OK");
+ }
"playAnimation" -> {
val args = call.arguments as ArrayList
_lib.play_animation(_viewer!!, args[0] as Int, args[1] as Boolean)
diff --git a/example/android/app/src/main/kotlin/app/polyvox/polyvox_filament_example/MainActivity.kt b/example/android/app/src/main/kotlin/app/polyvox/polyvox_filament_example/MainActivity.kt
index 59d17d7f..be77f789 100644
--- a/example/android/app/src/main/kotlin/app/polyvox/polyvox_filament_example/MainActivity.kt
+++ b/example/android/app/src/main/kotlin/app/polyvox/polyvox_filament_example/MainActivity.kt
@@ -1,429 +1,10 @@
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
+import io.flutter.embedding.android.TransparencyMode
class MainActivity: FlutterActivity() {
-
+ override fun getTransparencyMode(): TransparencyMode {
+ return TransparencyMode.transparent
+ }
}
-
-
-//
-
-
-// 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()
-// // 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)
-// // }
-// // }
-// }
diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml
index d74aa35c..7f9b8406 100644
--- a/example/android/app/src/main/res/values/styles.xml
+++ b/example/android/app/src/main/res/values/styles.xml
@@ -13,6 +13,6 @@
This Theme is only used starting with V2 of Flutter's Android embedding. -->
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 4db478a6..7e58e686 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -28,16 +28,16 @@ class _MyAppState extends State {
@override
Widget build(BuildContext context) {
return MaterialApp(
+ color: Colors.transparent,
home: Scaffold(
- appBar: AppBar(
- title: const Text('Plugin example app'),
- ),
+ backgroundColor: Colors.transparent,
body: Column(children: [
- SizedBox(
+ Expanded(child:SizedBox(
height:200, width:200,
child:FilamentWidget(
controller: _filamentController,
- )),
+ ))),
+
Expanded(
child: Wrap(
alignment: WrapAlignment.end,
@@ -63,6 +63,12 @@ class _MyAppState extends State {
await _filamentController.loadGltf(
'assets/FlightHelmet/FlightHelmet.gltf', 'assets/FlightHelmet');
}),
+ ElevatedButton(
+ child: const Text('remove asset'),
+ onPressed: () async {
+ await _filamentController
+ .removeAsset();
+ }),
ElevatedButton(
child: const Text('set all weights to 1'),
onPressed: () async {
diff --git a/ios/src/FilamentViewer.cpp b/ios/src/FilamentViewer.cpp
index a6ef10cb..38dea53f 100644
--- a/ios/src/FilamentViewer.cpp
+++ b/ios/src/FilamentViewer.cpp
@@ -116,8 +116,7 @@ namespace polyvox
LoadResource loadResource,
FreeResource freeResource) : _layer(layer),
_loadResource(loadResource),
- _freeResource(freeResource),
- _assetBuffer(nullptr, 0, 0)
+ _freeResource(freeResource)
{
_engine = Engine::create(Engine::Backend::OPENGL);
@@ -166,10 +165,23 @@ namespace polyvox
manipulator =
Manipulator::Builder().orbitHomePosition(0.0f, 0.0f, 0.05f).targetPosition(0.0f, 0.0f, 0.0f).build(Mode::ORBIT);
_asset = nullptr;
+
+ // Always add a direct light source since it is required for shadowing.
+ _sun = EntityManager::get().create();
+ LightManager::Builder(LightManager::Type::DIRECTIONAL)
+ .color(Color::cct(6500.0f))
+ .intensity(100000.0f)
+ .direction(math::float3(0.0f, 1.0f, 0.0f))
+ .castShadows(false)
+ // .castShadows(true)
+ .build(*_engine, _sun);
+ _scene->addEntity(_sun);
+
}
FilamentViewer::~FilamentViewer()
{
+ cleanup();
}
Renderer *FilamentViewer::getRenderer()
@@ -258,15 +270,15 @@ namespace polyvox
Log("Loading GLB at URI %s", uri);
- if (_asset)
- {
- _asset->releaseSourceData();
- _resourceLoader->evictResourceData();
- _scene->removeEntities(_asset->getEntities(), _asset->getEntityCount());
- _assetLoader->destroyAsset(_asset);
- }
- _asset = nullptr;
- _animator = nullptr;
+ // if (_asset)
+ // {
+ // _asset->releaseSourceData();
+ // _resourceLoader->evictResourceData();
+ // _scene->removeEntities(_asset->getEntities(), _asset->getEntityCount());
+ // _assetLoader->destroyAsset(_asset);
+ // }
+ // _asset = nullptr;
+ // _animator = nullptr;
ResourceBuffer rbuf = _loadResource(uri);
@@ -302,6 +314,8 @@ namespace polyvox
// transformToUnitCube();
+ _asset->releaseSourceData();
+
Log("Successfully loaded GLB.");
}
@@ -310,22 +324,14 @@ namespace polyvox
Log("Loading GLTF at URI %s", uri);
- if (_asset)
- {
- Log("Asset already exists");
- _resourceLoader->evictResourceData();
- _scene->removeEntities(_asset->getEntities(), _asset->getEntityCount());
- _assetLoader->destroyAsset(_asset);
- _freeResource(_assetBuffer);
- }
_asset = nullptr;
_animator = nullptr;
- _assetBuffer = _loadResource(uri);
+ ResourceBuffer rbuf = _loadResource(uri);
// Parse the glTF file and create Filament entities.
Log("Creating asset from JSON");
- _asset = _assetLoader->createAssetFromJson((uint8_t *)_assetBuffer.data, _assetBuffer.size);
+ _asset = _assetLoader->createAssetFromJson((uint8_t *)rbuf.data, rbuf.size);
Log("Created asset from JSON");
if (!_asset)
@@ -336,13 +342,29 @@ namespace polyvox
Log("Loading relative resources");
loadResources(string(relativeResourcePath) + string("/"));
Log("Loaded relative resources");
- // _asset->releaseSourceData();
+ _asset->releaseSourceData();
Log("Load complete for GLTF at URI %s", uri);
// transformToUnitCube();
}
+ void FilamentViewer::removeAsset() {
+ if (!_asset) {
+ Log("No asset loaded, ignoring call.");
+ return;
+ }
+
+ _resourceLoader->evictResourceData();
+ _scene->removeEntities(_asset->getEntities(), _asset->getEntityCount());
+ _assetLoader->destroyAsset(_asset);
+ _asset = nullptr;
+ _animator = nullptr;
+ _morphAnimationBuffer = nullptr;
+ _embeddedAnimationBuffer = nullptr;
+ }
+
+
///
/// Sets the active camera to the GLTF camera specified by [name].
/// Blender export arranges cameras as follows
@@ -354,12 +376,15 @@ namespace polyvox
///
bool FilamentViewer::setCamera(const char *cameraName)
{
+ Log("Attempting to set camera to %s.", cameraName);
size_t count = _asset->getCameraEntityCount();
- if(count == 0)
+ if(count == 0) {
+ Log("Failed, no cameras found in current asset.");
return false;
+ }
const utils::Entity* cameras = _asset->getCameraEntities();
- Log("Found %d cameras in asset", count);
+ Log("%d cameras found in current asset", cameraName, count);
for(int i=0; i < count; i++) {
auto inst = _ncm->getInstance(cameras[i]);
@@ -376,8 +401,9 @@ namespace polyvox
camera->setLensProjection(_cameraFocalLength, aspect, kNearPlane, kFarPlane);
_view->setCamera(camera);
+ Log("Successfully set camera.");
+ return true;
}
- return true;
}
Log("Unable to locate camera under name %s ", cameraName);
return false;
@@ -456,16 +482,6 @@ namespace polyvox
_freeResource(iblBuffer);
- // Always add a direct light source since it is required for shadowing.
- _sun = EntityManager::get().create();
- LightManager::Builder(LightManager::Type::DIRECTIONAL)
- .color(Color::cct(6500.0f))
- .intensity(100000.0f)
- .direction(math::float3(0.0f, 1.0f, 0.0f))
- .castShadows(true)
- .build(*_engine, _sun);
- _scene->addEntity(_sun);
-
Log("Skybox/IBL load complete.");
}
@@ -492,7 +508,6 @@ namespace polyvox
_assetLoader->destroyAsset(_asset);
_materialProvider->destroyMaterials();
AssetLoader::destroy(&_assetLoader);
- _freeResource(_assetBuffer);
};
void FilamentViewer::render()
@@ -503,12 +518,12 @@ namespace polyvox
return;
}
- if (morphAnimationBuffer)
+ if (_morphAnimationBuffer)
{
updateMorphAnimation();
}
- if(embeddedAnimationBuffer) {
+ if(_embeddedAnimationBuffer) {
updateEmbeddedAnimation();
}
@@ -545,35 +560,35 @@ namespace polyvox
void FilamentViewer::animateWeights(float *data, int numWeights, int numFrames, float frameLengthInMs)
{
Log("Making morph animation buffer with %d weights across %d frames and frame length %f ms ", numWeights, numFrames, frameLengthInMs);
- morphAnimationBuffer = std::make_unique(data, numWeights, numFrames, frameLengthInMs);
+ _morphAnimationBuffer = std::make_unique(data, numWeights, numFrames, frameLengthInMs);
}
void FilamentViewer::updateMorphAnimation()
{
- if(!morphAnimationBuffer) {
+ if(!_morphAnimationBuffer) {
return;
}
- if (morphAnimationBuffer->frameIndex == -1) {
- morphAnimationBuffer->frameIndex++;
- morphAnimationBuffer->startTime = high_resolution_clock::now();
- applyWeights(morphAnimationBuffer->frameData, morphAnimationBuffer->numWeights);
+ if (_morphAnimationBuffer->frameIndex == -1) {
+ _morphAnimationBuffer->frameIndex++;
+ _morphAnimationBuffer->startTime = high_resolution_clock::now();
+ applyWeights(_morphAnimationBuffer->frameData, _morphAnimationBuffer->numWeights);
}
else
{
- duration dur = high_resolution_clock::now() - morphAnimationBuffer->startTime;
- int frameIndex = static_cast(dur.count() / morphAnimationBuffer->frameLengthInMs);
+ duration dur = high_resolution_clock::now() - _morphAnimationBuffer->startTime;
+ int frameIndex = static_cast(dur.count() / _morphAnimationBuffer->frameLengthInMs);
- if (frameIndex > morphAnimationBuffer->numFrames - 1)
+ if (frameIndex > _morphAnimationBuffer->numFrames - 1)
{
- duration dur = high_resolution_clock::now() - morphAnimationBuffer->startTime;
- Log("Morph animation completed in %f ms (%d frames at framerate %f), final frame was %d", dur.count(), morphAnimationBuffer->numFrames, 1000 / morphAnimationBuffer->frameLengthInMs, morphAnimationBuffer->frameIndex);
- morphAnimationBuffer = nullptr;
- } else if (frameIndex != morphAnimationBuffer->frameIndex) {
- Log("Rendering frame %d (of a total %d)", frameIndex, morphAnimationBuffer->numFrames);
- morphAnimationBuffer->frameIndex = frameIndex;
- auto framePtrOffset = frameIndex * morphAnimationBuffer->numWeights;
- applyWeights(morphAnimationBuffer->frameData + framePtrOffset, morphAnimationBuffer->numWeights);
+ duration dur = high_resolution_clock::now() - _morphAnimationBuffer->startTime;
+ Log("Morph animation completed in %f ms (%d frames at framerate %f), final frame was %d", dur.count(), _morphAnimationBuffer->numFrames, 1000 / _morphAnimationBuffer->frameLengthInMs, _morphAnimationBuffer->frameIndex);
+ _morphAnimationBuffer = nullptr;
+ } else if (frameIndex != _morphAnimationBuffer->frameIndex) {
+ Log("Rendering frame %d (of a total %d)", frameIndex, _morphAnimationBuffer->numFrames);
+ _morphAnimationBuffer->frameIndex = frameIndex;
+ auto framePtrOffset = frameIndex * _morphAnimationBuffer->numWeights;
+ applyWeights(_morphAnimationBuffer->frameData + framePtrOffset, _morphAnimationBuffer->numWeights);
}
}
}
@@ -582,33 +597,33 @@ namespace polyvox
if(index > _animator->getAnimationCount() - 1) {
Log("Asset does not contain an animation at index %d", index);
} else {
- embeddedAnimationBuffer = make_unique(index, _animator->getAnimationDuration(index), loop);
+ _embeddedAnimationBuffer = make_unique(index, _animator->getAnimationDuration(index), loop);
}
}
void FilamentViewer::stopAnimation() {
// TODO - does this need to be threadsafe?
- embeddedAnimationBuffer = nullptr;
+ _embeddedAnimationBuffer = nullptr;
}
void FilamentViewer::updateEmbeddedAnimation() {
- duration dur = duration_cast>(high_resolution_clock::now() - embeddedAnimationBuffer->lastTime);
+ duration dur = duration_cast>(high_resolution_clock::now() - _embeddedAnimationBuffer->lastTime);
float startTime = 0;
- if(!embeddedAnimationBuffer->hasStarted) {
- embeddedAnimationBuffer->hasStarted = true;
- embeddedAnimationBuffer->lastTime = high_resolution_clock::now();
- } else if(dur.count() >= embeddedAnimationBuffer->duration) {
- if(embeddedAnimationBuffer->loop) {
- embeddedAnimationBuffer->lastTime = high_resolution_clock::now();
+ if(!_embeddedAnimationBuffer->hasStarted) {
+ _embeddedAnimationBuffer->hasStarted = true;
+ _embeddedAnimationBuffer->lastTime = high_resolution_clock::now();
+ } else if(dur.count() >= _embeddedAnimationBuffer->duration) {
+ if(_embeddedAnimationBuffer->loop) {
+ _embeddedAnimationBuffer->lastTime = high_resolution_clock::now();
} else {
- embeddedAnimationBuffer = nullptr;
+ _embeddedAnimationBuffer = nullptr;
return;
}
} else {
startTime = dur.count();
}
- _animator->applyAnimation(embeddedAnimationBuffer->animationIndex, startTime);
+ _animator->applyAnimation(_embeddedAnimationBuffer->animationIndex, startTime);
_animator->updateBoneMatrices();
}
diff --git a/ios/src/FilamentViewer.hpp b/ios/src/FilamentViewer.hpp
index 2cf9a2e3..7181cc46 100644
--- a/ios/src/FilamentViewer.hpp
+++ b/ios/src/FilamentViewer.hpp
@@ -95,6 +95,7 @@ namespace polyvox {
void loadGlb(const char* const uri);
void loadGltf(const char* const uri, const char* relativeResourcePath);
void loadSkybox(const char* const skyboxUri, const char* const iblUri);
+ void removeAsset();
void updateViewportAndCameraProjection(int height, int width, float scaleFactor);
void render();
@@ -157,7 +158,7 @@ namespace polyvox {
AssetLoader* _assetLoader;
FilamentAsset* _asset = nullptr;
- ResourceBuffer _assetBuffer;
+ // ResourceBuffer _assetBuffer;
NameComponentManager* _ncm;
Entity _sun;
@@ -181,8 +182,8 @@ namespace polyvox {
// animation flags;
bool isAnimating;
- unique_ptr morphAnimationBuffer;
- unique_ptr embeddedAnimationBuffer;
+ unique_ptr _morphAnimationBuffer;
+ unique_ptr _embeddedAnimationBuffer;
};
diff --git a/lib/filament_controller.dart b/lib/filament_controller.dart
index 2da99dd8..ea592306 100644
--- a/lib/filament_controller.dart
+++ b/lib/filament_controller.dart
@@ -17,6 +17,7 @@ abstract class FilamentController {
Future> getTargetNames(String meshName);
Future> getAnimationNames();
Future releaseSourceAssets();
+ Future removeAsset();
Future playAnimation(int index, {bool loop = false});
Future stopAnimation();
Future setCamera(String name);
@@ -122,6 +123,10 @@ class PolyvoxFilamentController extends FilamentController {
await _channel.invokeMethod("releaseSourceAssets");
}
+ Future removeAsset() async {
+ await _channel.invokeMethod("removeAsset");
+ }
+
Future zoom(double z) async {
await _channel.invokeMethod("zoom", z);
}