From e51577cf6b4501d1c4e5af9030cf29bedd1e72cc Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Sat, 13 Aug 2022 00:25:56 +1000 Subject: [PATCH] decouple assets from viewer to allow independent addition/removal/animation/etc --- android/CMakeLists.txt | 6 +- android/src/main/cpp/filament_android.cpp | 39 +- .../app/polyvox/filament/FilamentInterop.kt | 16 +- .../app/polyvox/filament/FilamentView.kt | 53 +- example/assets/background.png | 3 + example/assets/cube.blend | 3 + example/assets/cube.glb | 4 +- .../assets/default_env/default_env_ibl.ktx | 2 +- .../assets/default_env/default_env_skybox.ktx | 2 +- .../default_env/default_env_skybox_tiny.ktx | 3 - example/lib/main.dart | 133 +- ios/src/FilamentViewer.cpp | 1108 ++++++----------- ios/src/FilamentViewer.hpp | 104 +- ios/src/{Log.h => Log.hpp} | 9 +- ios/src/SceneAsset.cpp | 193 +++ ios/src/SceneAsset.hpp | 83 ++ ios/src/SceneAssetLoader.cpp | 123 ++ ios/src/SceneAssetLoader.hpp | 42 + ios/src/SceneResources.hpp | 84 ++ ...{streambuf.cpp => StreamBufferAdapter.cpp} | 22 +- ...{streambuf.hpp => StreamBufferAdapter.hpp} | 10 +- ios/src/{ => image}/imagematerial.S | 0 ios/src/{ => image}/imagematerial.apple.S | 0 ios/src/{ => image}/imagematerial.bin | Bin ios/src/{ => image}/imagematerial.c | 0 ios/src/{ => image}/imagematerial.h | 0 ios/src/{ => image}/materials/image.filamat | Bin ios/src/{ => image}/materials/image.mat | 0 lib/filament_controller.dart | 73 +- pubspec.yaml | 2 +- 30 files changed, 1124 insertions(+), 993 deletions(-) create mode 100644 example/assets/background.png create mode 100644 example/assets/cube.blend delete mode 100644 example/assets/default_env/default_env_skybox_tiny.ktx rename ios/src/{Log.h => Log.hpp} (79%) create mode 100644 ios/src/SceneAsset.cpp create mode 100644 ios/src/SceneAsset.hpp create mode 100644 ios/src/SceneAssetLoader.cpp create mode 100644 ios/src/SceneAssetLoader.hpp create mode 100644 ios/src/SceneResources.hpp rename ios/src/{streambuf.cpp => StreamBufferAdapter.cpp} (65%) rename ios/src/{streambuf.hpp => StreamBufferAdapter.hpp} (62%) rename ios/src/{ => image}/imagematerial.S (100%) rename ios/src/{ => image}/imagematerial.apple.S (100%) rename ios/src/{ => image}/imagematerial.bin (100%) rename ios/src/{ => image}/imagematerial.c (100%) rename ios/src/{ => image}/imagematerial.h (100%) rename ios/src/{ => image}/materials/image.filamat (100%) rename ios/src/{ => image}/materials/image.mat (100%) diff --git a/android/CMakeLists.txt b/android/CMakeLists.txt index 6b0cf8c4..6569591a 100644 --- a/android/CMakeLists.txt +++ b/android/CMakeLists.txt @@ -13,9 +13,11 @@ add_library( src/main/cpp/KtxReader1.cpp src/main/cpp/StbProvider.cpp src/main/cpp/JobSystem.cpp + ../ios/src/SceneAssetLoader.cpp ../ios/src/FilamentViewer.cpp - ../ios/src/streambuf.cpp - ../ios/src/imagematerial.c + ../ios/src/StreamBufferAdapter.cpp + ../ios/src/image/imagematerial.c + ../ios/src/SceneAsset.cpp ) target_link_libraries( diff --git a/android/src/main/cpp/filament_android.cpp b/android/src/main/cpp/filament_android.cpp index a1d60377..5e718545 100644 --- a/android/src/main/cpp/filament_android.cpp +++ b/android/src/main/cpp/filament_android.cpp @@ -1,4 +1,5 @@ #include "FilamentViewer.hpp" +#include "SceneAsset.hpp" #include #include #include @@ -41,6 +42,8 @@ static void freeResource(ResourceBuffer rb) { AAsset* asset = _assets[rb.id]; if(asset) { AAsset_close(asset); + } else { + __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Attempting to free resource at index [ %d ] that has already been released.", rb.id); } _assets[rb.id] = nullptr; } @@ -68,16 +71,16 @@ extern "C" { ((FilamentViewer*)viewer)->removeIbl(); } - void load_glb(void* viewer, const char* assetPath) { - ((FilamentViewer*)viewer)->loadGlb(assetPath); + void* load_glb(void* viewer, const char* assetPath) { + return ((FilamentViewer*)viewer)->loadGlb(assetPath); } - void load_gltf(void* viewer, const char* assetPath, const char* relativePath) { - ((FilamentViewer*)viewer)->loadGltf(assetPath, relativePath); + void* load_gltf(void* viewer, const char* assetPath, const char* relativePath) { + return ((FilamentViewer*)viewer)->loadGltf(assetPath, relativePath); } - bool set_camera(void* viewer, const char* nodeName) { - return ((FilamentViewer*)viewer)->setCamera(nodeName); + bool set_camera(void* viewer, void* asset, const char* nodeName) { + return ((FilamentViewer*)viewer)->setCamera((SceneAsset*)asset, nodeName); } void* filament_viewer_new( @@ -141,22 +144,22 @@ extern "C" { ((FilamentViewer*)viewer)->manipulator->grabEnd(); } - void apply_weights(void* viewer, float* weights, int count) { - ((FilamentViewer*)viewer)->applyWeights(weights, count); + void apply_weights(void* asset, float* weights, int count) { + ((SceneAsset*)asset)->applyWeights(weights, count); } - void animate_weights(void* viewer, float* data, int numWeights, int numFrames, float frameRate) { + void animate_weights(void* asset, float* data, int numWeights, int numFrames, float frameRate) { __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Animating %d frames, each with %d weights", numFrames, numWeights); - ((FilamentViewer*)viewer)->animateWeights((float*)data, numWeights, numFrames, frameRate); + ((SceneAsset*)asset)->animateWeights((float*)data, numWeights, numFrames, frameRate); } - void play_animation(void* viewer, int index, bool loop) { + void play_animation(void* asset, int index, bool loop) { __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Playing embedded animation %d", index); - ((FilamentViewer*)viewer)->playAnimation(index, loop); + ((SceneAsset*)asset)->playAnimation(index, loop); } - char** get_animation_names(void* viewer, int* countPtr) { - auto names = ((FilamentViewer*)viewer)->getAnimationNames(); + char** get_animation_names(void* asset, int* countPtr) { + auto names = ((SceneAsset*)asset)->getAnimationNames(); __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Got %d animation names", names->size()); char** names_c; names_c = new char*[names->size()]; @@ -168,8 +171,8 @@ extern "C" { return names_c; } - char** get_target_names(void* viewer, char* meshName, int* countPtr ) { - unique_ptr> names = ((FilamentViewer*)viewer)->getTargetNames(meshName); + char** get_target_names(void* asset, char* meshName, int* countPtr ) { + unique_ptr> names = ((SceneAsset*)asset)->getTargetNames(meshName); __android_log_print(ANDROID_LOG_VERBOSE, "filament_api", "Got %d names", names->size()); @@ -188,8 +191,8 @@ extern "C" { free(ptr); } - void remove_asset(void* viewer) { - ((FilamentViewer*)viewer)->removeAsset(); + void remove_asset(void* viewer, void* asset) { + ((FilamentViewer*)viewer)->removeAsset((SceneAsset*)asset); } } diff --git a/android/src/main/kotlin/app/polyvox/filament/FilamentInterop.kt b/android/src/main/kotlin/app/polyvox/filament/FilamentInterop.kt index 05a392f9..5001417e 100644 --- a/android/src/main/kotlin/app/polyvox/filament/FilamentInterop.kt +++ b/android/src/main/kotlin/app/polyvox/filament/FilamentInterop.kt @@ -32,7 +32,7 @@ interface FilamentInterop : Library { fun load_gltf(viewer:Pointer, uri:String, relativeResourcePath:String) : Pointer; - fun set_camera(viewer:Pointer, nodeName:String) : Boolean; + fun set_camera(viewer:Pointer, asset:Pointer, nodeName:String) : Boolean; fun render(viewer:Pointer); @@ -50,21 +50,19 @@ interface FilamentInterop : Library { fun grab_end(viewer:Pointer) - fun apply_weights(viewer:Pointer, weights:FloatArray, size:Int); + fun apply_weights(asset:Pointer, weights:FloatArray, size:Int); - fun animate_weights(viewer:Pointer, frames:FloatArray, numWeights:Int, numFrames:Int, frameRate:Float); + fun animate_weights(asset:Pointer, frames:FloatArray, numWeights:Int, numFrames:Int, frameRate:Float); - fun get_target_names(viewer:Pointer, meshName:String, outLen:IntByReference) : Pointer; + fun get_target_names(asset:Pointer, meshName:String, outLen:IntByReference) : Pointer; - fun get_animation_names(viewer:Pointer, outLen:IntByReference) : Pointer; + fun get_animation_names(asset:Pointer, outLen:IntByReference) : Pointer; - fun play_animation(viewer:Pointer, index:Int, loop:Boolean); + fun play_animation(asset:Pointer, index:Int, loop:Boolean); fun free_pointer(ptr:Pointer, size:Int); - fun release_source_assets(viewer:Pointer); - - fun remove_asset(viewer:Pointer); + fun remove_asset(viewer:Pointer, asset:Pointer); fun remove_skybox(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 903bda5a..3e1f9c3b 100644 --- a/android/src/main/kotlin/app/polyvox/filament/FilamentView.kt +++ b/android/src/main/kotlin/app/polyvox/filament/FilamentView.kt @@ -192,32 +192,31 @@ PlatformView { val key = loader.getLookupKeyForAsset(call.arguments as String) val key2 = loader.getLookupKeyForAsset(call.arguments as String, context.packageName) val path = loader.findAppBundlePath() - Log.v(TAG, "key ${key} key2 ${key2} path : ${path}") - _lib.load_glb( + val assetPtr = _lib.load_glb( _viewer!!, key ) - result.success("OK"); + result.success(Pointer.nativeValue(assetPtr)); } "loadGltf" -> { if (_viewer == null) return; val args = call.arguments as ArrayList val loader = FlutterInjector.instance().flutterLoader() - _lib.load_gltf( + val assetPtr = _lib.load_gltf( _viewer!!, loader.getLookupKeyForAsset(args[0] as String), loader.getLookupKeyForAsset(args[1] as String) ) - result.success("OK"); + result.success(Pointer.nativeValue(assetPtr)); } "setCamera" -> { - if (_viewer == null) - return; + val args = call.arguments as ArrayList<*> val success = _lib.set_camera( _viewer!!, - call.arguments as String + Pointer(args[0] as Long), + args[1] as String, ) if(success) { result.success("OK"); @@ -236,9 +235,10 @@ PlatformView { return; val countPtr = IntByReference(); - val arrPtr = _lib.get_target_names(_viewer!!, call.arguments as String, countPtr) + val args = call.arguments as ArrayList<*> + val namesPtr = _lib.get_target_names(Pointer(args[0] as Long), args[1] as String, countPtr) - val names = arrPtr.getStringArray(0, countPtr.value); + val names = namesPtr.getStringArray(0, countPtr.value); for(i in 0..countPtr.value-1) { Log.v(TAG, "Got target names ${names[i]} ${names[i].length}") @@ -246,16 +246,14 @@ PlatformView { val namesAsList = names.toCollection(ArrayList()) - _lib.free_pointer(arrPtr, countPtr.getValue()) + _lib.free_pointer(namesPtr, countPtr.getValue()) result.success(namesAsList) } "getAnimationNames" -> { - if(_viewer == null) - return; - + val assetPtr = Pointer(call.arguments as Long) val countPtr = IntByReference(); - val arrPtr = _lib.get_animation_names(_viewer!!, countPtr) + val arrPtr = _lib.get_animation_names(assetPtr, countPtr) val names = arrPtr.getStringArray(0, countPtr.value); @@ -269,23 +267,22 @@ PlatformView { result.success(names.toCollection(ArrayList())) } "applyWeights" -> { - if(_viewer == null) - return; - val weights = call.arguments as ArrayList; + val args = call.arguments as ArrayList<*> + val assetPtr = Pointer(args[0] as Long) + val weights = args[1] as ArrayList; - _lib.apply_weights(_viewer!!, weights.toFloatArray(), weights.size) + _lib.apply_weights(assetPtr, weights.toFloatArray(), weights.size) result.success("OK"); } "animateWeights" -> { - if(_viewer == null) - return; val args = call.arguments as ArrayList - val frames = args[0] as ArrayList; - val numWeights = args[1] as Int - val numFrames = args[2] as Int - val frameLenInMs = args[3] as Double + val assetPtr = Pointer(args[0] as Long) + val frames = args[1] as ArrayList; + val numWeights = args[2] as Int + val numFrames = args[3] as Int + val frameLenInMs = args[4] as Double - _lib.animate_weights(_viewer!!, frames.toFloatArray(), numWeights, numFrames, frameLenInMs.toFloat()) + _lib.animate_weights(assetPtr, frames.toFloatArray(), numWeights, numFrames, frameLenInMs.toFloat()) result.success("OK"); } "panStart" -> { @@ -331,12 +328,12 @@ PlatformView { result.success("OK"); } "removeAsset" -> { - _lib.remove_asset(_viewer!!) + _lib.remove_asset(_viewer!!, Pointer(call.arguments as Long)) result.success("OK"); } "playAnimation" -> { val args = call.arguments as ArrayList - _lib.play_animation(_viewer!!, args[0] as Int, args[1] as Boolean) + _lib.play_animation(Pointer(args[0] as Long), args[1] as Int, args[2] as Boolean) result.success("OK") } else -> { diff --git a/example/assets/background.png b/example/assets/background.png new file mode 100644 index 00000000..cb976eb1 --- /dev/null +++ b/example/assets/background.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c38fa36cbbbe969a2ae015fe56752343e8d06007c6eea28c56e22ffd56a8db1a +size 2570831 diff --git a/example/assets/cube.blend b/example/assets/cube.blend new file mode 100644 index 00000000..8b7d6249 --- /dev/null +++ b/example/assets/cube.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9fb09944d65a155fc5b6522f296dd875df02fc2944733a35eb09bec23bbabdcd +size 813192 diff --git a/example/assets/cube.glb b/example/assets/cube.glb index 7e61f4fb..f296467c 100644 --- a/example/assets/cube.glb +++ b/example/assets/cube.glb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:674e74fbef9b017aa071cd985bcc34c34c96fc57051a0724b6c23de78cd22db8 -size 2352 +oid sha256:1f53c13e2ee7cd36156fa3bcfcb9a199b5999c3cb15d7f964a326105ac7d0419 +size 11032 diff --git a/example/assets/default_env/default_env_ibl.ktx b/example/assets/default_env/default_env_ibl.ktx index 28522a4c..4ba3a2cc 100644 --- a/example/assets/default_env/default_env_ibl.ktx +++ b/example/assets/default_env/default_env_ibl.ktx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:defdd358826b3b8f58237c38e36b2a03d768f2307407152c79621d3358001f71 +oid sha256:0245800fa846c2a3c15cb1d2aa6ded86fcf8355e568907dcd50e073c589f85d6 size 2095464 diff --git a/example/assets/default_env/default_env_skybox.ktx b/example/assets/default_env/default_env_skybox.ktx index eb7f5264..3e52c7d2 100644 --- a/example/assets/default_env/default_env_skybox.ktx +++ b/example/assets/default_env/default_env_skybox.ktx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b7e5e33a6bbaee9f0d89f8525fa9c3723f9ace8ebaaf00b3a9dc8cdf7c6c1095 +oid sha256:e4050117d400c027dd47baa7b4a9ed46f7174b0690def9d70643d3c364faf758 size 1572932 diff --git a/example/assets/default_env/default_env_skybox_tiny.ktx b/example/assets/default_env/default_env_skybox_tiny.ktx deleted file mode 100644 index 8b195bcd..00000000 --- a/example/assets/default_env/default_env_skybox_tiny.ktx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ee52c238ccf3188a21533d6e096f74cadab26720d0fa710ad9737484db8ee4cb -size 98372 diff --git a/example/lib/main.dart b/example/lib/main.dart index eb7ac16d..d5f7ebf0 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -16,6 +16,9 @@ class MyApp extends StatefulWidget { class _MyAppState extends State { final FilamentController _filamentController = PolyvoxFilamentController(); + FilamentAsset? _cube; + FilamentAsset? _flightHelmet; + final weights = List.filled(255, 0.0); List _targets = []; bool _loop = false; @@ -32,22 +35,24 @@ class _MyAppState extends State { home: Scaffold( backgroundColor: Colors.transparent, body: Column(children: [ - Expanded(child:SizedBox( - height:200, width:200, - child:FilamentWidget( - controller: _filamentController, - ))), - Expanded( - child: SingleChildScrollView(child:Wrap( + child: SizedBox( + height: 200, + width: 200, + child: FilamentWidget( + controller: _filamentController, + ))), + Expanded( + child: SingleChildScrollView( + child: Wrap( alignment: WrapAlignment.end, crossAxisAlignment: WrapCrossAlignment.end, - children: [ - ElevatedButton( + children: [ + ElevatedButton( child: const Text('load background image'), onPressed: () async { - await _filamentController.setBackgroundImage( - 'assets/background3.png'); + await _filamentController + .setBackgroundImage('assets/background.png'); }), ElevatedButton( child: const Text('load skybox'), @@ -61,46 +66,57 @@ class _MyAppState extends State { child: const Text('remove skybox'), onPressed: () async { await _filamentController.removeSkybox(); - } - ), + }), ElevatedButton( - child: const Text('load cube'), - onPressed: () async { - await _filamentController.loadGltf( - 'assets/cube.glb' ,"assets"); - print(await _filamentController.getAnimationNames()); + child: const Text('load cube GLB'), + onPressed:_cube != null ? null : () async { + _cube = await _filamentController + .loadGlb('assets/cube.glb'); + print(await _filamentController + .getAnimationNames(_cube!)); }), - ElevatedButton( + ElevatedButton( + child: const Text('load cube GLTF'), + onPressed:() async { + if(_cube != null) { + await _filamentController.removeAsset(_cube!); + } + _cube = await _filamentController + .loadGltf('assets/cube.gltf', 'assets'); + print(await _filamentController + .getAnimationNames(_cube!)); + }), + ElevatedButton( child: const Text('load flight helmet'), - onPressed: () async { - await _filamentController.loadGltf( - 'assets/FlightHelmet/FlightHelmet.gltf', 'assets/FlightHelmet'); + onPressed:_flightHelmet != null ? null : () async { + _flightHelmet = await _filamentController.loadGltf( + 'assets/FlightHelmet/FlightHelmet.gltf', + 'assets/FlightHelmet'); }), - ElevatedButton( - child: const Text('remove asset'), + ElevatedButton( + child: const Text('remove cube'), onPressed: () async { - await _filamentController - .removeAsset(); + await _filamentController.removeAsset(_cube!); }), ElevatedButton( child: const Text('set all weights to 1'), onPressed: () async { await _filamentController - .applyWeights(List.filled(8, 1.0)); + .applyWeights(_cube!, List.filled(8, 1.0)); }), ElevatedButton( child: const Text('set all weights to 0'), onPressed: () async { await _filamentController - .applyWeights(List.filled(8, 0)); + .applyWeights(_cube!, List.filled(8, 0)); }), ElevatedButton( onPressed: () => - _filamentController.playAnimation(0, loop: _loop), + _filamentController.playAnimation(_cube!, 0, loop: _loop), child: const Text('play animation')), ElevatedButton( onPressed: () { - _filamentController.stopAnimation(); + _filamentController.stopAnimation(_cube!); }, child: const Text('stop animation')), Checkbox( @@ -120,7 +136,7 @@ class _MyAppState extends State { child: const Text('zoom out')), ElevatedButton( onPressed: () { - _filamentController.setCamera("Camera_Orientation"); + _filamentController.setCamera(_cube!, "Camera_Orientation"); }, child: const Text('set camera')), ElevatedButton( @@ -135,6 +151,7 @@ class _MyAppState extends State { List.filled(numWeights, frame / totalFrames)); _filamentController.animate( + _cube!, frames.reduce((a, b) => a + b), numWeights, totalFrames, @@ -145,7 +162,7 @@ class _MyAppState extends State { builder: (innerCtx) => ElevatedButton( onPressed: () async { final names = await _filamentController - .getTargetNames("Cube"); + .getTargetNames(_cube!, "Cube"); await showDialog( builder: (ctx) { @@ -173,7 +190,7 @@ class _MyAppState extends State { builder: (innerCtx) => ElevatedButton( onPressed: () async { final names = - await _filamentController.getAnimationNames(); + await _filamentController.getAnimationNames(_cube!); await showDialog( builder: (ctx) { @@ -219,52 +236,4 @@ class _MyAppState extends State { } } -// ElevatedButton( -// child: Text('load skybox'), -// onPressed: () { -// _filamentController.loadSkybox( -// 'assets/default_env/default_env_skybox.ktx', -// 'assets/default_env/default_env_ibl.ktx'); -// }), -// ElevatedButton( -// child: Text('load gltf'), -// onPressed: () { -// _filamentController.loadGltf( -// 'assets/guy.gltf', 'assets', 'Material'); -// }), -// ElevatedButton( -// child: Text('create morpher'), -// onPressed: () { -// _filamentController.createMorpher( -// 'CC_Base_Body.003', 'CC_Base_Body.003', -// materialName: 'Material'); -// }), -// ])), -// Column( -// children: _targets -// .asMap() -// .map((i, t) => MapEntry( -// i, -// Row(children: [ -// Text(t), -// Slider( -// min: 0, -// max: 1, -// divisions: 10, -// value: weights[i], -// onChanged: (v) { -// setState(() { -// weights[i] = v; -// _filamentController -// .applyWeights(weights); -// }); -// }) -// ]))) -// .values -// .toList(), -// ) -// ElevatedButton( -// child: const Text('init'), -// onPressed: () async { -// await _filamentController.initialize(); -// }), + diff --git a/ios/src/FilamentViewer.cpp b/ios/src/FilamentViewer.cpp index 55da9fd2..11e4377c 100644 --- a/ios/src/FilamentViewer.cpp +++ b/ios/src/FilamentViewer.cpp @@ -16,7 +16,7 @@ #include "FilamentViewer.hpp" -#include "streambuf.hpp" +#include "StreamBufferAdapter.hpp" #include #include @@ -35,12 +35,13 @@ #include #include +#include #include #include #include -#include #include + #include #include @@ -59,773 +60,480 @@ #include -#include #include #include -#include "Log.h" - -#include "imagematerial.h" +#include "Log.hpp" +#include "SceneResources.hpp" +#include "image/imagematerial.h" using namespace filament; using namespace filament::math; using namespace gltfio; using namespace utils; -using namespace std::chrono; + using namespace image; -namespace filament -{ - class IndirectLight; - class LightManager; +namespace filament { +class IndirectLight; +class LightManager; +} // namespace filament + +namespace polyvox { + +const double kNearPlane = 0.05; // 5 cm +const double kFarPlane = 1000.0; // 1 km +const float kScaleMultiplier = 100.0f; +const float kAperture = 16.0f; +const float kShutterSpeed = 1.0f / 125.0f; +const float kSensitivity = 100.0f; + +// filament::math::mat4f composeMatrix(const filament::math::float3 +// &translation, +// const filament::math::quatf &rotation, +// const filament::math::float3 &scale) +// { +// float tx = translation[0]; +// float ty = translation[1]; +// float tz = translation[2]; +// float qx = rotation[0]; +// float qy = rotation[1]; +// float qz = rotation[2]; +// float qw = rotation[3]; +// float sx = scale[0]; +// float sy = scale[1]; +// float sz = scale[2]; +// return filament::math::mat4f( +// (1 - 2 * qy * qy - 2 * qz * qz) * sx, +// (2 * qx * qy + 2 * qz * qw) * sx, +// (2 * qx * qz - 2 * qy * qw) * sx, +// 0.f, +// (2 * qx * qy - 2 * qz * qw) * sy, +// (1 - 2 * qx * qx - 2 * qz * qz) * sy, +// (2 * qy * qz + 2 * qx * qw) * sy, +// 0.f, +// (2 * qx * qz + 2 * qy * qw) * sz, +// (2 * qy * qz - 2 * qx * qw) * sz, +// (1 - 2 * qx * qx - 2 * qy * qy) * sz, +// 0.f, tx, ty, tz, 1.f); +// } + +FilamentViewer::FilamentViewer(void *layer, LoadResource loadResource, + FreeResource freeResource) + : _layer(layer), _loadResource(loadResource), _freeResource(freeResource) { + _engine = Engine::create(Engine::Backend::OPENGL); + + _renderer = _engine->createRenderer(); + + _renderer->setDisplayInfo({.refreshRate = 60.0f, + .presentationDeadlineNanos = (uint64_t)0, + .vsyncOffsetNanos = (uint64_t)0}); + _scene = _engine->createScene(); + + Entity camera = EntityManager::get().create(); + _mainCamera = _engine->createCamera(camera); + _view = _engine->createView(); + _view->setScene(_scene); + _view->setCamera(_mainCamera); + + _cameraFocalLength = 28.0f; + _mainCamera->setExposure(kAperture, kShutterSpeed, kSensitivity); + + _swapChain = _engine->createSwapChain(_layer); + + View::DynamicResolutionOptions options; + options.enabled = true; + // options.homogeneousScaling = homogeneousScaling; + // options.minScale = filament::math::float2{ minScale }; + // options.maxScale = filament::math::float2{ maxScale }; + // options.sharpness = sharpness; + options.quality = View::QualityLevel::MEDIUM; + ; + _view->setDynamicResolutionOptions(options); + + View::MultiSampleAntiAliasingOptions multiSampleAntiAliasingOptions; + multiSampleAntiAliasingOptions.enabled = true; + + _view->setMultiSampleAntiAliasingOptions(multiSampleAntiAliasingOptions); + + _materialProvider = gltfio::createUbershaderProvider( + _engine, UBERARCHIVE_DEFAULT_DATA, UBERARCHIVE_DEFAULT_SIZE); + + EntityManager &em = EntityManager::get(); + _ncm = new NameComponentManager(em); + _assetLoader = AssetLoader::create({_engine, _materialProvider, _ncm, &em}); + _resourceLoader = new ResourceLoader({.engine = _engine, + .normalizeSkinningWeights = true, + .recomputeBoundingBoxes = true}); + _stbDecoder = createStbProvider(_engine); + _resourceLoader->addTextureProvider("image/png", _stbDecoder); + _resourceLoader->addTextureProvider("image/jpeg", _stbDecoder); + manipulator = Manipulator::Builder() + .orbitHomePosition(0.0f, 0.0f, 0.05f) + .targetPosition(0.0f, 0.0f, 0.0f) + .build(Mode::ORBIT); + + // Always add a direct light source since it is required for shadowing. + _sun = EntityManager::get().create(); + LightManager::Builder(LightManager::Type::SUN) + .color(Color::cct(6500.0f)) + .intensity(150000.0f) + .direction(math::float3(0.0f, 0.0f, -1.0f)) + .castShadows(false) + // .castShadows(true) + .build(*_engine, _sun); + _scene->addEntity(_sun); + + + _sceneAssetLoader = new SceneAssetLoader(_loadResource, + _freeResource, + _assetLoader, + _resourceLoader, + _ncm, + _engine, + _scene); } -namespace polyvox -{ +static constexpr float4 sFullScreenTriangleVertices[3] = { + {-1.0f, -1.0f, 1.0f, 1.0f}, + {3.0f, -1.0f, 1.0f, 1.0f}, + {-1.0f, 3.0f, 1.0f, 1.0f}}; - const double kNearPlane = 0.05; // 5 cm - const double kFarPlane = 1000.0; // 1 km - const float kScaleMultiplier = 100.0f; - const float kAperture = 16.0f; - const float kShutterSpeed = 1.0f / 125.0f; - const float kSensitivity = 100.0f; +static const uint16_t sFullScreenTriangleIndices[3] = {0, 1, 2}; - filament::math::mat4f composeMatrix(const filament::math::float3 &translation, - const filament::math::quatf &rotation, const filament::math::float3 &scale) - { - float tx = translation[0]; - float ty = translation[1]; - float tz = translation[2]; - float qx = rotation[0]; - float qy = rotation[1]; - float qz = rotation[2]; - float qw = rotation[3]; - float sx = scale[0]; - float sy = scale[1]; - float sz = scale[2]; - return filament::math::mat4f( - (1 - 2 * qy * qy - 2 * qz * qz) * sx, - (2 * qx * qy + 2 * qz * qw) * sx, - (2 * qx * qz - 2 * qy * qw) * sx, - 0.f, - (2 * qx * qy - 2 * qz * qw) * sy, - (1 - 2 * qx * qx - 2 * qz * qz) * sy, - (2 * qy * qz + 2 * qx * qw) * sy, - 0.f, - (2 * qx * qz + 2 * qy * qw) * sz, - (2 * qy * qz - 2 * qx * qw) * sz, - (1 - 2 * qx * qx - 2 * qy * qy) * sz, - 0.f, tx, ty, tz, 1.f); - } +void FilamentViewer::createImageRenderable() { - FilamentViewer::FilamentViewer( - void *layer, - LoadResource loadResource, - FreeResource freeResource) : _layer(layer), - _loadResource(loadResource), - _freeResource(freeResource) - { - _engine = Engine::create(Engine::Backend::OPENGL); + if (_imageEntity) + return; - _renderer = _engine->createRenderer(); + auto &em = EntityManager::get(); + _imageMaterial = + Material::Builder() + .package(IMAGEMATERIAL_IMAGE_DATA, IMAGEMATERIAL_IMAGE_SIZE) + .build(*_engine); - _renderer->setDisplayInfo({.refreshRate = 60.0f, - .presentationDeadlineNanos = (uint64_t)0, - .vsyncOffsetNanos = (uint64_t)0}); - _scene = _engine->createScene(); - Entity camera = EntityManager::get().create(); - _mainCamera = _engine->createCamera(camera); - _view = _engine->createView(); - _view->setScene(_scene); - _view->setCamera(_mainCamera); + _imageVb = VertexBuffer::Builder() + .vertexCount(3) + .bufferCount(1) + .attribute(VertexAttribute::POSITION, 0, + VertexBuffer::AttributeType::FLOAT4, 0) + .build(*_engine); - _cameraFocalLength = 28.0f; - _mainCamera->setExposure(kAperture, kShutterSpeed, kSensitivity); + _imageVb->setBufferAt( + *_engine, 0, + {sFullScreenTriangleVertices, sizeof(sFullScreenTriangleVertices)}); - _swapChain = _engine->createSwapChain(_layer); + _imageIb = IndexBuffer::Builder() + .indexCount(3) + .bufferType(IndexBuffer::IndexType::USHORT) + .build(*_engine); - View::DynamicResolutionOptions options; - options.enabled = true; - // options.homogeneousScaling = homogeneousScaling; - // options.minScale = filament::math::float2{ minScale }; - // options.maxScale = filament::math::float2{ maxScale }; - // options.sharpness = sharpness; - options.quality = View::QualityLevel::MEDIUM; - ; - _view->setDynamicResolutionOptions(options); + _imageIb->setBuffer(*_engine, {sFullScreenTriangleIndices, + sizeof(sFullScreenTriangleIndices)}); - View::MultiSampleAntiAliasingOptions multiSampleAntiAliasingOptions; - multiSampleAntiAliasingOptions.enabled = true; + Entity imageEntity = em.create(); + RenderableManager::Builder(1) + .boundingBox({{}, {1.0f, 1.0f, 1.0f}}) + .material(0, _imageMaterial->getDefaultInstance()) + .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, _imageVb, + _imageIb, 0, 3) + .culling(false) + .build(*_engine, imageEntity); - _view->setMultiSampleAntiAliasingOptions(multiSampleAntiAliasingOptions); + _scene->addEntity(imageEntity); - _materialProvider = gltfio::createUbershaderProvider(_engine, UBERARCHIVE_DEFAULT_DATA, UBERARCHIVE_DEFAULT_SIZE); + _imageEntity = &imageEntity; - EntityManager &em = EntityManager::get(); - _ncm = new NameComponentManager(em); - _assetLoader = AssetLoader::create({_engine, _materialProvider, _ncm, &em}); - _resourceLoader = new ResourceLoader( - {.engine = _engine, .normalizeSkinningWeights = true, .recomputeBoundingBoxes = true}); - _stbDecoder = createStbProvider(_engine); - _resourceLoader->addTextureProvider("image/png", _stbDecoder); - _resourceLoader->addTextureProvider("image/jpeg", _stbDecoder); - 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::SUN) - .color(Color::cct(6500.0f)) - .intensity(150000.0f) - .direction(math::float3(0.0f, 0.0f, -1.0f)) - .castShadows(false) - // .castShadows(true) - .build(*_engine, _sun); - _scene->addEntity(_sun); - } - - static constexpr float4 sFullScreenTriangleVertices[3] = { - {-1.0f, -1.0f, 1.0f, 1.0f}, - {3.0f, -1.0f, 1.0f, 1.0f}, - {-1.0f, 3.0f, 1.0f, 1.0f}}; - - static const uint16_t sFullScreenTriangleIndices[3] = {0, 1, 2}; - - void FilamentViewer::createImageRenderable() - { - - if (_imageEntity) - return; - - auto &em = EntityManager::get(); - _imageMaterial = Material::Builder() - .package(IMAGEMATERIAL_IMAGE_DATA, IMAGEMATERIAL_IMAGE_SIZE) + Texture *texture = Texture::Builder() + .width(1) + .height(1) + .levels(1) + .format(Texture::InternalFormat::RGBA8) + .sampler(Texture::Sampler::SAMPLER_2D) .build(*_engine); + static uint32_t pixel = 0; + Texture::PixelBufferDescriptor buffer(&pixel, 4, Texture::Format::RGBA, + Texture::Type::UBYTE); + texture->setImage(*_engine, 0, std::move(buffer)); +} - _imageVb = VertexBuffer::Builder() - .vertexCount(3) - .bufferCount(1) - .attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT4, 0) - .build(*_engine); +void FilamentViewer::setBackgroundImage(const char *resourcePath) { - _imageVb->setBufferAt( - *_engine, 0, {sFullScreenTriangleVertices, sizeof(sFullScreenTriangleVertices)}); + if (colorGrading) { + _engine->destroy(colorGrading); + } + ToneMapper *tm = new LinearToneMapper(); + colorGrading = ColorGrading::Builder().toneMapper(tm).build(*_engine); + delete tm; - _imageIb = IndexBuffer::Builder() - .indexCount(3) - .bufferType(IndexBuffer::IndexType::USHORT) - .build(*_engine); + _view->setColorGrading(colorGrading); - _imageIb->setBuffer(*_engine, - {sFullScreenTriangleIndices, sizeof(sFullScreenTriangleIndices)}); + createImageRenderable(); - Entity imageEntity = em.create(); - RenderableManager::Builder(1) - .boundingBox({{}, {1.0f, 1.0f, 1.0f}}) - .material(0, _imageMaterial->getDefaultInstance()) - .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, _imageVb, _imageIb, 0, 3) - .culling(false) - .build(*_engine, imageEntity); - - _scene->addEntity(imageEntity); - - _imageEntity = &imageEntity; - - Texture *texture = Texture::Builder() - .width(1) - .height(1) - .levels(1) - .format(Texture::InternalFormat::RGBA8) - .sampler(Texture::Sampler::SAMPLER_2D) - .build(*_engine); - static uint32_t pixel = 0; - Texture::PixelBufferDescriptor buffer(&pixel, 4, Texture::Format::RGBA, Texture::Type::UBYTE); - texture->setImage(*_engine, 0, std::move(buffer)); + if (_imageTexture) { + _engine->destroy(_imageTexture); + _imageTexture = nullptr; } - void FilamentViewer::setBackgroundImage(const char *resourcePath) - { + ResourceBuffer bg = _loadResource(resourcePath); - if (colorGrading) - { - _engine->destroy(colorGrading); - } - ToneMapper *tm = new LinearToneMapper(); - colorGrading = ColorGrading::Builder() - .toneMapper(tm) - .build(*_engine); - delete tm; + polyvox::StreamBufferAdapter sb((char *)bg.data, (char *)bg.data + bg.size); - _view->setColorGrading(colorGrading); + std::istream *inputStream = new std::istream(&sb); - createImageRenderable(); + LinearImage *image = new LinearImage(ImageDecoder::decode( + *inputStream, resourcePath, ImageDecoder::ColorSpace::SRGB)); - if (_imageTexture) - { - _engine->destroy(_imageTexture); - _imageTexture = nullptr; - } - - ResourceBuffer bg = _loadResource(resourcePath); - - polyvox::streambuf sb((char *)bg.data, (char *)bg.data + bg.size); - - std::istream *inputStream = new std::istream(&sb); - - LinearImage *image = new LinearImage(ImageDecoder::decode( - *inputStream, resourcePath, ImageDecoder::ColorSpace::SRGB)); - - if (!image->isValid()) - { - Log("Invalid image : %s", resourcePath); - return; - } - - delete inputStream; - - _freeResource(bg); - - uint32_t channels = image->getChannels(); - uint32_t w = image->getWidth(); - uint32_t h = image->getHeight(); - _imageTexture = Texture::Builder() - .width(w) - .height(h) - .levels(0xff) - .format(channels == 3 ? Texture::InternalFormat::RGB16F : Texture::InternalFormat::RGBA16F) - .sampler(Texture::Sampler::SAMPLER_2D) - .build(*_engine); - - Texture::PixelBufferDescriptor::Callback freeCallback = [](void *buf, size_t, void *data) - { - delete reinterpret_cast(data); - }; - - Texture::PixelBufferDescriptor buffer( - image->getPixelRef(), - size_t(w * h * channels * sizeof(float)), - channels == 3 ? Texture::Format::RGB : Texture::Format::RGBA, - Texture::Type::FLOAT, - freeCallback); - - _imageTexture->setImage(*_engine, 0, std::move(buffer)); - // _imageTexture->generateMipmaps(*_engine); - - float srcWidth = _imageTexture->getWidth(); - float srcHeight = _imageTexture->getHeight(); - float dstWidth = _view->getViewport().width; - float dstHeight = _view->getViewport().height; - - mat3f transform( - 1.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 1.0f); - - _imageMaterial->setDefaultParameter("transform", transform); - _imageMaterial->setDefaultParameter( - "image", _imageTexture, _imageSampler); - - _imageMaterial->setDefaultParameter("showImage", 1); - - _imageMaterial->setDefaultParameter( - "backgroundColor", RgbType::sRGB, float3(1.0f)); + if (!image->isValid()) { + Log("Invalid image : %s", resourcePath); + return; } - FilamentViewer::~FilamentViewer() - { - cleanup(); - } + delete inputStream; - Renderer *FilamentViewer::getRenderer() - { - return _renderer; - } + _freeResource(bg); - void FilamentViewer::createSwapChain(void *surface) - { - _swapChain = _engine->createSwapChain(surface); - Log("swapchain created."); - } + uint32_t channels = image->getChannels(); + uint32_t w = image->getWidth(); + uint32_t h = image->getHeight(); + _imageTexture = Texture::Builder() + .width(w) + .height(h) + .levels(0xff) + .format(channels == 3 ? Texture::InternalFormat::RGB16F + : Texture::InternalFormat::RGBA16F) + .sampler(Texture::Sampler::SAMPLER_2D) + .build(*_engine); - void FilamentViewer::destroySwapChain() - { - if (_swapChain) - { - _engine->destroy(_swapChain); - _swapChain = nullptr; - Log("Swapchain destroyed."); - } - } - - void FilamentViewer::applyWeights(float *weights, int count) - { - - for (size_t i = 0, c = _asset->getEntityCount(); i != c; ++i) - { - RenderableManager &rm = _engine->getRenderableManager(); - auto inst = rm.getInstance(_asset->getEntities()[i]); - rm.setMorphWeights( - inst, - weights, - count); - } - } - - void FilamentViewer::loadResources(string relativeResourcePath) - { - const char *const *const resourceUris = _asset->getResourceUris(); - const size_t resourceUriCount = _asset->getResourceUriCount(); - - Log("Loading %d resources for asset", resourceUriCount); - - for (size_t i = 0; i < resourceUriCount; i++) - { - string uri = relativeResourcePath + string(resourceUris[i]); - Log("Creating resource buffer for resource at %s", uri.c_str()); - ResourceBuffer buf = _loadResource(uri.c_str()); - - // using FunctionCallback = std::function; - - // auto cb = [&] (void * ptr, unsigned int len, void * misc) { - - // }; - // FunctionCallback fcb = cb; - ResourceLoader::BufferDescriptor b( - buf.data, buf.size); - _resourceLoader->addResourceData(resourceUris[i], std::move(b)); - _freeResource(buf); - } - - _resourceLoader->loadResources(_asset); - const Entity *entities = _asset->getEntities(); - RenderableManager &rm = _engine->getRenderableManager(); - for (int i = 0; i < _asset->getEntityCount(); i++) - { - Entity e = entities[i]; - auto inst = rm.getInstance(e); - rm.setCulling(inst, false); - } - - _animator = _asset->getAnimator(); - - _scene->addEntities(_asset->getEntities(), _asset->getEntityCount()); + Texture::PixelBufferDescriptor::Callback freeCallback = [](void *buf, size_t, + void *data) { + delete reinterpret_cast(data); }; - void FilamentViewer::loadGlb(const char *const uri) - { + Texture::PixelBufferDescriptor buffer( + image->getPixelRef(), size_t(w * h * channels * sizeof(float)), + channels == 3 ? Texture::Format::RGB : Texture::Format::RGBA, + Texture::Type::FLOAT, freeCallback); - Log("Loading GLB at URI %s", uri); + _imageTexture->setImage(*_engine, 0, std::move(buffer)); + _imageTexture->generateMipmaps(*_engine); - ResourceBuffer rbuf = _loadResource(uri); + float srcWidth = _imageTexture->getWidth(); + float srcHeight = _imageTexture->getHeight(); + float dstWidth = _view->getViewport().width; + float dstHeight = _view->getViewport().height; - _asset = _assetLoader->createAssetFromBinary( - (const uint8_t *)rbuf.data, rbuf.size); + mat3f transform(1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f); - if (!_asset) - { - Log("Unknown error loading GLB asset."); - exit(1); - } + _imageMaterial->setDefaultParameter("transform", transform); + _imageMaterial->setDefaultParameter("image", _imageTexture, _imageSampler); - int entityCount = _asset->getEntityCount(); + _imageMaterial->setDefaultParameter("showImage", 1); - _scene->addEntities(_asset->getEntities(), entityCount); + _imageMaterial->setDefaultParameter("backgroundColor", RgbType::sRGB, + float3(1.0f)); +} - Log("Added %d entities to scene", entityCount); - _resourceLoader->loadResources(_asset); - _animator = _asset->getAnimator(); +FilamentViewer::~FilamentViewer() { cleanup(); } - const Entity *entities = _asset->getEntities(); - RenderableManager &rm = _engine->getRenderableManager(); - for (int i = 0; i < _asset->getEntityCount(); i++) - { - Entity e = entities[i]; - auto inst = rm.getInstance(e); - rm.setCulling(inst, false); - } +Renderer *FilamentViewer::getRenderer() { return _renderer; } - _freeResource(rbuf); +void FilamentViewer::createSwapChain(void *surface) { + _swapChain = _engine->createSwapChain(surface); + Log("swapchain created."); +} - _animator->updateBoneMatrices(); - - // transformToUnitCube(); - - _asset->releaseSourceData(); - - Log("Successfully loaded GLB."); +void FilamentViewer::destroySwapChain() { + if (_swapChain) { + _engine->destroy(_swapChain); + _swapChain = nullptr; + Log("Swapchain destroyed."); } +} - void FilamentViewer::loadGltf(const char *const uri, const char *const relativeResourcePath) - { - - Log("Loading GLTF at URI %s", uri); - - _asset = nullptr; - _animator = nullptr; - - ResourceBuffer rbuf = _loadResource(uri); - - // Parse the glTF file and create Filament entities. - Log("Creating asset from JSON"); - _asset = _assetLoader->createAssetFromJson((uint8_t *)rbuf.data, rbuf.size); - Log("Created asset from JSON"); - - if (!_asset) - { - Log("Unable to parse asset"); - exit(1); - } - Log("Loading relative resources"); - loadResources(string(relativeResourcePath) + string("/")); - Log("Loaded relative resources"); - _asset->releaseSourceData(); - - Log("Load complete for GLTF at URI %s", uri); - - // transformToUnitCube(); +SceneAsset *FilamentViewer::loadGlb(const char *const uri) { + Log("Loading GLB at URI %s", uri); + SceneAsset *asset = _sceneAssetLoader->fromGlb(uri); + if (!asset) { + Log("Unknown error loading asset."); + } else { + _assets.push_back(asset); } + return asset; +} - void FilamentViewer::removeAsset() - { - if (!_asset) - { - Log("No asset loaded, ignoring call."); - return; - } - - mtx.lock(); - - _resourceLoader->evictResourceData(); - _scene->removeEntities(_asset->getEntities(), _asset->getEntityCount()); - _assetLoader->destroyAsset(_asset); - _asset = nullptr; - _animator = nullptr; - _morphAnimationBuffer = nullptr; - _embeddedAnimationBuffer = nullptr; - _view->setCamera(_mainCamera); - mtx.unlock(); +SceneAsset *FilamentViewer::loadGltf(const char *const uri, + const char *const relativeResourcePath) { + Log("Loading GLTF at URI %s with relativeResourcePath %s", uri, + relativeResourcePath); + SceneAsset *asset = _sceneAssetLoader->fromGltf(uri, relativeResourcePath); + if (!asset) { + Log("Unknown error loading asset."); + } else { + _assets.push_back(asset); } + return asset; +} - /// - /// Sets the active camera to the GLTF camera node specified by [name]. - /// N.B. Blender will generally export a three-node hierarchy - Camera1->Camera_Orientation->Camera2. - /// The correct name will be the grandchild (i.e. Camera2 in this scenario). - /// - bool FilamentViewer::setCamera(const char *cameraName) - { - Log("Attempting to set camera to %s.", cameraName); - size_t count = _asset->getCameraEntityCount(); - if (count == 0) - { - Log("Failed, no cameras found in current asset."); - return false; +void FilamentViewer::removeAsset(SceneAsset *asset) { + mtx.lock(); + _sceneAssetLoader->remove(asset); + _view->setCamera(_mainCamera); + bool erased = false; + for (auto it = _assets.begin(); it != _assets.end();) { + if (*it == asset) { + _assets.erase(it); + erased = true; + break; } + } + if (!erased) { + Log("Error removing asset from scene : not found"); + } + mtx.unlock(); +} - const utils::Entity *cameras = _asset->getCameraEntities(); - Log("%zu cameras found in current asset", count); - for (int i = 0; i < count; i++) - { - - auto inst = _ncm->getInstance(cameras[i]); - const char *name = _ncm->getName(inst); - Log("Camera %d : %s", i, name); - if (strcmp(name, cameraName) == 0) - { - - Camera *camera = _engine->getCameraComponent(cameras[i]); - _view->setCamera(camera); - - const Viewport &vp = _view->getViewport(); - const double aspect = (double)vp.width / vp.height; - - Log("Camera focal length : %f aspect %f", camera->getFocalLength(), aspect); - camera->setScaling({1.0 / aspect, 1.0}); - Log("Successfully set camera."); - return true; - } - } - Log("Unable to locate camera under name %s ", cameraName); +/// +/// Sets the active camera to the GLTF camera node specified by [name]. +/// N.B. Blender will generally export a three-node hierarchy - +/// Camera1->Camera_Orientation->Camera2. The correct name will be the +/// grandchild (i.e. Camera2 in this scenario). +/// +bool FilamentViewer::setCamera(SceneAsset *asset, const char *cameraName) { + Log("Attempting to set camera to %s.", cameraName); + size_t count = asset->getCameraEntityCount(); + if (count == 0) { + Log("Failed, no cameras found in current asset."); return false; } - unique_ptr> FilamentViewer::getAnimationNames() - { - if (!_asset) - { - Log("No asset, ignoring call."); - return nullptr; - } - size_t count = _animator->getAnimationCount(); + const utils::Entity *cameras = asset->getCameraEntities(); + Log("%zu cameras found in asset", count); + for (int i = 0; i < count; i++) { - Log("Found %d animations in asset.", count); + auto inst = _ncm->getInstance(cameras[i]); + const char *name = _ncm->getName(inst); + Log("Camera %d : %s", i, name); + if (strcmp(name, cameraName) == 0) { - unique_ptr> names = make_unique>(); + Camera *camera = _engine->getCameraComponent(cameras[i]); + _view->setCamera(camera); - for (size_t i = 0; i < count; i++) - { - names->push_back(_animator->getAnimationName(i)); - } + const Viewport &vp = _view->getViewport(); + const double aspect = (double)vp.width / vp.height; - return names; - } - - unique_ptr> FilamentViewer::getTargetNames(const char *meshName) - { - if (!_asset) - { - Log("No asset, ignoring call."); - return nullptr; - } - Log("Retrieving morph target names for mesh %s", meshName); - unique_ptr> names = make_unique>(); - const Entity *entities = _asset->getEntities(); - RenderableManager &rm = _engine->getRenderableManager(); - for (int i = 0; i < _asset->getEntityCount(); i++) - { - Entity e = entities[i]; - auto inst = _ncm->getInstance(e); - const char *name = _ncm->getName(inst); - Log("Got entity instance name %s", name); - if (strcmp(name, meshName) == 0) - { - size_t count = _asset->getMorphTargetCountAt(e); - for (int j = 0; j < count; j++) - { - const char *morphName = _asset->getMorphTargetNameAt(e, j); - names->push_back(morphName); - } - break; - } - } - return names; - } - - void FilamentViewer::loadSkybox(const char *const skyboxPath) - { - if (!skyboxPath) - { - _scene->setSkybox(nullptr); - } - else - { - ResourceBuffer skyboxBuffer = _loadResource(skyboxPath); - - image::Ktx1Bundle *skyboxBundle = - new image::Ktx1Bundle(static_cast(skyboxBuffer.data), static_cast(skyboxBuffer.size)); - _skyboxTexture = ktxreader::Ktx1Reader::createTexture(_engine, skyboxBundle, false); - _skybox = filament::Skybox::Builder().environment(_skyboxTexture).build(*_engine); - - _scene->setSkybox(_skybox); - _freeResource(skyboxBuffer); + Log("Camera focal length : %f aspect %f", camera->getFocalLength(), + aspect); + camera->setScaling({1.0 / aspect, 1.0}); + Log("Successfully set camera."); + return true; } } - - void FilamentViewer::removeSkybox() - { - _scene->setSkybox(nullptr); - } - - void FilamentViewer::removeIbl() - { - _scene->setIndirectLight(nullptr); - } - - void FilamentViewer::loadIbl(const char *const iblPath) - { - if (!iblPath) - { - _scene->setIndirectLight(nullptr); - } - else - { - - Log("Loading IBL from %s", iblPath); - - // Load IBL. - ResourceBuffer iblBuffer = _loadResource(iblPath); - - image::Ktx1Bundle *iblBundle = new image::Ktx1Bundle( - static_cast(iblBuffer.data), static_cast(iblBuffer.size)); - math::float3 harmonics[9]; - iblBundle->getSphericalHarmonics(harmonics); - _iblTexture = ktxreader::Ktx1Reader::createTexture(_engine, iblBundle, false); - _indirectLight = IndirectLight::Builder() - .reflections(_iblTexture) - .irradiance(3, harmonics) - .intensity(30000.0f) - .build(*_engine); - _scene->setIndirectLight(_indirectLight); - - _freeResource(iblBuffer); - - Log("Skybox/IBL load complete."); - } - } - - void FilamentViewer::transformToUnitCube() - { - if (!_asset) - { - Log("No asset, cannot transform."); - return; - } - auto &tm = _engine->getTransformManager(); - auto aabb = _asset->getBoundingBox(); - auto center = aabb.center(); - auto halfExtent = aabb.extent(); - auto maxExtent = max(halfExtent) * 2; - auto scaleFactor = 2.0f / maxExtent; - auto transform = math::mat4f::scaling(scaleFactor) * math::mat4f::translation(-center); - tm.setTransform(tm.getInstance(_asset->getRoot()), transform); - } - - void FilamentViewer::cleanup() - { - _resourceLoader->asyncCancelLoad(); - _assetLoader->destroyAsset(_asset); - _materialProvider->destroyMaterials(); - AssetLoader::destroy(&_assetLoader); - }; - - void FilamentViewer::render() - { - - if (!_view || !_mainCamera || !_swapChain) - { - Log("Not ready for rendering"); - return; - } - - mtx.lock(); - if (_asset) - { - updateMorphAnimation(); - updateEmbeddedAnimation(); - } - - math::float3 eye, target, upward; - manipulator->getLookAt(&eye, &target, &upward); - _mainCamera->lookAt(eye, target, upward); - - // Render the scene, unless the renderer wants to skip the frame. - if (_renderer->beginFrame(_swapChain)) - { - _renderer->render(_view); - _renderer->endFrame(); - } - mtx.unlock(); - } - - void FilamentViewer::updateViewportAndCameraProjection(int width, int height, float contentScaleFactor) - { - if (!_view || !_mainCamera) - { - Log("Skipping camera update, no view or camrea"); - return; - } - - const uint32_t _width = width * contentScaleFactor; - const uint32_t _height = height * contentScaleFactor; - _view->setViewport({0, 0, _width, _height}); - - const double aspect = (double)width / height; - _mainCamera->setLensProjection(_cameraFocalLength, aspect, kNearPlane, kFarPlane); - - Log("Set viewport to width: %d height: %d scaleFactor : %f", width, height, contentScaleFactor); - } - - 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); - } - - void FilamentViewer::updateMorphAnimation() - { - if (!_morphAnimationBuffer) - { - return; - } - - 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); - - 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); - } - } - } - - void FilamentViewer::playAnimation(int index, bool loop) - { - 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); - } - } - - void FilamentViewer::stopAnimation() - { - // TODO - does this need to be threadsafe? - _embeddedAnimationBuffer = nullptr; - } - - void FilamentViewer::updateEmbeddedAnimation() - { - if (!_embeddedAnimationBuffer) - { - return; - } - 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(); - } - else - { - _embeddedAnimationBuffer = nullptr; - return; - } - } - else - { - startTime = dur.count(); - } - - _animator->applyAnimation(_embeddedAnimationBuffer->animationIndex, startTime); - _animator->updateBoneMatrices(); - } - + Log("Unable to locate camera under name %s ", cameraName); + return false; } + +void FilamentViewer::loadSkybox(const char *const skyboxPath) { + if (!skyboxPath) { + _scene->setSkybox(nullptr); + } else { + ResourceBuffer skyboxBuffer = _loadResource(skyboxPath); + + image::Ktx1Bundle *skyboxBundle = + new image::Ktx1Bundle(static_cast(skyboxBuffer.data), + static_cast(skyboxBuffer.size)); + _skyboxTexture = + ktxreader::Ktx1Reader::createTexture(_engine, skyboxBundle, false); + _skybox = + filament::Skybox::Builder().environment(_skyboxTexture).build(*_engine); + + _scene->setSkybox(_skybox); + _freeResource(skyboxBuffer); + } +} + +void FilamentViewer::removeSkybox() { _scene->setSkybox(nullptr); } + +void FilamentViewer::removeIbl() { _scene->setIndirectLight(nullptr); } + +void FilamentViewer::loadIbl(const char *const iblPath) { + if (!iblPath) { + _scene->setIndirectLight(nullptr); + } else { + + Log("Loading IBL from %s", iblPath); + + // Load IBL. + ResourceBuffer iblBuffer = _loadResource(iblPath); + + image::Ktx1Bundle *iblBundle = + new image::Ktx1Bundle(static_cast(iblBuffer.data), + static_cast(iblBuffer.size)); + math::float3 harmonics[9]; + iblBundle->getSphericalHarmonics(harmonics); + _iblTexture = + ktxreader::Ktx1Reader::createTexture(_engine, iblBundle, false); + _indirectLight = IndirectLight::Builder() + .reflections(_iblTexture) + .irradiance(3, harmonics) + .intensity(30000.0f) + .build(*_engine); + _scene->setIndirectLight(_indirectLight); + + _freeResource(iblBuffer); + + Log("Skybox/IBL load complete."); + } +} + +void FilamentViewer::cleanup() { + _resourceLoader->asyncCancelLoad(); + _materialProvider->destroyMaterials(); + AssetLoader::destroy(&_assetLoader); +}; + +void FilamentViewer::render() { + + if (!_view || !_mainCamera || !_swapChain) { + Log("Not ready for rendering"); + return; + } + + mtx.lock(); + for (auto &asset : _assets) { + asset->updateAnimations(); + } + + math::float3 eye, target, upward; + manipulator->getLookAt(&eye, &target, &upward); + _mainCamera->lookAt(eye, target, upward); + + // Render the scene, unless the renderer wants to skip the frame. + if (_renderer->beginFrame(_swapChain)) { + _renderer->render(_view); + _renderer->endFrame(); + } + mtx.unlock(); +} + +void FilamentViewer::updateViewportAndCameraProjection( + int width, int height, float contentScaleFactor) { + if (!_view || !_mainCamera) { + Log("Skipping camera update, no view or camrea"); + return; + } + + const uint32_t _width = width * contentScaleFactor; + const uint32_t _height = height * contentScaleFactor; + _view->setViewport({0, 0, _width, _height}); + + const double aspect = (double)width / height; + _mainCamera->setLensProjection(_cameraFocalLength, aspect, kNearPlane, + kFarPlane); + + Log("Set viewport to width: %d height: %d scaleFactor : %f", width, height, + contentScaleFactor); +} + +} // namespace polyvox diff --git a/ios/src/FilamentViewer.hpp b/ios/src/FilamentViewer.hpp index 52959872..b739a180 100644 --- a/ios/src/FilamentViewer.hpp +++ b/ios/src/FilamentViewer.hpp @@ -30,6 +30,10 @@ #include #include +#include "SceneAssetLoader.hpp" +#include "SceneAsset.hpp" +#include "SceneResources.hpp" + using namespace std; using namespace filament; using namespace filament::math; @@ -39,55 +43,6 @@ using namespace camutils; namespace polyvox { - - typedef std::chrono::time_point time_point_t; - - struct EmbeddedAnimationBuffer { - EmbeddedAnimationBuffer(int animationIndex, float duration, bool loop) : animationIndex(animationIndex), duration(duration), loop(loop) {} - bool hasStarted = false; - int animationIndex; - float duration = 0; - time_point_t lastTime; - bool loop; - }; - - - - struct ResourceBuffer { - ResourceBuffer(const void* data, const uint32_t size, const uint32_t id) : data(data), size(size), id(id) {}; - - ResourceBuffer& operator=(ResourceBuffer other) - { - data = other.data; - size = other.size; - id = other.id; - return *this; - } - const void* data; - uint32_t size; - uint32_t id; - }; - - using LoadResource = std::function; - using FreeResource = std::function; - - struct MorphAnimationBuffer { - - MorphAnimationBuffer(float* frameData, - int numWeights, - int numFrames, - float frameLength) : frameData(frameData), numWeights(numWeights), numFrames(numFrames), frameLengthInMs(frameLength) { - } - - int frameIndex = -1; - int numFrames; - float frameLengthInMs; - time_point_t startTime; - - float* frameData; - int numWeights; - }; - class FilamentViewer { public: FilamentViewer(void* layer, LoadResource loadResource, FreeResource freeResource); @@ -99,42 +54,16 @@ namespace polyvox { void loadIbl(const char* const iblUri); void removeIbl(); - void loadGlb(const char* const uri); - void loadGltf(const char* const uri, const char* relativeResourcePath); - void removeAsset(); + SceneAsset* loadGlb(const char* const uri); + SceneAsset* loadGltf(const char* const uri, const char* relativeResourcePath); + void removeAsset(SceneAsset* asset); void updateViewportAndCameraProjection(int height, int width, float scaleFactor); void render(); - unique_ptr> getTargetNames(const char* meshName); - unique_ptr> getAnimationNames(); + Manipulator* manipulator; - - /// - /// Manually set the weights for all morph targets in the assets to the provided values. - /// See [animateWeights] if you want to automatically - /// - void applyWeights(float* weights, int count); - - /// - /// Update the asset's morph target weights every "frame" (which is an arbitrary length of time, i.e. this is not the same as a frame at the framerate of the underlying rendering framework). - /// Accordingly: - /// length(data) = numWeights * numFrames - /// total_animation_duration_in_ms = number_of_frames * frameLengthInMs - /// - void animateWeights(float* data, int numWeights, int numFrames, float frameLengthInMs); - - /// - /// Play an embedded animation (i.e. an animation node embedded in the GLTF asset). If [loop] is true, the animation will repeat indefinitely. - /// - void playAnimation(int index, bool loop); - - /// - /// Immediately stop the currently playing animation. NOOP if no animation is playing. - /// - void stopAnimation(); - - bool setCamera(const char* nodeName); + bool setCamera(SceneAsset* asset, const char* nodeName); void destroySwapChain(); void createSwapChain(void* surface); @@ -161,10 +90,10 @@ namespace polyvox { SwapChain* _swapChain = nullptr; - Animator* _animator; + vector _assets; AssetLoader* _assetLoader; - FilamentAsset* _asset = nullptr; + SceneAssetLoader* _sceneAssetLoader; NameComponentManager* _ncm; std::mutex mtx; // mutex to ensure thread safety when removing assets @@ -184,24 +113,15 @@ namespace polyvox { float _cameraFocalLength = 0.0f; - void updateMorphAnimation(); - void updateEmbeddedAnimation(); - - // animation flags; - bool isAnimating; - unique_ptr _morphAnimationBuffer; - unique_ptr _embeddedAnimationBuffer; - + // these flags relate to the textured quad we use for rendering unlit background images Texture* _imageTexture = nullptr; Entity* _imageEntity = nullptr; VertexBuffer* _imageVb = nullptr; IndexBuffer* _imageIb = nullptr; Material* _imageMaterial = nullptr; TextureSampler _imageSampler; - ColorGrading *colorGrading = nullptr; - }; diff --git a/ios/src/Log.h b/ios/src/Log.hpp similarity index 79% rename from ios/src/Log.h rename to ios/src/Log.hpp index b487fa19..0f30594d 100644 --- a/ios/src/Log.h +++ b/ios/src/Log.hpp @@ -1,3 +1,8 @@ +#pragma once + +#ifndef POLYVOX_FILAMENT_LOG_H +#define POLYVOX_FILAMENT_LOG_H + #ifdef __OBJC__ #import #elif defined __ANDROID__ @@ -7,7 +12,7 @@ #include #endif -void Log(const char *fmt, ...) { +static void Log(const char *fmt, ...) { va_list args; va_start(args, fmt); @@ -23,3 +28,5 @@ void Log(const char *fmt, ...) { va_end(args); } + +#endif \ No newline at end of file diff --git a/ios/src/SceneAsset.cpp b/ios/src/SceneAsset.cpp new file mode 100644 index 00000000..01ea017f --- /dev/null +++ b/ios/src/SceneAsset.cpp @@ -0,0 +1,193 @@ +#include +#include "SceneResources.hpp" +#include "SceneAsset.hpp" +#include "Log.hpp" +#include +#include +#include +#include +#include + +#include + + +using namespace std::chrono; + +namespace polyvox { + +using namespace std; +using namespace filament; +using namespace filament::gltfio; +using namespace utils; + +SceneAsset::SceneAsset(FilamentAsset* asset, Engine* engine, NameComponentManager* ncm) + : _asset(asset), _engine(engine), _ncm(ncm) { + _animator = _asset->getAnimator(); + } + +SceneAsset::~SceneAsset() { _asset = nullptr; } + +void SceneAsset::applyWeights(float *weights, int count) { + RenderableManager& rm = _engine->getRenderableManager(); + for (size_t i = 0, c = _asset->getEntityCount(); i != c; ++i) { + auto inst = rm.getInstance(_asset->getEntities()[i]); + rm.setMorphWeights(inst, weights, count); + } +} + +void SceneAsset::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); +} + +void SceneAsset::updateAnimations() { + updateMorphAnimation(); + updateEmbeddedAnimation(); +} + +void SceneAsset::updateMorphAnimation() { + if (!_morphAnimationBuffer) { + return; + } + + 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); + + 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); + } + } +} + +void SceneAsset::playAnimation(int index, bool loop) { + if (index > _animator->getAnimationCount() - 1) { + Log("Asset does not contain an animation at index %d", index); + } else { + _boneAnimationStatus = make_unique( + index, _animator->getAnimationDuration(index), loop); + } +} + +void SceneAsset::stopAnimation() { + // TODO - does this need to be threadsafe? + _boneAnimationStatus = nullptr; +} + +void SceneAsset::updateEmbeddedAnimation() { + if (!_boneAnimationStatus) { + return; + } + + duration dur = duration_cast>( + high_resolution_clock::now() - _boneAnimationStatus->lastTime); + float startTime = 0; + if (!_boneAnimationStatus->hasStarted) { + _boneAnimationStatus->hasStarted = true; + _boneAnimationStatus->lastTime = high_resolution_clock::now(); + } else if (dur.count() >= _boneAnimationStatus->duration) { + if (_boneAnimationStatus->loop) { + _boneAnimationStatus->lastTime = high_resolution_clock::now(); + } else { + _boneAnimationStatus = nullptr; + return; + } + } else { + startTime = dur.count(); + } + + _animator->applyAnimation(_boneAnimationStatus->animationIndex, startTime); + _animator->updateBoneMatrices(); +} + +unique_ptr> SceneAsset::getAnimationNames() { + size_t count = _animator->getAnimationCount(); + + Log("Found %d animations in asset.", count); + + unique_ptr> names = make_unique>(); + + for (size_t i = 0; i < count; i++) { + names->push_back(_animator->getAnimationName(i)); + } + + return names; +} + +unique_ptr> SceneAsset::getTargetNames(const char *meshName) { + if (!_asset) { + Log("No asset, ignoring call."); + return nullptr; + } + Log("Retrieving morph target names for mesh %s", meshName); + unique_ptr> names = make_unique>(); + const Entity *entities = _asset->getEntities(); + RenderableManager &rm = _engine->getRenderableManager(); + for (int i = 0; i < _asset->getEntityCount(); i++) { + Entity e = entities[i]; + auto inst = _ncm->getInstance(e); + const char *name = _ncm->getName(inst); + Log("Got entity instance name %s", name); + if (strcmp(name, meshName) == 0) { + size_t count = _asset->getMorphTargetCountAt(e); + for (int j = 0; j < count; j++) { + const char *morphName = _asset->getMorphTargetNameAt(e, j); + names->push_back(morphName); + } + break; + } + } + return names; +} + +void SceneAsset::transformToUnitCube() + { + if (!_asset) + { + Log("No asset, cannot transform."); + return; + } + auto &tm = _engine->getTransformManager(); + auto aabb = _asset->getBoundingBox(); + auto center = aabb.center(); + auto halfExtent = aabb.extent(); + auto maxExtent = max(halfExtent) * 2; + auto scaleFactor = 2.0f / maxExtent; + auto transform = math::mat4f::scaling(scaleFactor) * math::mat4f::translation(-center); + tm.setTransform(tm.getInstance(_asset->getRoot()), transform); + } + + const utils::Entity* SceneAsset::getCameraEntities() { + return _asset->getCameraEntities(); + } + + size_t SceneAsset::getCameraEntityCount() { + return _asset->getCameraEntityCount(); + } + +} // namespace polyvox \ No newline at end of file diff --git a/ios/src/SceneAsset.hpp b/ios/src/SceneAsset.hpp new file mode 100644 index 00000000..a9e65fc4 --- /dev/null +++ b/ios/src/SceneAsset.hpp @@ -0,0 +1,83 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "SceneResources.hpp" + + +namespace polyvox { + using namespace filament; + using namespace filament::gltfio; + using namespace utils; + + using namespace std; + class SceneAsset { + friend class SceneAssetLoader; + public: + SceneAsset(FilamentAsset* asset, Engine* engine, NameComponentManager* ncm); + ~SceneAsset(); + + unique_ptr> getTargetNames(const char* meshName); + unique_ptr> getAnimationNames(); + + /// + /// Update the bone/morph target animations to reflect the current frame (if applicable). + /// + void updateAnimations(); + + /// + /// Immediately stop the currently playing animation. NOOP if no animation is playing. + /// + void stopAnimation(); + + /// + /// Play an embedded animation (i.e. an animation node embedded in the GLTF asset). If [loop] is true, the animation will repeat indefinitely. + /// + void playAnimation(int index, bool loop); + + /// + /// Manually set the weights for all morph targets in the assets to the provided values. + /// See [animateWeights] if you want to automatically + /// + void applyWeights(float* weights, int count); + + /// + /// Update the asset's morph target weights every "frame" (which is an arbitrary length of time, i.e. this is not the same as a frame at the framerate of the underlying rendering framework). + /// Accordingly: + /// length(data) = numWeights * numFrames + /// total_animation_duration_in_ms = number_of_frames * frameLengthInMs + /// + void animateWeights(float* data, int numWeights, int numFrames, float frameLengthInMs); + + void transformToUnitCube(); + + const utils::Entity* getCameraEntities(); + + size_t getCameraEntityCount(); + + + private: + FilamentAsset* _asset = nullptr; + Engine* _engine = nullptr; + NameComponentManager* _ncm; + void updateMorphAnimation(); + void updateEmbeddedAnimation(); + + Animator* _animator; + + // animation flags; + bool isAnimating; + unique_ptr _morphAnimationBuffer; + unique_ptr _boneAnimationStatus; + + }; +} \ No newline at end of file diff --git a/ios/src/SceneAssetLoader.cpp b/ios/src/SceneAssetLoader.cpp new file mode 100644 index 00000000..6f1deacd --- /dev/null +++ b/ios/src/SceneAssetLoader.cpp @@ -0,0 +1,123 @@ +#include "SceneAssetLoader.hpp" +#include "Log.hpp" + +#include + +namespace polyvox { + +using namespace filament; +using namespace filament::gltfio; + +SceneAssetLoader::SceneAssetLoader(LoadResource loadResource, + FreeResource freeResource, + AssetLoader *assetLoader, + ResourceLoader *resourceLoader, + NameComponentManager *ncm, Engine *engine, + Scene *scene) + : _loadResource(loadResource), _freeResource(freeResource), + _assetLoader(assetLoader), _resourceLoader(resourceLoader), _ncm(ncm), + _engine(engine), _scene(scene) {} + +SceneAsset *SceneAssetLoader::fromGltf(const char *uri, + const char *relativeResourcePath) { + ResourceBuffer rbuf = _loadResource(uri); + + // Parse the glTF file and create Filament entities. + Log("Creating asset from JSON"); + FilamentAsset *asset = + _assetLoader->createAssetFromJson((uint8_t *)rbuf.data, rbuf.size); + Log("Created asset from JSON"); + + if (!asset) { + Log("Unable to parse asset"); + return nullptr; + } + Log("Loading relative resources"); + + const char *const *const resourceUris = asset->getResourceUris(); + const size_t resourceUriCount = asset->getResourceUriCount(); + + Log("Loading %d resources for asset", resourceUriCount); + + for (size_t i = 0; i < resourceUriCount; i++) { + string uri = + string(relativeResourcePath) + string("/") + string(resourceUris[i]); + Log("Creating resource buffer for resource at %s", uri.c_str()); + ResourceBuffer buf = _loadResource(uri.c_str()); + + // using FunctionCallback = std::function; auto cb = [&] (void * ptr, unsigned int len, void * misc) { + // }; + // FunctionCallback fcb = cb; + + ResourceLoader::BufferDescriptor b(buf.data, buf.size); + _resourceLoader->addResourceData(resourceUris[i], std::move(b)); + _freeResource(buf); + } + + _resourceLoader->loadResources(asset); + const Entity *entities = asset->getEntities(); + RenderableManager &rm = _engine->getRenderableManager(); + for (int i = 0; i < asset->getEntityCount(); i++) { + Entity e = entities[i]; + auto inst = rm.getInstance(e); + rm.setCulling(inst, false); + } + + asset->getAnimator()->updateBoneMatrices(); + + _scene->addEntities(asset->getEntities(), asset->getEntityCount()); + + Log("Loaded relative resources"); + asset->releaseSourceData(); + + Log("Load complete for GLTF at URI %s", uri); + return new SceneAsset(asset, _engine, _ncm); +} + +SceneAsset *SceneAssetLoader::fromGlb(const char *uri) { + Log("Loading GLB at URI %s", uri); + + ResourceBuffer rbuf = _loadResource(uri); + + FilamentAsset *asset = _assetLoader->createAssetFromBinary( + (const uint8_t *)rbuf.data, rbuf.size); + + if (!asset) { + Log("Unknown error loading GLB asset."); + return nullptr; + } + + int entityCount = asset->getEntityCount(); + + _scene->addEntities(asset->getEntities(), entityCount); + + Log("Added %d entities to scene", entityCount); + _resourceLoader->loadResources(asset); + + const Entity *entities = asset->getEntities(); + RenderableManager &rm = _engine->getRenderableManager(); + for (int i = 0; i < asset->getEntityCount(); i++) { + Entity e = entities[i]; + auto inst = rm.getInstance(e); + // check this + rm.setCulling(inst, false); + } + + _freeResource(rbuf); + + asset->getAnimator()->updateBoneMatrices(); + + asset->releaseSourceData(); + + Log("Successfully loaded GLB."); + return new SceneAsset(asset, _engine, _ncm); +} + +void SceneAssetLoader::remove(SceneAsset *asset) { + _resourceLoader->evictResourceData(); + _scene->removeEntities(asset->_asset->getEntities(), + asset->_asset->getEntityCount()); + _assetLoader->destroyAsset(asset->_asset); +} +} // namespace polyvox \ No newline at end of file diff --git a/ios/src/SceneAssetLoader.hpp b/ios/src/SceneAssetLoader.hpp new file mode 100644 index 00000000..44b33c9c --- /dev/null +++ b/ios/src/SceneAssetLoader.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include +#include +#include + +#include "SceneResources.hpp" +#include "SceneAsset.hpp" + +namespace polyvox { + using namespace filament; + using namespace filament::gltfio; + using namespace utils; + + class SceneAssetLoader { + public: + SceneAssetLoader( + LoadResource loadResource, + FreeResource freeResource, + AssetLoader* assetLoader, + ResourceLoader* resourceLoader, + NameComponentManager* ncm, + Engine* engine, + Scene* scene); + SceneAsset* fromGltf(const char* uri, const char* relativeResourcePath); + SceneAsset* fromGlb(const char* uri); + void remove(SceneAsset* asset); + + private: + LoadResource _loadResource; + FreeResource _freeResource; + AssetLoader* _assetLoader; + ResourceLoader* _resourceLoader; + NameComponentManager* _ncm; + Scene* _scene; + Engine* _engine; + + + }; +} \ No newline at end of file diff --git a/ios/src/SceneResources.hpp b/ios/src/SceneResources.hpp new file mode 100644 index 00000000..f34bda9d --- /dev/null +++ b/ios/src/SceneResources.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include + +namespace polyvox { + + // + // Pairs a memory buffer with an ID that can be used to unload the backing asset if needed. + // Use this when you want to load an asset from a resource that requires more than just `free` on the underlying buffer. + // e.g. + // ``` + // uint64_t id = get_next_resource_id(); + // AAsset *asset = AAssetManager_open(am, name, AASSET_MODE_BUFFER); + // off_t length = AAsset_getLength(asset); + // const void * buffer = AAsset_getBuffer(asset); + // uint8_t *buf = new uint8_t[length ]; + // memcpy(buf,buffer, length); + // ResourceBuffer rb(buf, length, id); + // ... + // ... + // (elsewhere) + // AAsset* asset = get_asset_from_id(rb.id); + // AAsset_close(asset); + // free_asset_id(rb.id); + // + struct ResourceBuffer { + ResourceBuffer(const void* data, const uint32_t size, const uint32_t id) : data(data), size(size), id(id) {}; + + ResourceBuffer& operator=(ResourceBuffer other) + { + data = other.data; + size = other.size; + id = other.id; + return *this; + } + const void* data; + uint32_t size; + uint32_t id; + }; + + // + // Typedef for any function that loads a resource into a ResourceBuffer from an asset URI. + // + using LoadResource = std::function; + + // + // Typedef for any function that frees a ResourceBuffer. + // + using FreeResource = std::function; + + typedef std::chrono::time_point time_point_t; + + // + // Holds the current state of a bone animation embeded in a GLTF asset. + // + struct BoneAnimationStatus { + BoneAnimationStatus(int animationIndex, float duration, bool loop) : animationIndex(animationIndex), duration(duration), loop(loop) {} + bool hasStarted = false; + int animationIndex; + float duration = 0; + time_point_t lastTime; + bool loop; + }; + + // + // Holds the current state of a morph-target animation in a GLTF asset. + // + struct MorphAnimationStatus { + + MorphAnimationStatus(float* frameData, + int numWeights, + int numFrames, + float frameLength) : frameData(frameData), numWeights(numWeights), numFrames(numFrames), frameLengthInMs(frameLength) { + } + + int frameIndex = -1; + int numFrames; + float frameLengthInMs; + time_point_t startTime; + + float* frameData; + int numWeights; + }; +} \ No newline at end of file diff --git a/ios/src/streambuf.cpp b/ios/src/StreamBufferAdapter.cpp similarity index 65% rename from ios/src/streambuf.cpp rename to ios/src/StreamBufferAdapter.cpp index d2308b8f..76bfeb71 100644 --- a/ios/src/streambuf.cpp +++ b/ios/src/StreamBufferAdapter.cpp @@ -7,11 +7,11 @@ using namespace std; namespace polyvox { -class streambuf : public std::streambuf +class StreamBufferAdapter : public std::streambuf { public: - streambuf(const char *begin, const char *end); - ~streambuf() { + StreamBufferAdapter(const char *begin, const char *end); + ~StreamBufferAdapter() { } streamsize size(); @@ -26,16 +26,16 @@ class streambuf : public std::streambuf }; -streambuf::streambuf(const char *begin, const char *end) +StreamBufferAdapter::StreamBufferAdapter(const char *begin, const char *end) { setg((char*)begin, (char*)begin, (char*)end); } -streamsize streambuf::size() { +streamsize StreamBufferAdapter::size() { return egptr() - eback(); } -streambuf::int_type streambuf::underflow() +streambuf::int_type StreamBufferAdapter::underflow() { if (gptr() == egptr()) { return traits_type::eof(); @@ -43,7 +43,7 @@ streambuf::int_type streambuf::underflow() return *(gptr()); } -streambuf::int_type streambuf::uflow() +streambuf::int_type StreamBufferAdapter::uflow() { if (gptr() == egptr()) { return traits_type::eof(); @@ -53,7 +53,7 @@ streambuf::int_type streambuf::uflow() return *(gptr()); } -streambuf::int_type streambuf::pbackfail(int_type ch) +streambuf::int_type StreamBufferAdapter::pbackfail(int_type ch) { if (gptr() == eback() || (ch != traits_type::eof() && ch != gptr()[-1])) return traits_type::eof(); @@ -61,12 +61,12 @@ streambuf::int_type streambuf::pbackfail(int_type ch) return *(gptr()); } -streamsize streambuf::showmanyc() +streamsize StreamBufferAdapter::showmanyc() { return egptr() - gptr(); } -streampos streambuf::seekoff(streamoff off, ios_base::seekdir way, ios_base::openmode which = ios_base::in) { +streampos StreamBufferAdapter::seekoff(streamoff off, ios_base::seekdir way, ios_base::openmode which = ios_base::in) { if(way == ios_base::beg) { setg(eback(), eback()+off, egptr()); } else if(way == ios_base::cur) { @@ -77,7 +77,7 @@ streampos streambuf::seekoff(streamoff off, ios_base::seekdir way, ios_base::ope return gptr() - eback(); } -streampos streambuf::seekpos(streampos sp, ios_base::openmode which = ios_base::in) { +streampos StreamBufferAdapter::seekpos(streampos sp, ios_base::openmode which = ios_base::in) { return seekoff(sp - pos_type(off_type(0)), std::ios_base::beg, which); } } \ No newline at end of file diff --git a/ios/src/streambuf.hpp b/ios/src/StreamBufferAdapter.hpp similarity index 62% rename from ios/src/streambuf.hpp rename to ios/src/StreamBufferAdapter.hpp index 5a593a24..b9685fd1 100644 --- a/ios/src/streambuf.hpp +++ b/ios/src/StreamBufferAdapter.hpp @@ -5,11 +5,15 @@ namespace polyvox { - class streambuf : public std::streambuf + // + // A generic adapter to expose any contiguous section of memory as a std::streambuf. + // Mostly for Android/iOS assets which may not be able to be directly loaded as streams. + // + class StreamBufferAdapter : public std::streambuf { public: - streambuf(const char *begin, const char *end); - ~streambuf() { + StreamBufferAdapter(const char *begin, const char *end); + ~StreamBufferAdapter() { } streamsize size(); diff --git a/ios/src/imagematerial.S b/ios/src/image/imagematerial.S similarity index 100% rename from ios/src/imagematerial.S rename to ios/src/image/imagematerial.S diff --git a/ios/src/imagematerial.apple.S b/ios/src/image/imagematerial.apple.S similarity index 100% rename from ios/src/imagematerial.apple.S rename to ios/src/image/imagematerial.apple.S diff --git a/ios/src/imagematerial.bin b/ios/src/image/imagematerial.bin similarity index 100% rename from ios/src/imagematerial.bin rename to ios/src/image/imagematerial.bin diff --git a/ios/src/imagematerial.c b/ios/src/image/imagematerial.c similarity index 100% rename from ios/src/imagematerial.c rename to ios/src/image/imagematerial.c diff --git a/ios/src/imagematerial.h b/ios/src/image/imagematerial.h similarity index 100% rename from ios/src/imagematerial.h rename to ios/src/image/imagematerial.h diff --git a/ios/src/materials/image.filamat b/ios/src/image/materials/image.filamat similarity index 100% rename from ios/src/materials/image.filamat rename to ios/src/image/materials/image.filamat diff --git a/ios/src/materials/image.mat b/ios/src/image/materials/image.mat similarity index 100% rename from ios/src/materials/image.mat rename to ios/src/image/materials/image.mat diff --git a/lib/filament_controller.dart b/lib/filament_controller.dart index 8f30b18c..c395f585 100644 --- a/lib/filament_controller.dart +++ b/lib/filament_controller.dart @@ -2,6 +2,8 @@ import 'dart:async'; import 'package:flutter/services.dart'; +typedef FilamentAsset = int; + abstract class FilamentController { void onFilamentViewCreated(int id); Future setBackgroundImage(String path); @@ -9,22 +11,21 @@ abstract class FilamentController { Future removeSkybox(); Future loadIbl(String path); Future removeIbl(); - Future loadGlb(String path); - Future loadGltf(String path, String relativeResourcePath); + Future loadGlb(String path); + Future loadGltf(String path, String relativeResourcePath); Future panStart(double x, double y); Future panUpdate(double x, double y); Future panEnd(); Future rotateStart(double x, double y); Future rotateUpdate(double x, double y); Future rotateEnd(); - Future applyWeights(List weights); - Future> getTargetNames(String meshName); - Future> getAnimationNames(); - Future releaseSourceAssets(); - Future removeAsset(); - Future playAnimation(int index, {bool loop = false}); - Future stopAnimation(); - Future setCamera(String name); + Future applyWeights(FilamentAsset asset, List weights); + Future> getTargetNames(FilamentAsset asset, String meshName); + Future> getAnimationNames(FilamentAsset asset); + Future removeAsset(FilamentAsset asset); + Future playAnimation(FilamentAsset asset, int index, {bool loop = false}); + Future stopAnimation(FilamentAsset asset); + Future setCamera(FilamentAsset asset, String name); /// /// Set the weights of all morph targets in the mesh to the specified weights at successive frames (where each frame requires a duration of [frameLengthInMs]. @@ -32,9 +33,8 @@ abstract class FilamentController { /// Each frame is [numWeights] in length, where each entry is the weight to be applied to the morph target located at that index in the mesh primitive at that frame. /// In other words, weights is a contiguous sequence of floats of size W*F, where W is the number of weights and F is the number of frames /// - Future animate( + Future animate(FilamentAsset asset, List data, int numWeights, int numFrames, double frameLengthInMs); - Future createMorpher(String meshName, List primitives); Future zoom(double z); } @@ -86,15 +86,18 @@ class PolyvoxFilamentController extends FilamentController { await _channel.invokeMethod("removeIbl"); } - Future loadGlb(String path) async { + Future loadGlb(String path) async { print("Loading GLB at $path "); - await _channel.invokeMethod("loadGlb", path); + var asset = await _channel.invokeMethod("loadGlb", path); + print("Got asset : $asset "); + return asset as FilamentAsset; } - Future loadGltf(String path, String relativeResourcePath) async { + Future loadGltf(String path, String relativeResourcePath) async { print( "Loading GLTF at $path with relative resource path $relativeResourcePath"); - await _channel.invokeMethod("loadGltf", [path, relativeResourcePath]); + var asset = await _channel.invokeMethod("loadGltf", [path, relativeResourcePath]); + return asset as FilamentAsset; } Future panStart(double x, double y) async { @@ -121,53 +124,45 @@ class PolyvoxFilamentController extends FilamentController { await _channel.invokeMethod("rotateEnd"); } - Future applyWeights(List weights) async { - await _channel.invokeMethod("applyWeights", weights); + Future applyWeights(FilamentAsset asset, List weights) async { + await _channel.invokeMethod("applyWeights", [asset, weights]); } - Future> getTargetNames(String meshName) async { - var result = (await _channel.invokeMethod("getTargetNames", meshName)) + Future> getTargetNames(FilamentAsset asset, String meshName) async { + var result = (await _channel.invokeMethod("getTargetNames", [asset, meshName])) .cast(); return result; } - Future> getAnimationNames() async { + Future> getAnimationNames(FilamentAsset asset) async { var result = - (await _channel.invokeMethod("getAnimationNames")).cast(); + (await _channel.invokeMethod("getAnimationNames", asset)).cast(); return result; } - Future animate(List weights, int numWeights, int numFrames, + Future animate(FilamentAsset asset, List weights, int numWeights, int numFrames, double frameLengthInMs) async { await _channel.invokeMethod( - "animateWeights", [weights, numWeights, numFrames, frameLengthInMs]); + "animateWeights", [asset, weights, numWeights, numFrames, frameLengthInMs]); } - Future releaseSourceAssets() async { - await _channel.invokeMethod("releaseSourceAssets"); - } - - Future removeAsset() async { - await _channel.invokeMethod("removeAsset"); + Future removeAsset(FilamentAsset asset) async { + await _channel.invokeMethod("removeAsset", asset); } Future zoom(double z) async { await _channel.invokeMethod("zoom", z); } - Future createMorpher(String meshName, List primitives) async { - await _channel.invokeMethod("createMorpher", [meshName, primitives]); + Future playAnimation(FilamentAsset asset, int index, {bool loop = false}) async { + await _channel.invokeMethod("playAnimation", [asset, index, loop]); } - Future playAnimation(int index, {bool loop = false}) async { - await _channel.invokeMethod("playAnimation", [index, loop]); - } - - Future stopAnimation() async { + Future stopAnimation(FilamentAsset asset) async { await _channel.invokeMethod("stopAnimation"); } - Future setCamera(String name) async { - await _channel.invokeMethod("setCamera", name); + Future setCamera(FilamentAsset asset, String name) async { + await _channel.invokeMethod("setCamera", [asset, name]); } } diff --git a/pubspec.yaml b/pubspec.yaml index 141bacb6..f8ecb1f1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.0.1 homepage: environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.17.1 <3.0.0" flutter: ">=1.20.0" dependencies: