From 2553d854e92ec6c5ccd63300d885ed723e04f3b2 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Tue, 17 Oct 2023 08:57:00 +0800 Subject: [PATCH] replace isReadyForScene with hasViewer stream and update version number/CHANGELOG --- CHANGELOG.md | 5 ++- example/lib/main.dart | 9 +++-- lib/filament_controller.dart | 25 ++++++++---- lib/filament_controller_ffi.dart | 45 +++++++++------------ lib/filament_controller_method_channel.dart | 12 ++++-- pubspec.yaml | 2 +- 6 files changed, 55 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41cc7d81..f78c012c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ -## 0.0.1 +## 0.5.0 -* TODO: Describe initial release. +* Replaced `isReadyForScene` Future in `FilamentController` with the `Stream` `hasViewer`. +* Rendering is set to false when the app is hidden, inactive or paused; on resume, this will be set to the value it held prior to being hidden/inactive/paused. diff --git a/example/lib/main.dart b/example/lib/main.dart index f7ebcbcc..37c7429f 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -42,7 +42,6 @@ class ExampleWidget extends StatefulWidget { class _ExampleWidgetState extends State { FilamentController? _filamentController; - FilamentEntity? _shapes; FilamentEntity? _flightHelmet; List? _animations; @@ -65,10 +64,13 @@ class _ExampleWidgetState extends State { bool _coneHidden = false; bool _frustumCulling = true; + StreamSubscription? _hasViewerListener; + @override void dispose() { super.dispose(); _pickResultListener?.cancel(); + _hasViewerListener?.cancel(); } Widget _item(void Function() onTap, String text) { @@ -92,9 +94,10 @@ class _ExampleWidgetState extends State { picked = _filamentController!.getNameForEntity(entityId!); }); }); - _filamentController!.isReadyForScene.then((readyForScene) { + _hasViewerListener = + _filamentController!.hasViewer.listen((bool hasViewer) { setState(() { - _readyForScene = readyForScene; + _readyForScene = hasViewer; }); }); } diff --git a/lib/filament_controller.dart b/lib/filament_controller.dart index 7d462327..7861eb3d 100644 --- a/lib/filament_controller.dart +++ b/lib/filament_controller.dart @@ -8,20 +8,32 @@ typedef FilamentEntity = int; enum ToneMapper { ACES, FILMIC, LINEAR } -class TextureDetails { +class TextureDetails { final int textureId; final int width; final int height; - TextureDetails({required this.textureId, required this.width, required this.height}); + TextureDetails( + {required this.textureId, required this.width, required this.height}); } abstract class FilamentController { - - Future get isReadyForScene; - + /// + /// The Flutter texture ID and dimensions for current texture in use. + /// This is only used by [FilamentWidget]; you shouldn't need to access directly yourself. + /// TextureDetails? get textureDetails; + /// + /// A stream to indicate whether a FilamentViewer is available. + /// [FilamentWidget] will (asynchronously) create a [FilamentViewer] after being inserted into the widget hierarchy; + /// listen to this stream beforehand to perform any work necessary once the viewer is available. + /// [FilamentWidget] may also destroy/recreate the viewer on certain lifecycle events (e.g. backgrounding a mobile app); + /// listen for any corresponding [false]/[true] events to perform related work. + /// Note this is not a broadcast stream; only one listener can be registered and events will be buffered. + /// + Stream get hasViewer; + /// /// The result(s) of calling [pick] (see below). /// This may be a broadcast stream, so you should ensure you have subscribed to this stream before calling [pick]. @@ -66,7 +78,6 @@ abstract class FilamentController { /// Future destroyViewer(); - /// /// Destroys the specified backing texture. You probably want to call [destroy] instead of this; this is exposed mostly for lifecycle changes which are handled by FilamentWidget. /// @@ -82,7 +93,7 @@ abstract class FilamentController { /// This will dispatch a request to the native platform to create a hardware texture (Metal on iOS, OpenGL on Linux, GLES on Android and Windows) and a FilamentViewer (the main interface for manipulating the 3D scene) . /// 4) The FilamentController will notify FilamentWidget that a texture is available /// 5) The FilamentWidget will replace the empty Container with a Texture widget - /// If you need to wait until a FilamentViewer has been created, [await] the [isReadyForScene] Future. + /// If you need to wait until a FilamentViewer has been created, listen to the [viewer] stream. /// void createViewer(int width, int height); diff --git a/lib/filament_controller_ffi.dart b/lib/filament_controller_ffi.dart index 9cf6dbe1..8d4f554d 100644 --- a/lib/filament_controller_ffi.dart +++ b/lib/filament_controller_ffi.dart @@ -16,18 +16,17 @@ class FilamentControllerFFI extends FilamentController { double _pixelRatio = 1.0; - Completer _isReadyForScene = Completer(); - Future get isReadyForScene => _isReadyForScene.future; - late Pointer? _assetManager; late NativeLibrary _lib; Pointer? _viewer; - final String? uberArchivePath; + Stream get hasViewer => _hasViewerController.stream; + final _hasViewerController = StreamController(); + Stream get pickResult => _pickResultController.stream; final _pickResultController = StreamController.broadcast(); @@ -99,12 +98,12 @@ class FilamentControllerFFI extends FilamentController { _assetManager = null; _lib.destroy_filament_viewer_ffi(viewer!); - _isReadyForScene = Completer(); + _hasViewerController.add(false); } @override Future destroyTexture() async { - if(textureDetails != null) { + if (textureDetails != null) { await _channel.invokeMethod("destroyTexture", textureDetails!.textureId); } print("Texture destroyed"); @@ -118,14 +117,11 @@ class FilamentControllerFFI extends FilamentController { throw Exception( "Viewer already exists, make sure you call destroyViewer first"); } - if(textureDetails != null) { + if (textureDetails != null) { throw Exception( "Texture already exists, make sure you call destroyTexture first"); } - if (_isReadyForScene.isCompleted) { - throw Exception( - "Do not call createViewer when a viewer has already been created without calling destroyViewer"); - } + var loader = Pointer.fromAddress( await _channel.invokeMethod("getResourceLoaderWrapper")); if (loader == nullptr) { @@ -140,7 +136,6 @@ class FilamentControllerFFI extends FilamentController { await _channel.invokeMethod("createTexture", [size.width, size.height]); var flutterTextureId = textures[0]; - // void* on iOS (pointer to pixel buffer), void* on Android (pointer to native window), null on Windows/macOS var surfaceAddress = textures[1] as int? ?? 0; @@ -192,8 +187,9 @@ class FilamentControllerFFI extends FilamentController { _assetManager = _lib.get_asset_manager(_viewer!); - _isReadyForScene.complete(true); - textureDetails = TextureDetails(textureId: flutterTextureId!, width: width, height: height); + textureDetails = TextureDetails( + textureId: flutterTextureId!, width: width, height: height); + _hasViewerController.add(true); } /// @@ -259,23 +255,22 @@ class FilamentControllerFFI extends FilamentController { /// # Given we don't do this on other platforms, I'm OK to stick with the existing solution for the time being. /// ############################################################################ /// - + @override Future resize(int width, int height, {double scaleFactor = 1.0}) async { - - // we defer to the FilamentWidget to ensure that every call to [resize] is synchronized + // we defer to the FilamentWidget to ensure that every call to [resize] is synchronized // so this exception should never be thrown (right?) - if(textureDetails == null) { + if (textureDetails == null) { throw Exception("Resize currently underway, ignoring"); } var _textureDetails = textureDetails; textureDetails = null; - + _lib.set_rendering_ffi(_viewer!, false); - if(_textureDetails != null) { + if (_textureDetails != null) { if (_viewer != null) { _lib.destroy_swap_chain_ffi(_viewer!); } @@ -288,7 +283,6 @@ class FilamentControllerFFI extends FilamentController { print("Size after pixel ratio : $width x $height "); - var textures = await _channel .invokeMethod("createTexture", [newSize.width, newSize.height]); @@ -306,19 +300,18 @@ class FilamentControllerFFI extends FilamentController { if (nativeTexture != 0) { assert(surfaceAddress == 0); print("Creating render target from native texture $nativeTexture"); - _lib.create_render_target_ffi( - _viewer!, nativeTexture, newSize.width.toInt(), newSize.height.toInt()); + _lib.create_render_target_ffi(_viewer!, nativeTexture, + newSize.width.toInt(), newSize.height.toInt()); } _lib.update_viewport_and_camera_projection_ffi( _viewer!, newSize.width.toInt(), newSize.height.toInt(), 1.0); await setRendering(_rendering); - textureDetails = TextureDetails(textureId: textures[0]!, width: width, height: height); + textureDetails = + TextureDetails(textureId: textures[0]!, width: width, height: height); } - - @override Future clearBackgroundImage() async { if (_viewer == null) { diff --git a/lib/filament_controller_method_channel.dart b/lib/filament_controller_method_channel.dart index d5d1a7ff..993f2212 100644 --- a/lib/filament_controller_method_channel.dart +++ b/lib/filament_controller_method_channel.dart @@ -120,8 +120,8 @@ class FilamentControllerMethodChannel extends FilamentController { bool _resizing = false; - - Future resize(int width, int height, {double scaleFactor = 1.0}) async { + Future resize(int width, int height, + {double scaleFactor = 1.0}) async { throw Exception(); _resizing = true; _textureId = await _channel.invokeMethod( @@ -670,12 +670,16 @@ class FilamentControllerMethodChannel extends FilamentController { // TODO: implement getNameForEntity throw UnimplementedError(); } - + @override // TODO: implement textureDetails TextureDetails? get textureDetails => throw UnimplementedError(); - + @override // TODO: implement rendering bool get rendering => throw UnimplementedError(); + + @override + // TODO: implement hasViewer + Stream get hasViewer => throw UnimplementedError(); } diff --git a/pubspec.yaml b/pubspec.yaml index 01a1b39e..5815846a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: polyvox_filament description: A Flutter plugin to wrap the Filament rendering engine. -version: 0.0.1 +version: 0.5.0 homepage: environment: