From 45576466c99f7a6607e1f0026767320dfc76d833 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 4 Oct 2023 14:49:48 +0800 Subject: [PATCH] allow passing uberarchive path to FilamentViewer and expose destroy_viewer --- example/.gitattributes | 4 + example/.gitignore | 2 + example/android/.gitignore | 2 + example/lib/main.dart | 493 +++++++++++--------- ios/include/AssetManager.hpp | 7 +- ios/include/FilamentViewer.hpp | 2 +- ios/include/PolyvoxFilamentApi.h | 2 +- ios/include/PolyvoxFilamentFFIApi.h | 2 +- ios/src/AssetManager.cpp | 25 +- ios/src/FilamentViewer.cpp | 6 +- ios/src/PolyvoxFilamentApi.cpp | 4 +- ios/src/PolyvoxFilamentFFIApi.cpp | 20 +- lib/filament_controller.dart | 12 + lib/filament_controller_ffi.dart | 16 +- lib/filament_controller_method_channel.dart | 10 + lib/generated_bindings.dart | 22 +- 16 files changed, 368 insertions(+), 261 deletions(-) create mode 100644 example/.gitattributes diff --git a/example/.gitattributes b/example/.gitattributes new file mode 100644 index 00000000..f1636a02 --- /dev/null +++ b/example/.gitattributes @@ -0,0 +1,4 @@ +assets/lit_opaque_43.uberz filter=lfs diff=lfs merge=lfs -text +assets/BusterDrone filter=lfs diff=lfs merge=lfs -text +assets/FlightHelmet filter=lfs diff=lfs merge=lfs -text +assets/lit_opaque_32.uberz filter=lfs diff=lfs merge=lfs -text diff --git a/example/.gitignore b/example/.gitignore index 0fa6b675..ecf07d50 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -44,3 +44,5 @@ app.*.map.json /android/app/debug /android/app/profile /android/app/release + +/android/.cxx/**/* diff --git a/example/android/.gitignore b/example/android/.gitignore index 6f568019..473d8bd3 100644 --- a/example/android/.gitignore +++ b/example/android/.gitignore @@ -6,6 +6,8 @@ gradle-wrapper.jar /local.properties GeneratedPluginRegistrant.java +.cxx/ + # Remember to never publicly share your keystore. # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app key.properties diff --git a/example/lib/main.dart b/example/lib/main.dart index e8f8e857..8820be7e 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -40,7 +40,7 @@ class ExampleWidget extends StatefulWidget { } class _ExampleWidgetState extends State { - final _filamentController = FilamentControllerFFI(); + FilamentControllerFFI? _filamentController; FilamentEntity? _shapes; FilamentEntity? _flightHelmet; @@ -54,7 +54,7 @@ class _ExampleWidgetState extends State { bool _rendering = false; int _framerate = 60; bool _postProcessing = true; - bool _initialized = false; + bool _active = false; bool _coneHidden = false; bool _frustumCulling = true; @@ -69,7 +69,11 @@ class _ExampleWidgetState extends State { Widget _item(void Function() onTap, String text) { return GestureDetector( - onTap: onTap, + onTap: () { + setState(() { + onTap(); + }); + }, child: Container( color: Colors.transparent, padding: EdgeInsets.symmetric(vertical: 10, horizontal: 10), @@ -78,213 +82,242 @@ class _ExampleWidgetState extends State { @override Widget build(BuildContext context) { - var children = [ - _item(() { - _filamentController.render(); - }, "render"), - _item(() { - setState(() { - _rendering = !_rendering; - _filamentController.setRendering(_rendering); - }); - }, "Rendering: $_rendering "), - _item(() { - setState(() { - _framerate = _framerate == 60 ? 30 : 60; - _filamentController.setFrameRate(_framerate); - }); - }, "$_framerate fps"), - _item(() { - _filamentController.setBackgroundColor(Color(0xFF73C9FA)); - }, "set background color"), - _item(() { - _filamentController.setBackgroundImage('assets/background.ktx'); - }, "load background image"), - _item(() { - _filamentController.setBackgroundImage('assets/background.ktx', - fillHeight: true); - }, "load background image (fill height)"), - _item(() { - _filamentController - .loadSkybox('assets/default_env/default_env_skybox.ktx'); - }, 'load skybox'), - _item(() { - _filamentController.loadIbl('assets/default_env/default_env_ibl.ktx'); - }, 'load IBL'), - _item(() { - setState(() { - _postProcessing = !_postProcessing; - }); - _filamentController.setPostProcessing(_postProcessing); - }, "${_postProcessing ? "Disable" : "Enable"} postprocessing"), - _item( - () { - _filamentController.removeSkybox(); - }, - 'remove skybox', - ), - _item(() async { - _shapes = await _filamentController.loadGlb('assets/shapes/shapes.glb'); - _animations = await _filamentController.getAnimationNames(_shapes!); - setState(() {}); - }, 'load shapes GLB'), - _item(() async { - _animations = await _filamentController.setCamera(_shapes!, null); - setState(() {}); - }, 'set camera to first camera in shapes GLB'), - _item(() async { - if (_coneHidden) { - _filamentController.reveal(_shapes!, "Cone"); - } else { - _filamentController.hide(_shapes!, "Cone"); - } - setState(() { - _coneHidden = !_coneHidden; - }); - }, _coneHidden ? 'show cone' : 'hide cone'), - _item(() async { - if (_shapes != null) { - _filamentController.removeAsset(_shapes!); - } - _shapes = await _filamentController.loadGltf( - 'assets/shapes/shapes.gltf', 'assets/shapes'); - }, 'load shapes GLTF'), - _item(() async { - _filamentController.transformToUnitCube(_shapes!); - }, 'transform to unit cube'), - _item(() async { - _filamentController.setPosition(_shapes!, 1.0, 1.0, -1.0); - }, 'set shapes position to 1, 1, -1'), - _item(() async { - _filamentController.setPosition(_shapes!, 1.0, 1.0, -1.0); - }, 'move camera to shapes position'), - _item(() async { - var frameData = Float32List.fromList( - List.generate(120, (i) => i / 120).expand((x) { - var vals = List.filled(7, x); - vals[3] = 1.0; - // vals[4] = 0; - vals[5] = 0; - vals[6] = 0; - return vals; - }).toList()); + var children = []; - _filamentController.setBoneAnimation( - _shapes!, - BoneAnimationData( - "Bone.001", ["Cube.001"], frameData, 1000.0 / 60.0)); - // , - // "Bone.001", - // "Cube.001", - // BoneTransform([Vec3(x: 0, y: 0.0, z: 0.0)], - // [Quaternion(x: 1, y: 1, z: 1, w: 1)])); - }, 'construct bone animation'), - _item(() async { - _filamentController.removeAsset(_shapes!); - _shapes = null; - }, 'remove shapes'), - _item(() async { - _filamentController.clearAssets(); - _shapes = null; - }, 'clear all assets'), - _item(() async { - var names = - await _filamentController.getMorphTargetNames(_shapes!, "Cylinder"); - await showDialog( - context: context, - builder: (ctx) { - return Container( - height: 100, - width: 100, - color: Colors.white, - child: Text(names.join(","))); - }); - }, "show morph target names for Cylinder"), - _item(() { - _filamentController.setMorphTargetWeights( - _shapes!, "Cylinder", List.filled(4, 1.0)); - }, "set Cylinder morph weights to 1"), - _item(() { - _filamentController.setMorphTargetWeights( - _shapes!, "Cylinder", List.filled(4, 0.0)); - }, "set Cylinder morph weights to 0.0"), - _item(() async { - var morphs = - await _filamentController.getMorphTargetNames(_shapes!, "Cylinder"); - final animation = AnimationBuilder( - availableMorphs: morphs, framerate: 30, meshName: "Cylinder") - .setDuration(4) - .setMorphTargets(["Key 1", "Key 2"]) - .interpolateMorphWeights(0, 4, 0, 1) - .build(); - _filamentController.setMorphAnimationData(_shapes!, animation); - }, "animate cylinder morph weights #1 and #2"), - _item(() async { - var morphs = - await _filamentController.getMorphTargetNames(_shapes!, "Cylinder"); - final animation = AnimationBuilder( - availableMorphs: morphs, framerate: 30, meshName: "Cylinder") - .setDuration(4) - .setMorphTargets(["Key 3", "Key 4"]) - .interpolateMorphWeights(0, 4, 0, 1) - .build(); - _filamentController.setMorphAnimationData(_shapes!, animation); - }, "animate cylinder morph weights #3 and #4"), - _item(() async { - var morphs = - await _filamentController.getMorphTargetNames(_shapes!, "Cube"); - final animation = AnimationBuilder( - availableMorphs: morphs, framerate: 30, meshName: "Cube") - .setDuration(4) - .setMorphTargets(["Key 1", "Key 2"]) - .interpolateMorphWeights(0, 4, 0, 1) - .build(); - _filamentController.setMorphAnimationData(_shapes!, animation); - }, "animate shapes morph weights #1 and #2"), - _item(() { - _filamentController.setMaterialColor( - _shapes!, "Cone", 0, Colors.purple); - }, "set cone material color to purple"), - _item(() { - _loop = !_loop; - setState(() {}); - }, "toggle animation looping ${_loop ? "OFF" : "ON"}") - ]; - if (_animations != null) { - children.addAll(_animations!.map((a) => _item(() { - _filamentController.playAnimation(_shapes!, _animations!.indexOf(a), - replaceActive: true, crossfade: 0.5, loop: _loop); - }, "play animation ${_animations!.indexOf(a)} (replace/fade)"))); - children.addAll(_animations!.map((a) => _item(() { - _filamentController.playAnimation(_shapes!, _animations!.indexOf(a), - replaceActive: false, loop: _loop); - }, "play animation ${_animations!.indexOf(a)} (noreplace)"))); + if (_filamentController == null) { + children.addAll([ + _item(() { + _filamentController = FilamentControllerFFI(); + }, "create viewer (default ubershader)"), + _item(() { + _filamentController = FilamentControllerFFI( + uberArchivePath: Platform.isWindows + ? "assets/lit_opaque_32.uberz" + : "assets/lit_opaque_43.uberz"); + }, "create viewer (custom ubershader - lit opaque only)"), + ]); + } else { + children.addAll([ + _item(() { + _filamentController!.destroy(); + _filamentController = null; + }, "destroy viewer/texture") + ]); } - children.add(_item(() { - _filamentController.setToneMapping(ToneMapper.LINEAR); - }, "Set tone mapping to linear")); + if (_filamentController != null) { + children.addAll([ + _item(() { + _filamentController!.render(); + }, "render"), + _item(() { + setState(() { + _rendering = !_rendering; + _filamentController!.setRendering(_rendering); + }); + }, "Rendering: $_rendering "), + _item(() { + setState(() { + _framerate = _framerate == 60 ? 30 : 60; + _filamentController!.setFrameRate(_framerate); + }); + }, "$_framerate fps"), + _item(() { + _filamentController!.setBackgroundColor(Color(0xFF73C9FA)); + }, "set background color"), + _item(() { + _filamentController!.setBackgroundImage('assets/background.ktx'); + }, "load background image"), + _item(() { + _filamentController! + .setBackgroundImage('assets/background.ktx', fillHeight: true); + }, "load background image (fill height)"), + _item(() { + _filamentController! + .loadSkybox('assets/default_env/default_env_skybox.ktx'); + }, 'load skybox'), + _item(() { + _filamentController! + .loadIbl('assets/default_env/default_env_ibl.ktx'); + }, 'load IBL'), + _item(() { + setState(() { + _postProcessing = !_postProcessing; + }); + _filamentController!.setPostProcessing(_postProcessing); + }, "${_postProcessing ? "Disable" : "Enable"} postprocessing"), + _item( + () { + _filamentController!.removeSkybox(); + }, + 'remove skybox', + ), + _item(() async { + _shapes = + await _filamentController!.loadGlb('assets/shapes/shapes.glb'); + _animations = await _filamentController!.getAnimationNames(_shapes!); + setState(() {}); + }, 'load shapes GLB'), + _item(() async { + _animations = await _filamentController!.setCamera(_shapes!, null); + setState(() {}); + }, 'set camera to first camera in shapes GLB'), + _item(() async { + if (_coneHidden) { + _filamentController!.reveal(_shapes!, "Cone"); + } else { + _filamentController!.hide(_shapes!, "Cone"); + } + setState(() { + _coneHidden = !_coneHidden; + }); + }, _coneHidden ? 'show cone' : 'hide cone'), + _item(() async { + if (_shapes != null) { + _filamentController!.removeAsset(_shapes!); + } + _shapes = await _filamentController! + .loadGltf('assets/shapes/shapes.gltf', 'assets/shapes'); + }, 'load shapes GLTF'), + _item(() async { + _filamentController!.transformToUnitCube(_shapes!); + }, 'transform to unit cube'), + _item(() async { + _filamentController!.setPosition(_shapes!, 1.0, 1.0, -1.0); + }, 'set shapes position to 1, 1, -1'), + _item(() async { + _filamentController!.setPosition(_shapes!, 1.0, 1.0, -1.0); + }, 'move camera to shapes position'), + _item(() async { + var frameData = Float32List.fromList( + List.generate(120, (i) => i / 120).expand((x) { + var vals = List.filled(7, x); + vals[3] = 1.0; + // vals[4] = 0; + vals[5] = 0; + vals[6] = 0; + return vals; + }).toList()); - children.add(_item(() { - _filamentController.moveCameraToAsset(_shapes!); - }, "Move camera to asset")); + _filamentController!.setBoneAnimation( + _shapes!, + BoneAnimationData( + "Bone.001", ["Cube.001"], frameData, 1000.0 / 60.0)); + // , + // "Bone.001", + // "Cube.001", + // BoneTransform([Vec3(x: 0, y: 0.0, z: 0.0)], + // [Quaternion(x: 1, y: 1, z: 1, w: 1)])); + }, 'construct bone animation'), + _item(() async { + _filamentController!.removeAsset(_shapes!); + _shapes = null; + }, 'remove shapes'), + _item(() async { + _filamentController!.clearAssets(); + _shapes = null; + }, 'clear all assets'), + _item(() async { + var names = await _filamentController! + .getMorphTargetNames(_shapes!, "Cylinder"); + await showDialog( + context: context, + builder: (ctx) { + return Container( + height: 100, + width: 100, + color: Colors.white, + child: Text(names.join(","))); + }); + }, "show morph target names for Cylinder"), + _item(() { + _filamentController! + .setMorphTargetWeights(_shapes!, "Cylinder", List.filled(4, 1.0)); + }, "set Cylinder morph weights to 1"), + _item(() { + _filamentController! + .setMorphTargetWeights(_shapes!, "Cylinder", List.filled(4, 0.0)); + }, "set Cylinder morph weights to 0.0"), + _item(() async { + var morphs = await _filamentController! + .getMorphTargetNames(_shapes!, "Cylinder"); + final animation = AnimationBuilder( + availableMorphs: morphs, framerate: 30, meshName: "Cylinder") + .setDuration(4) + .setMorphTargets(["Key 1", "Key 2"]) + .interpolateMorphWeights(0, 4, 0, 1) + .build(); + _filamentController!.setMorphAnimationData(_shapes!, animation); + }, "animate cylinder morph weights #1 and #2"), + _item(() async { + var morphs = await _filamentController! + .getMorphTargetNames(_shapes!, "Cylinder"); + final animation = AnimationBuilder( + availableMorphs: morphs, framerate: 30, meshName: "Cylinder") + .setDuration(4) + .setMorphTargets(["Key 3", "Key 4"]) + .interpolateMorphWeights(0, 4, 0, 1) + .build(); + _filamentController!.setMorphAnimationData(_shapes!, animation); + }, "animate cylinder morph weights #3 and #4"), + _item(() async { + var morphs = + await _filamentController!.getMorphTargetNames(_shapes!, "Cube"); + final animation = AnimationBuilder( + availableMorphs: morphs, framerate: 30, meshName: "Cube") + .setDuration(4) + .setMorphTargets(["Key 1", "Key 2"]) + .interpolateMorphWeights(0, 4, 0, 1) + .build(); + _filamentController!.setMorphAnimationData(_shapes!, animation); + }, "animate shapes morph weights #1 and #2"), + _item(() { + _filamentController! + .setMaterialColor(_shapes!, "Cone", 0, Colors.purple); + }, "set cone material color to purple"), + _item(() { + _loop = !_loop; + setState(() {}); + }, "toggle animation looping ${_loop ? "OFF" : "ON"}") + ]); + if (_animations != null) { + children.addAll(_animations!.map((a) => _item(() { + _filamentController!.playAnimation( + _shapes!, _animations!.indexOf(a), + replaceActive: true, crossfade: 0.5, loop: _loop); + }, "play animation ${_animations!.indexOf(a)} (replace/fade)"))); + children.addAll(_animations!.map((a) => _item(() { + _filamentController!.playAnimation( + _shapes!, _animations!.indexOf(a), + replaceActive: false, loop: _loop); + }, "play animation ${_animations!.indexOf(a)} (noreplace)"))); + } - children.add(_item(() { - setState(() { - _frustumCulling = !_frustumCulling; - }); + children.add(_item(() { + _filamentController!.setToneMapping(ToneMapper.LINEAR); + }, "Set tone mapping to linear")); - _filamentController.setViewFrustumCulling(_frustumCulling); - }, "${_frustumCulling ? "Disable" : "Enable"} frustum culling")); + children.add(_item(() { + _filamentController!.moveCameraToAsset(_shapes!); + }, "Move camera to asset")); + children.add(_item(() { + setState(() { + _frustumCulling = !_frustumCulling; + }); + _filamentController!.setViewFrustumCulling(_frustumCulling); + }, "${_frustumCulling ? "Disable" : "Enable"} frustum culling")); + } return Stack(children: [ - Positioned.fill( - child: FilamentGestureDetector( - showControlOverlay: true, - controller: _filamentController, - child: FilamentWidget( - controller: _filamentController, - ))), + _filamentController != null + ? Positioned.fill( + child: FilamentGestureDetector( + showControlOverlay: true, + controller: _filamentController!, + child: FilamentWidget( + controller: _filamentController!, + ))) + : Container(), Positioned( bottom: 0, left: 0, @@ -325,7 +358,7 @@ class _ExampleWidgetState extends State { // _item(30 () async { 'remove light'), // _item(31 () async { 'clear all lights'), // _item(32 () async { 'set camera model matrix'), - )))), + )))) ]); } } @@ -334,26 +367,26 @@ class _ExampleWidgetState extends State { // break; // case -2: -// _filamentController.render(); +// _filamentController!.render(); // break; // case -4: // setState(() { // _rendering = !_rendering; -// _filamentController.setRendering(_rendering); +// _filamentController!.setRendering(_rendering); // }); // break; // case -5: // setState(() { // _framerate = _framerate == 60 ? 30 : 60; -// _filamentController.setFrameRate(_framerate); +// _filamentController!.setFrameRate(_framerate); // }); // break; // case -6: -// _filamentController.setBackgroundColor(Color(0xFF73C9FA)); +// _filamentController!.setBackgroundColor(Color(0xFF73C9FA)); // break; // case 5: -// _flightHelmet ??= await _filamentController.loadGltf( +// _flightHelmet ??= await _filamentController!.loadGltf( // 'assets/FlightHelmet/FlightHelmet.gltf', 'assets/FlightHelmet'); // break; @@ -363,14 +396,14 @@ class _ExampleWidgetState extends State { // }); // break; // case 14: -// _filamentController.setCamera(_shapes!, "Camera_Orientation"); +// _filamentController!.setCamera(_shapes!, "Camera_Orientation"); // break; // case 15: // break; // case 17: // var animationNames = -// await _filamentController.getAnimationNames(_shapes!); +// await _filamentController!.getAnimationNames(_shapes!); // await showDialog( // context: context, @@ -384,17 +417,17 @@ class _ExampleWidgetState extends State { // break; // case 18: -// _filamentController.panStart(1, 1); -// _filamentController.panUpdate(1, 2); -// _filamentController.panEnd(); +// _filamentController!.panStart(1, 1); +// _filamentController!.panUpdate(1, 2); +// _filamentController!.panEnd(); // break; // case 19: -// _filamentController.panStart(1, 1); -// _filamentController.panUpdate(0, 0); -// _filamentController.panEnd(); +// _filamentController!.panStart(1, 1); +// _filamentController!.panUpdate(0, 0); +// _filamentController!.panEnd(); // break; // case 20: -// _filamentController.clearAssets(); +// _filamentController!.clearAssets(); // break; // case 21: // break; @@ -403,7 +436,7 @@ class _ExampleWidgetState extends State { // case 23: // break; // case 24: -// _filamentController.setRotation(_shapes!, pi / 2, 0.0, 1.0, 0.0); +// _filamentController!.setRotation(_shapes!, pi / 2, 0.0, 1.0, 0.0); // break; // case 25: // setState(() { @@ -411,28 +444,28 @@ class _ExampleWidgetState extends State { // }); // break; // case 26: -// _filamentController.setCameraPosition(0, 0, 3); -// _filamentController.setCameraRotation(0, 0, 1, 0); +// _filamentController!.setCameraPosition(0, 0, 3); +// _filamentController!.setCameraRotation(0, 0, 1, 0); // break; // case 27: // _framerate = _framerate == 60 ? 30 : 60; -// _filamentController.setFrameRate(_framerate); +// _filamentController!.setFrameRate(_framerate); // break; // case 28: -// _filamentController.setBackgroundImagePosition(25, 25); +// _filamentController!.setBackgroundImagePosition(25, 25); // break; // case 29: -// _light = await _filamentController.addLight( +// _light = await _filamentController!.addLight( // 1, 6500, 15000000, 0, 1, 0, 0, -1, 0, true); // break; // case 30: // if (_light != null) { -// _filamentController.removeLight(_light!); +// _filamentController!.removeLight(_light!); // _light = null; // } // break; // case 31: -// _filamentController.clearLights(); +// _filamentController!.clearLights(); // break; // case 32: @@ -443,8 +476,8 @@ class _ExampleWidgetState extends State { // break; // case 34: // var duration = -// await _filamentController.getAnimationDuration(_shapes!, 0); -// _filamentController.playAnimation(_shapes!, 0, +// await _filamentController!.getAnimationDuration(_shapes!, 0); +// _filamentController!.playAnimation(_shapes!, 0, // loop: false, crossfade: 0.5); // await Future.delayed( // Duration(milliseconds: (duration * 1000.0).toInt())); @@ -460,13 +493,13 @@ class _ExampleWidgetState extends State { // // }); // break; // case 35: -// _filamentController.playAnimation(_shapes!, 1, +// _filamentController!.playAnimation(_shapes!, 1, // loop: false, crossfade: 0.5); // break; // case 36: -// _filamentController.playAnimation(_shapes!, 2, +// _filamentController!.playAnimation(_shapes!, 2, // loop: false, crossfade: 0.5); // break; // case 37: -// _filamentController.stopAnimation(_shapes!, 0); +// _filamentController!.stopAnimation(_shapes!, 0); // break; diff --git a/ios/include/AssetManager.hpp b/ios/include/AssetManager.hpp index 4bcf7ff2..175724fc 100644 --- a/ios/include/AssetManager.hpp +++ b/ios/include/AssetManager.hpp @@ -18,9 +18,10 @@ namespace polyvox { class AssetManager { public: AssetManager(const ResourceLoaderWrapper* const loader, - NameComponentManager *ncm, - Engine *engine, - Scene *scene); + NameComponentManager* ncm, + Engine* engine, + Scene* scene, + const char* uberArchivePath); ~AssetManager(); EntityId loadGltf(const char* uri, const char* relativeResourcePath); EntityId loadGlb(const char* uri, bool unlit); diff --git a/ios/include/FilamentViewer.hpp b/ios/include/FilamentViewer.hpp index 6cb5e767..b3ec2c04 100644 --- a/ios/include/FilamentViewer.hpp +++ b/ios/include/FilamentViewer.hpp @@ -49,7 +49,7 @@ namespace polyvox { class FilamentViewer { public: - FilamentViewer(const void* context, const ResourceLoaderWrapper* const resourceLoaderWrapper, void* const platform=nullptr); + FilamentViewer(const void* context, const ResourceLoaderWrapper* const resourceLoaderWrapper, void* const platform=nullptr, const char* uberArchivePath=nullptr); ~FilamentViewer(); void setToneMapping(ToneMapping toneMapping); diff --git a/ios/include/PolyvoxFilamentApi.h b/ios/include/PolyvoxFilamentApi.h index deac041a..3c10989a 100644 --- a/ios/include/PolyvoxFilamentApi.h +++ b/ios/include/PolyvoxFilamentApi.h @@ -53,7 +53,7 @@ extern "C" { #endif typedef int32_t EntityId; -FLUTTER_PLUGIN_EXPORT const void* create_filament_viewer(const void* const context, const ResourceLoaderWrapper* const loader, void* const platform); +FLUTTER_PLUGIN_EXPORT const void* create_filament_viewer(const void* const context, const ResourceLoaderWrapper* const loader, void* const platform, const char* uberArchivePath); FLUTTER_PLUGIN_EXPORT void destroy_filament_viewer(const void* const viewer); FLUTTER_PLUGIN_EXPORT ResourceLoaderWrapper* make_resource_loader(LoadFilamentResourceFromOwner loadFn, FreeFilamentResourceFromOwner freeFn, void* owner); FLUTTER_PLUGIN_EXPORT void* get_asset_manager(const void* const viewer); diff --git a/ios/include/PolyvoxFilamentFFIApi.h b/ios/include/PolyvoxFilamentFFIApi.h index e78b6771..5d35b0bb 100644 --- a/ios/include/PolyvoxFilamentFFIApi.h +++ b/ios/include/PolyvoxFilamentFFIApi.h @@ -15,7 +15,7 @@ extern "C" { typedef int32_t EntityId; typedef void (*FilamentRenderCallback)(void* const owner); -FLUTTER_PLUGIN_EXPORT void* const create_filament_viewer_ffi(void* const context, void* const platform, const ResourceLoaderWrapper* const loader, void (*renderCallback)(void* const renderCallbackOwner), void* const renderCallbackOwner); +FLUTTER_PLUGIN_EXPORT void* const create_filament_viewer_ffi(void* const context, void* const platform, const char* uberArchivePath, const ResourceLoaderWrapper* const loader, void (*renderCallback)(void* const renderCallbackOwner), void* const renderCallbackOwner); FLUTTER_PLUGIN_EXPORT void create_swap_chain_ffi(void* const viewer, void* const surface, uint32_t width, uint32_t height); FLUTTER_PLUGIN_EXPORT void create_render_target_ffi(void* const viewer, uint32_t nativeTextureId, uint32_t width, uint32_t height); FLUTTER_PLUGIN_EXPORT void destroy_filament_viewer_ffi(void* const viewer); diff --git a/ios/src/AssetManager.cpp b/ios/src/AssetManager.cpp index d90b6c0e..290333d2 100644 --- a/ios/src/AssetManager.cpp +++ b/ios/src/AssetManager.cpp @@ -38,9 +38,10 @@ using namespace filament; using namespace filament::gltfio; AssetManager::AssetManager(const ResourceLoaderWrapper* const resourceLoaderWrapper, - NameComponentManager *ncm, - Engine *engine, - Scene *scene) + NameComponentManager* ncm, + Engine* engine, + Scene* scene, + const char* uberArchivePath) : _resourceLoaderWrapper(resourceLoaderWrapper), _ncm(ncm), _engine(engine), @@ -52,15 +53,17 @@ _scene(scene) { _gltfResourceLoader = new ResourceLoader({.engine = _engine, .normalizeSkinningWeights = true }); - // TODO - allow passing uberz archive - // e.g. auto uberArchivePath = "packages/polyvox_filament/assets/default.uberz" - // auto uberdata = resourceLoaderWrapper->load(uberArchivePath); - // if (!uberdata.data) { - // Log("Failed to load ubershader material. This is fatal."); - // } - // _ubershaderProvider = gltfio::createUbershaderProvider(_engine, uberdata.data, uberdata.size); - _ubershaderProvider = gltfio::createUbershaderProvider( + if(uberArchivePath) { + auto uberdata = resourceLoaderWrapper->load(uberArchivePath); + if (!uberdata.data) { + Log("Failed to load ubershader material. This is fatal."); + } + _ubershaderProvider = gltfio::createUbershaderProvider(_engine, uberdata.data, uberdata.size); + resourceLoaderWrapper->free(uberdata); + } else { + _ubershaderProvider = gltfio::createUbershaderProvider( _engine, UBERARCHIVE_DEFAULT_DATA, UBERARCHIVE_DEFAULT_SIZE); + } Log("Created ubershader provider."); EntityManager &em = EntityManager::get(); diff --git a/ios/src/FilamentViewer.cpp b/ios/src/FilamentViewer.cpp index 85651be8..b4950f85 100644 --- a/ios/src/FilamentViewer.cpp +++ b/ios/src/FilamentViewer.cpp @@ -111,7 +111,7 @@ static constexpr float4 sFullScreenTriangleVertices[3] = { static const uint16_t sFullScreenTriangleIndices[3] = {0, 1, 2}; -FilamentViewer::FilamentViewer(const void* sharedContext, const ResourceLoaderWrapper* const resourceLoaderWrapper, void* const platform) +FilamentViewer::FilamentViewer(const void* sharedContext, const ResourceLoaderWrapper* const resourceLoaderWrapper, void* const platform, const char* uberArchivePath) : _resourceLoaderWrapper(resourceLoaderWrapper) { #if TARGET_OS_IPHONE @@ -190,7 +190,9 @@ FilamentViewer::FilamentViewer(const void* sharedContext, const ResourceLoaderWr _resourceLoaderWrapper, _ncm, _engine, - _scene); + _scene, + uberArchivePath + ); _imageTexture = Texture::Builder() .width(1) diff --git a/ios/src/PolyvoxFilamentApi.cpp b/ios/src/PolyvoxFilamentApi.cpp index d0675696..2e763700 100644 --- a/ios/src/PolyvoxFilamentApi.cpp +++ b/ios/src/PolyvoxFilamentApi.cpp @@ -15,8 +15,8 @@ extern "C" { #include "PolyvoxFilamentApi.h" - FLUTTER_PLUGIN_EXPORT const void* create_filament_viewer(const void* context, const ResourceLoaderWrapper* const loader, void* const platform) { - return (const void*) new FilamentViewer(context, loader, platform); + FLUTTER_PLUGIN_EXPORT const void* create_filament_viewer(const void* context, const ResourceLoaderWrapper* const loader, void* const platform, const char* uberArchivePath) { + return (const void*) new FilamentViewer(context, loader, platform, uberArchivePath); } FLUTTER_PLUGIN_EXPORT ResourceLoaderWrapper* make_resource_loader(LoadFilamentResourceFromOwner loadFn, FreeFilamentResourceFromOwner freeFn, void* const owner) { diff --git a/ios/src/PolyvoxFilamentFFIApi.cpp b/ios/src/PolyvoxFilamentFFIApi.cpp index e5ce2420..cf4ef280 100644 --- a/ios/src/PolyvoxFilamentFFIApi.cpp +++ b/ios/src/PolyvoxFilamentFFIApi.cpp @@ -44,6 +44,7 @@ public: void* const createViewer( void* const context, void* const platform, + const char* uberArchivePath, const ResourceLoaderWrapper* const loader, void (*renderCallback)(void*), void* const owner ) { @@ -51,7 +52,7 @@ public: _renderCallbackOwner = owner; std::packaged_task lambda([&]() mutable { - return new FilamentViewer(context, loader, platform); + return new FilamentViewer(context, loader, platform, uberArchivePath); }); auto fut = add_task(lambda); fut.wait(); @@ -59,6 +60,16 @@ public: return (void* const)_viewer; } + void destroyViewer() { + std::packaged_task lambda([&]() mutable { + _rendering = false; + destroy_filament_viewer(_viewer); + _viewer = nullptr; + }); + auto fut = add_task(lambda); + fut.wait(); + } + void setRendering(bool rendering) { _rendering = rendering; @@ -106,6 +117,7 @@ extern "C" FLUTTER_PLUGIN_EXPORT void* const create_filament_viewer_ffi( void* const context, void* const platform, + const char* uberArchivePath, const ResourceLoaderWrapper* const loader, void (*renderCallback)(void* const renderCallbackOwner), void* const renderCallbackOwner) { @@ -113,7 +125,11 @@ extern "C" { _rl = new RenderLoop(); } - return _rl->createViewer(context, platform, loader, renderCallback, renderCallbackOwner); + return _rl->createViewer(context, platform,uberArchivePath, loader, renderCallback, renderCallbackOwner); + } + + FLUTTER_PLUGIN_EXPORT void destroy_filament_viewer_ffi(void* const viewer) { + _rl->destroyViewer(); } FLUTTER_PLUGIN_EXPORT void create_swap_chain_ffi(void* const viewer, void* const surface, uint32_t width, uint32_t height) diff --git a/lib/filament_controller.dart b/lib/filament_controller.dart index d720929f..4ade9cb8 100644 --- a/lib/filament_controller.dart +++ b/lib/filament_controller.dart @@ -22,7 +22,19 @@ abstract class FilamentController { Future render(); Future setFrameRate(int framerate); void setPixelRatio(double ratio); + + /// + /// Destroys the viewer and all backing textures. You can leave the FilamentWidget in the hierarchy after this is called, but + Future destroy(); + + /// + /// Destroys the viewer only, leaving the texture intact. You probably want to call [destroy] instead of this; [destroyViewer] is exposed mostly for lifecycle changes which are handled by FilamentWidget. + /// Future destroyViewer(); + + /// + /// Destroys the backing texture. You probably want to call [destroy] instead of this; this is exposed mostly for lifecycle changes which are handled by FilamentWidget. + /// Future destroyTexture(); /// diff --git a/lib/filament_controller_ffi.dart b/lib/filament_controller_ffi.dart index 32cb7c4b..cd45c334 100644 --- a/lib/filament_controller_ffi.dart +++ b/lib/filament_controller_ffi.dart @@ -30,11 +30,13 @@ class FilamentControllerFFI extends FilamentController { bool _resizing = false; + final String? uberArchivePath; + /// /// This controller uses platform channels to bridge Dart with the C/C++ code for the Filament API. /// Setting up the context/texture (since this is platform-specific) and the render ticker are platform-specific; all other methods are passed through by the platform channel to the methods specified in PolyvoxFilamentApi.h. /// - FilamentControllerFFI() { + FilamentControllerFFI({this.uberArchivePath}) { _channel.setMethodCallHandler((call) async { throw Exception("Unknown method channel invocation ${call.method}"); }); @@ -74,14 +76,23 @@ class FilamentControllerFFI extends FilamentController { print("Set pixel ratio to $ratio"); } + @override + Future destroy() async { + await destroyViewer(); + await destroyTexture(); + } + @override Future destroyViewer() async { if (_viewer == null || _resizing) { throw Exception("No viewer available, ignoring"); } + var viewer = _viewer; + _viewer = null; + _assetManager = null; - _lib.destroy_filament_viewer_ffi(_viewer!); + _lib.destroy_filament_viewer_ffi(viewer!); _isReadyForScene = Completer(); } @@ -139,6 +150,7 @@ class FilamentControllerFFI extends FilamentController { _viewer = _lib.create_filament_viewer_ffi( Pointer.fromAddress(sharedContext ?? 0), driver, + uberArchivePath?.toNativeUtf8().cast() ?? nullptr, Pointer.fromAddress(loader), renderCallback, renderCallbackOwner); diff --git a/lib/filament_controller_method_channel.dart b/lib/filament_controller_method_channel.dart index 4c41e543..dcc56c16 100644 --- a/lib/filament_controller_method_channel.dart +++ b/lib/filament_controller_method_channel.dart @@ -9,6 +9,10 @@ import 'filament_controller.dart'; typedef AssetManager = int; +/// +/// This is a previous iteration of FilamentController that used platform channels for every distinct platform. +/// This is no longer used; currently kept only for reference/posterity. +/// class FilamentControllerMethodChannel extends FilamentController { late MethodChannel _channel = MethodChannel("app.polyvox.filament/event"); @@ -636,4 +640,10 @@ class FilamentControllerMethodChannel extends FilamentController { throw Exception("Failed to reveal mesh $meshName"); } } + + @override + Future destroy() { + // TODO: implement destroy + throw UnimplementedError(); + } } diff --git a/lib/generated_bindings.dart b/lib/generated_bindings.dart index e222284e..319b039d 100644 --- a/lib/generated_bindings.dart +++ b/lib/generated_bindings.dart @@ -23,11 +23,13 @@ class NativeLibrary { ffi.Pointer context, ffi.Pointer loader, ffi.Pointer platform, + ffi.Pointer uberArchivePath, ) { return _create_filament_viewer( context, loader, platform, + uberArchivePath, ); } @@ -36,10 +38,14 @@ class NativeLibrary { ffi.Pointer Function( ffi.Pointer, ffi.Pointer, - ffi.Pointer)>>('create_filament_viewer'); + ffi.Pointer, + ffi.Pointer)>>('create_filament_viewer'); late final _create_filament_viewer = _create_filament_viewerPtr.asFunction< - ffi.Pointer Function(ffi.Pointer, - ffi.Pointer, ffi.Pointer)>(); + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer)>(); void destroy_filament_viewer( ffi.Pointer viewer, @@ -1326,6 +1332,7 @@ class NativeLibrary { ffi.Pointer create_filament_viewer_ffi( ffi.Pointer context, ffi.Pointer platform, + ffi.Pointer uberArchivePath, ffi.Pointer loader, ffi.Pointer< ffi.NativeFunction< @@ -1336,6 +1343,7 @@ class NativeLibrary { return _create_filament_viewer_ffi( context, platform, + uberArchivePath, loader, renderCallback, renderCallbackOwner, @@ -1347,6 +1355,7 @@ class NativeLibrary { ffi.Pointer Function( ffi.Pointer, ffi.Pointer, + ffi.Pointer, ffi.Pointer, ffi.Pointer< ffi.NativeFunction< @@ -1358,6 +1367,7 @@ class NativeLibrary { ffi.Pointer Function( ffi.Pointer, ffi.Pointer, + ffi.Pointer, ffi.Pointer, ffi.Pointer< ffi.NativeFunction< @@ -2146,7 +2156,7 @@ class NativeLibrary { _get_morph_target_name_count_ffiPtr.asFunction< int Function(ffi.Pointer, int, ffi.Pointer)>(); - int set_post_processing_ffi( + void set_post_processing_ffi( ffi.Pointer viewer, bool enabled, ) { @@ -2158,10 +2168,10 @@ class NativeLibrary { late final _set_post_processing_ffiPtr = _lookup< ffi - .NativeFunction, ffi.Bool)>>( + .NativeFunction, ffi.Bool)>>( 'set_post_processing_ffi'); late final _set_post_processing_ffi = _set_post_processing_ffiPtr - .asFunction, bool)>(); + .asFunction, bool)>(); void ios_dummy_ffi() { return _ios_dummy_ffi();