replace isReadyForScene with hasViewer stream and update version number/CHANGELOG

This commit is contained in:
Nick Fisher
2023-10-17 08:57:00 +08:00
parent 7f9c5a0f2d
commit 2553d854e9
6 changed files with 55 additions and 43 deletions

View File

@@ -1,3 +1,4 @@
## 0.0.1 ## 0.5.0
* TODO: Describe initial release. * Replaced `isReadyForScene` Future in `FilamentController` with the `Stream<bool>` `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.

View File

@@ -42,7 +42,6 @@ class ExampleWidget extends StatefulWidget {
class _ExampleWidgetState extends State<ExampleWidget> { class _ExampleWidgetState extends State<ExampleWidget> {
FilamentController? _filamentController; FilamentController? _filamentController;
FilamentEntity? _shapes; FilamentEntity? _shapes;
FilamentEntity? _flightHelmet; FilamentEntity? _flightHelmet;
List<String>? _animations; List<String>? _animations;
@@ -65,10 +64,13 @@ class _ExampleWidgetState extends State<ExampleWidget> {
bool _coneHidden = false; bool _coneHidden = false;
bool _frustumCulling = true; bool _frustumCulling = true;
StreamSubscription? _hasViewerListener;
@override @override
void dispose() { void dispose() {
super.dispose(); super.dispose();
_pickResultListener?.cancel(); _pickResultListener?.cancel();
_hasViewerListener?.cancel();
} }
Widget _item(void Function() onTap, String text) { Widget _item(void Function() onTap, String text) {
@@ -92,9 +94,10 @@ class _ExampleWidgetState extends State<ExampleWidget> {
picked = _filamentController!.getNameForEntity(entityId!); picked = _filamentController!.getNameForEntity(entityId!);
}); });
}); });
_filamentController!.isReadyForScene.then((readyForScene) { _hasViewerListener =
_filamentController!.hasViewer.listen((bool hasViewer) {
setState(() { setState(() {
_readyForScene = readyForScene; _readyForScene = hasViewer;
}); });
}); });
} }

View File

@@ -8,20 +8,32 @@ typedef FilamentEntity = int;
enum ToneMapper { ACES, FILMIC, LINEAR } enum ToneMapper { ACES, FILMIC, LINEAR }
class TextureDetails { class TextureDetails {
final int textureId; final int textureId;
final int width; final int width;
final int height; 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 { 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; 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<bool> get hasViewer;
/// ///
/// The result(s) of calling [pick] (see below). /// 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]. /// 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(); 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. /// 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) . /// 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 /// 4) The FilamentController will notify FilamentWidget that a texture is available
/// 5) The FilamentWidget will replace the empty Container with a Texture widget /// 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); void createViewer(int width, int height);

View File

@@ -16,18 +16,17 @@ class FilamentControllerFFI extends FilamentController {
double _pixelRatio = 1.0; double _pixelRatio = 1.0;
Completer _isReadyForScene = Completer();
Future get isReadyForScene => _isReadyForScene.future;
late Pointer<Void>? _assetManager; late Pointer<Void>? _assetManager;
late NativeLibrary _lib; late NativeLibrary _lib;
Pointer<Void>? _viewer; Pointer<Void>? _viewer;
final String? uberArchivePath; final String? uberArchivePath;
Stream<bool> get hasViewer => _hasViewerController.stream;
final _hasViewerController = StreamController<bool>();
Stream<FilamentEntity> get pickResult => _pickResultController.stream; Stream<FilamentEntity> get pickResult => _pickResultController.stream;
final _pickResultController = StreamController<FilamentEntity>.broadcast(); final _pickResultController = StreamController<FilamentEntity>.broadcast();
@@ -99,12 +98,12 @@ class FilamentControllerFFI extends FilamentController {
_assetManager = null; _assetManager = null;
_lib.destroy_filament_viewer_ffi(viewer!); _lib.destroy_filament_viewer_ffi(viewer!);
_isReadyForScene = Completer(); _hasViewerController.add(false);
} }
@override @override
Future destroyTexture() async { Future destroyTexture() async {
if(textureDetails != null) { if (textureDetails != null) {
await _channel.invokeMethod("destroyTexture", textureDetails!.textureId); await _channel.invokeMethod("destroyTexture", textureDetails!.textureId);
} }
print("Texture destroyed"); print("Texture destroyed");
@@ -118,14 +117,11 @@ class FilamentControllerFFI extends FilamentController {
throw Exception( throw Exception(
"Viewer already exists, make sure you call destroyViewer first"); "Viewer already exists, make sure you call destroyViewer first");
} }
if(textureDetails != null) { if (textureDetails != null) {
throw Exception( throw Exception(
"Texture already exists, make sure you call destroyTexture first"); "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<ResourceLoaderWrapper>.fromAddress( var loader = Pointer<ResourceLoaderWrapper>.fromAddress(
await _channel.invokeMethod("getResourceLoaderWrapper")); await _channel.invokeMethod("getResourceLoaderWrapper"));
if (loader == nullptr) { if (loader == nullptr) {
@@ -140,7 +136,6 @@ class FilamentControllerFFI extends FilamentController {
await _channel.invokeMethod("createTexture", [size.width, size.height]); await _channel.invokeMethod("createTexture", [size.width, size.height]);
var flutterTextureId = textures[0]; var flutterTextureId = textures[0];
// void* on iOS (pointer to pixel buffer), void* on Android (pointer to native window), null on Windows/macOS // 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; var surfaceAddress = textures[1] as int? ?? 0;
@@ -192,8 +187,9 @@ class FilamentControllerFFI extends FilamentController {
_assetManager = _lib.get_asset_manager(_viewer!); _assetManager = _lib.get_asset_manager(_viewer!);
_isReadyForScene.complete(true); textureDetails = TextureDetails(
textureDetails = TextureDetails(textureId: flutterTextureId!, width: width, height: height); 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. /// # Given we don't do this on other platforms, I'm OK to stick with the existing solution for the time being.
/// ############################################################################ /// ############################################################################
/// ///
@override @override
Future resize(int width, int height, {double scaleFactor = 1.0}) async { 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?) // so this exception should never be thrown (right?)
if(textureDetails == null) { if (textureDetails == null) {
throw Exception("Resize currently underway, ignoring"); throw Exception("Resize currently underway, ignoring");
} }
var _textureDetails = textureDetails; var _textureDetails = textureDetails;
textureDetails = null; textureDetails = null;
_lib.set_rendering_ffi(_viewer!, false); _lib.set_rendering_ffi(_viewer!, false);
if(_textureDetails != null) { if (_textureDetails != null) {
if (_viewer != null) { if (_viewer != null) {
_lib.destroy_swap_chain_ffi(_viewer!); _lib.destroy_swap_chain_ffi(_viewer!);
} }
@@ -288,7 +283,6 @@ class FilamentControllerFFI extends FilamentController {
print("Size after pixel ratio : $width x $height "); print("Size after pixel ratio : $width x $height ");
var textures = await _channel var textures = await _channel
.invokeMethod("createTexture", [newSize.width, newSize.height]); .invokeMethod("createTexture", [newSize.width, newSize.height]);
@@ -306,19 +300,18 @@ class FilamentControllerFFI extends FilamentController {
if (nativeTexture != 0) { if (nativeTexture != 0) {
assert(surfaceAddress == 0); assert(surfaceAddress == 0);
print("Creating render target from native texture $nativeTexture"); print("Creating render target from native texture $nativeTexture");
_lib.create_render_target_ffi( _lib.create_render_target_ffi(_viewer!, nativeTexture,
_viewer!, nativeTexture, newSize.width.toInt(), newSize.height.toInt()); newSize.width.toInt(), newSize.height.toInt());
} }
_lib.update_viewport_and_camera_projection_ffi( _lib.update_viewport_and_camera_projection_ffi(
_viewer!, newSize.width.toInt(), newSize.height.toInt(), 1.0); _viewer!, newSize.width.toInt(), newSize.height.toInt(), 1.0);
await setRendering(_rendering); await setRendering(_rendering);
textureDetails = TextureDetails(textureId: textures[0]!, width: width, height: height); textureDetails =
TextureDetails(textureId: textures[0]!, width: width, height: height);
} }
@override @override
Future clearBackgroundImage() async { Future clearBackgroundImage() async {
if (_viewer == null) { if (_viewer == null) {

View File

@@ -120,8 +120,8 @@ class FilamentControllerMethodChannel extends FilamentController {
bool _resizing = false; bool _resizing = false;
Future<TextureDetails> resize(int width, int height,
Future<TextureDetails> resize(int width, int height, {double scaleFactor = 1.0}) async { {double scaleFactor = 1.0}) async {
throw Exception(); throw Exception();
_resizing = true; _resizing = true;
_textureId = await _channel.invokeMethod( _textureId = await _channel.invokeMethod(
@@ -670,12 +670,16 @@ class FilamentControllerMethodChannel extends FilamentController {
// TODO: implement getNameForEntity // TODO: implement getNameForEntity
throw UnimplementedError(); throw UnimplementedError();
} }
@override @override
// TODO: implement textureDetails // TODO: implement textureDetails
TextureDetails? get textureDetails => throw UnimplementedError(); TextureDetails? get textureDetails => throw UnimplementedError();
@override @override
// TODO: implement rendering // TODO: implement rendering
bool get rendering => throw UnimplementedError(); bool get rendering => throw UnimplementedError();
@override
// TODO: implement hasViewer
Stream<bool> get hasViewer => throw UnimplementedError();
} }

View File

@@ -1,6 +1,6 @@
name: polyvox_filament name: polyvox_filament
description: A Flutter plugin to wrap the Filament rendering engine. description: A Flutter plugin to wrap the Filament rendering engine.
version: 0.0.1 version: 0.5.0
homepage: homepage:
environment: environment: