require createViewer to be specified manually

This commit is contained in:
Nick Fisher
2023-10-27 20:32:16 +08:00
parent 59c2f8d125
commit 40485081bc
5 changed files with 123 additions and 109 deletions

View File

@@ -1,3 +1,8 @@
## 0.6.0
* `createViewer` is no longer called by `FilamentWidget` and must be called manually at least one frame after a FilamentWidget has been inserted into the widget hierarchy.
## 0.5.0 ## 0.5.0
* Replaced `isReadyForScene` Future in `FilamentController` with the `Stream<bool>` `hasViewer`. * Replaced `isReadyForScene` Future in `FilamentController` with the `Stream<bool>` `hasViewer`.

View File

@@ -84,7 +84,7 @@ class MyApp extends StatelessWidget {
This is a relatively lightweight object, however its constructor will load/bind symbols from the native library. This may momentarily block the UI, so you may wish to structure your app so that this is hidden behind a static widget until it is available. This is a relatively lightweight object, however its constructor will load/bind symbols from the native library. This may momentarily block the UI, so you may wish to structure your app so that this is hidden behind a static widget until it is available.
Next, create an instance of `FilamentWidget` in the widget hierarchy where you want the rendering canvas to appear. This can be sized as large or as small as you want. Flutter widgets can be positioned above or below the `FilamentWidget`. Next, create an instance of `FilamentWidget` in the widget hierarchy where you want the rendering canvas to appear. This can be sized as large or as small as you want. On most platforms, Flutter widgets can be positioned above or below the `FilamentWidget`.
``` ```
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
@@ -105,20 +105,20 @@ class MyApp extends StatelessWidget {
``` ```
When a `FilamentWidget` is added to the widget hierarchy: When a `FilamentWidget` is added to the widget hierarchy:
1) on the first frame, by default a Container will be rendered with solid red. If you want to change this, pass a widget as the `initial` paramer to the `FilamentWidget` constructor. 1) by default a Container will be rendered with solid red. If you want to change this, pass a widget as the `initial` paramer to the `FilamentWidget` constructor.
2) on the second frame, `FilamentWidget` will retrieve its actual size and request the `FilamentController` to create: 2) on the second frame, `FilamentWidget` will pass its dimensions/pixel ratio to the `FilamentController`
* the backing textures needed to insert a `Texture` widget into 3) You can then call `createViewer` to create:
* the rendering surface (on most platforms, a backing texture that will be registered with Flutter for use in a `Texture` widget)
* a rendering thread * a rendering thread
* a `FilamentViewer` and an `AssetManager`, which will allow you to load assets/cameras/lighting/etc via the `FilamentController` * a `FilamentViewer` and an `AssetManager`, which will allow you to load assets/cameras/lighting/etc via the `FilamentController`
3) after an indeterminate number of frames, `FilamentController` will notify `FilamentWidget` when a texture is available the viewport 4) after an indeterminate number of frames, `FilamentController` will notify `FilamentWidget` when a rendering surface is available the viewport
4) `FilamentWidget` will replace the default `initial` Widget with the viewport (which will initially be solid black or white, depending on your platform). 5) `FilamentWidget` will replace the default `initial` Widget with the viewport (which will initially be solid black or white, depending on your platform).
It's important to note that there *will* be a delay between adding a `FilamentWidget` and the actual rendering viewport becoming available. This is why we fill `FilamentWidget` with red - to make it abundantly clear that you need to handle this asynchronous delay appropriately. You can call `await _filamentController.isReadyForScene` if you need to wait until the viewport is actually ready for rendering. IMPORTANT: there *will* be a delay between adding a `FilamentWidget`, calling `createViewer` and the actual rendering viewport becoming available. This is why we fill `FilamentWidget` with red - to make it abundantly clear that you need to handle this asynchronous delay appropriately. Once `createViewer` has completed, the viewport is available for rendering.
> Currently, the `initial` widget will also be displayed whenever the viewport is resized (including changing orientation on mobile and drag-to-resize on desktop). You probably want to change this from the default red. > Currently, the `initial` widget will also be displayed whenever the viewport is resized (including changing orientation on mobile and drag-to-resize on desktop). You probably want to change this from the default red.
Congratulations! You now have a scene. It's completely empty, so you probably want to add something visible.
Congratulations! You now have a scene. It's completely empty, so you probably want to add.
### Load a background ### Load a background

View File

@@ -1,6 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_filament/animations/animation_data.dart'; import 'package:flutter_filament/animations/animation_data.dart';
@@ -21,7 +20,6 @@ class TextureDetails {
} }
abstract class FilamentController { abstract class FilamentController {
/// ///
/// Whether a Flutter Texture widget should be inserted into the widget hierarchy. /// Whether a Flutter Texture widget should be inserted into the widget hierarchy.
/// This will be false on certain platforms where we use a transparent window underlay. /// This will be false on certain platforms where we use a transparent window underlay.
@@ -35,17 +33,6 @@ abstract class FilamentController {
/// ///
final textureDetails = ValueNotifier<TextureDetails?>(null); final textureDetails = ValueNotifier<TextureDetails?>(null);
///
/// 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].
@@ -73,13 +60,6 @@ abstract class FilamentController {
/// ///
Future setFrameRate(int framerate); Future setFrameRate(int framerate);
///
/// Called by FilamentGestureDetector to set the pixel ratio (obtained from [MediaQuery]) before creating the texture/viewport.
/// You may call this yourself if you want to increase/decrease the pixel density of the viewport, but calling this method won't do anything on its own.
/// You will need to manually recreate the texture/viewer afterwards.
///
void setPixelRatio(double ratio);
/// ///
/// Destroys the viewer and all backing textures. You can leave the FilamentWidget in the hierarchy after this is called, but you will need to manually call [createViewer] to /// Destroys the viewer and all backing textures. You can leave the FilamentWidget in the hierarchy after this is called, but you will need to manually call [createViewer] to
/// ///
@@ -96,24 +76,26 @@ abstract class FilamentController {
Future destroyTexture(); Future destroyTexture();
/// ///
/// Called by [FilamentWidget]; you generally will not need to call this yourself. /// Create a FilamentViewer. Must be called at least one frame after a [FilamentWidget] has been inserted into the rendering hierarchy.
/// To recap, you can create a viewport is created in the Flutter rendering hierarchy by:
/// 1) Create a FilamentController
/// 2) Insert a FilamentWidget into the rendering tree, passing your FilamentController
/// 3) Initially, the FilamentWidget will only contain an empty Container (by default, with a solid red background).
/// This widget will render a single frame to get its actual size, then will itself call [createViewer]. You do not need to call [createViewer] yourself.
/// 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, listen to the [viewer] stream.
/// ///
Future createViewer(Rect rect); /// Before a FilamentViewer is created, the FilamentWidget will only contain an empty Container (by default, with a solid red background).
/// FilamentWidget will then call [setDimensions] with dimensions/pixel ratio of the viewport
/// Calling [createViewer] will then 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) .
/// [FilamentWidget] will be notified that a texture is available and will replace the empty Container with a Texture widget
///
Future createViewer();
/// ///
/// Resize the viewport & backing texture. /// Sets the dimensions of the viewport and pixel ratio (obtained from [MediaQuery]) to be used the next time [resize] or [createViewer] is called.
/// This is called by FilamentWidget; you shouldn't need to invoke this manually. /// This is called by FilamentWidget; you shouldn't need to invoke this manually.
/// ///
Future resize(Rect rect); Future setDimensions(ui.Rect rect, double pixelRatio);
///
/// Resize the viewport & backing texture to the current dimensions (as last set by [setDimensions]).
/// This is called by FilamentWidget; you shouldn't need to invoke this manually.
///
Future resize();
/// ///
/// Set the background image to [path] (which should have a file extension .png, .jpg, or .ktx). /// Set the background image to [path] (which should have a file extension .png, .jpg, or .ktx).

View File

@@ -4,6 +4,7 @@ import 'dart:io';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:ffi/ffi.dart'; import 'package:ffi/ffi.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_filament/filament_controller.dart'; import 'package:flutter_filament/filament_controller.dart';
@@ -31,9 +32,10 @@ class FilamentControllerFFI extends FilamentController {
final String? uberArchivePath; final String? uberArchivePath;
Pointer<Void> _driver = nullptr.cast<Void>();
@override @override
Stream<bool> get hasViewer => _hasViewerController.stream; final rect = ValueNotifier<Rect?>(null);
final _hasViewerController = StreamController<bool>();
@override @override
Stream<FilamentEntity> get pickResult => _pickResultController.stream; Stream<FilamentEntity> get pickResult => _pickResultController.stream;
@@ -62,8 +64,9 @@ class FilamentControllerFFI extends FilamentController {
_resizingWidth = call.arguments[0]; _resizingWidth = call.arguments[0];
_resizingHeight = call.arguments[1]; _resizingHeight = call.arguments[1];
_resizeTimer = Timer(const Duration(milliseconds: 500), () async { _resizeTimer = Timer(const Duration(milliseconds: 500), () async {
await resize(Offset.zero & this.rect.value = Offset.zero &
ui.Size(_resizingWidth!.toDouble(), _resizingHeight!.toDouble())); ui.Size(_resizingWidth!.toDouble(), _resizingHeight!.toDouble());
await resize();
}); });
}); });
late DynamicLibrary dl; late DynamicLibrary dl;
@@ -78,10 +81,10 @@ class FilamentControllerFFI extends FilamentController {
_usesBackingWindow = result; _usesBackingWindow = result;
}); });
} }
} }
bool _rendering = false; bool _rendering = false;
@override
bool get rendering => _rendering; bool get rendering => _rendering;
@override @override
@@ -107,9 +110,10 @@ class FilamentControllerFFI extends FilamentController {
} }
@override @override
void setPixelRatio(double ratio) { Future setDimensions(Rect rect, double ratio) async {
this.rect.value = Rect.fromLTWH(rect.left, rect.top,
rect.width * _pixelRatio, rect.height * _pixelRatio);
_pixelRatio = ratio; _pixelRatio = ratio;
print("Set pixel ratio to $ratio");
} }
@override @override
@@ -129,7 +133,6 @@ class FilamentControllerFFI extends FilamentController {
_assetManager = null; _assetManager = null;
_lib.destroy_filament_viewer_ffi(viewer!); _lib.destroy_filament_viewer_ffi(viewer!);
_hasViewerController.add(false);
} }
@override @override
@@ -141,14 +144,15 @@ class FilamentControllerFFI extends FilamentController {
print("Texture destroyed"); print("Texture destroyed");
} }
Pointer<Void> _driver = nullptr.cast<Void>();
/// ///
/// Called by `FilamentWidget`. You do not need to call this yourself. /// Called by `FilamentWidget`. You do not need to call this yourself.
/// ///
@override @override
Future createViewer(Rect rect) async { Future createViewer() async {
if (rect.value == null) {
throw Exception(
"Dimensions have not yet been set by FilamentWidget. You need to wait for at least one frame after FilamentWidget has been inserted into the hierarchy");
}
if (_viewer != null) { if (_viewer != null) {
throw Exception( throw Exception(
"Viewer already exists, make sure you call destroyViewer first"); "Viewer already exists, make sure you call destroyViewer first");
@@ -164,8 +168,6 @@ class FilamentControllerFFI extends FilamentController {
throw Exception("Failed to get resource loader"); throw Exception("Failed to get resource loader");
} }
rect = Rect.fromLTWH(rect.left, rect.top, rect.width * _pixelRatio, rect.height * _pixelRatio);
if (Platform.isWindows && requiresTextureWidget) { if (Platform.isWindows && requiresTextureWidget) {
_driver = Pointer<Void>.fromAddress( _driver = Pointer<Void>.fromAddress(
await _channel.invokeMethod("getDriverPlatform")); await _channel.invokeMethod("getDriverPlatform"));
@@ -178,7 +180,9 @@ class FilamentControllerFFI extends FilamentController {
var renderCallbackOwner = var renderCallbackOwner =
Pointer<Void>.fromAddress(renderCallbackResult[1]); Pointer<Void>.fromAddress(renderCallbackResult[1]);
var renderingSurface = await _createRenderingSurface(rect); var renderingSurface = await _createRenderingSurface();
print("Got rendering surface");
_viewer = _lib.create_filament_viewer_ffi( _viewer = _lib.create_filament_viewer_ffi(
Pointer<Void>.fromAddress(renderingSurface.sharedContext ?? 0), Pointer<Void>.fromAddress(renderingSurface.sharedContext ?? 0),
@@ -187,7 +191,7 @@ class FilamentControllerFFI extends FilamentController {
loader, loader,
renderCallback, renderCallback,
renderCallbackOwner); renderCallbackOwner);
print("Created viewer");
if (_viewer!.address == 0) { if (_viewer!.address == 0) {
throw Exception("Failed to create viewer. Check logs for details"); throw Exception("Failed to create viewer. Check logs for details");
} }
@@ -195,29 +199,31 @@ class FilamentControllerFFI extends FilamentController {
_assetManager = _lib.get_asset_manager(_viewer!); _assetManager = _lib.get_asset_manager(_viewer!);
_lib.create_swap_chain_ffi(_viewer!, renderingSurface.surface, _lib.create_swap_chain_ffi(_viewer!, renderingSurface.surface,
rect.width.toInt(), rect.height.toInt()); rect.value!.width.toInt(), rect.value!.height.toInt());
print("Created swap chain");
if (renderingSurface.textureHandle != 0) { if (renderingSurface.textureHandle != 0) {
print( print(
"Creating render target from native texture ${renderingSurface.textureHandle}"); "Creating render target from native texture ${renderingSurface.textureHandle}");
_lib.create_render_target_ffi(_viewer!, renderingSurface.textureHandle, _lib.create_render_target_ffi(_viewer!, renderingSurface.textureHandle,
rect.width.toInt(), rect.height.toInt()); rect.value!.width.toInt(), rect.value!.height.toInt());
} }
textureDetails.value = TextureDetails( textureDetails.value = TextureDetails(
textureId: renderingSurface.flutterTextureId!, textureId: renderingSurface.flutterTextureId!,
width: rect.width.toInt(), width: rect.value!.width.toInt(),
height: rect.height.toInt()); height: rect.value!.height.toInt());
print("texture details ${textureDetails.value}");
_lib.update_viewport_and_camera_projection_ffi( _lib.update_viewport_and_camera_projection_ffi(
_viewer!, rect.width.toInt(), rect.height.toInt(), 1.0); _viewer!, rect.value!.width.toInt(), rect.value!.height.toInt(), 1.0);
_hasViewerController.add(true);
} }
Future<RenderingSurface> _createRenderingSurface(Rect rect) async { Future<RenderingSurface> _createRenderingSurface() async {
return RenderingSurface.from(await _channel.invokeMethod( return RenderingSurface.from(await _channel.invokeMethod("createTexture", [
"createTexture", rect.value!.width,
[rect.width, rect.height, rect.left, rect.top])); rect.value!.height,
rect.value!.left,
rect.value!.top
]));
} }
/// ///
@@ -286,8 +292,7 @@ class FilamentControllerFFI extends FilamentController {
/// ///
bool _resizing = false; bool _resizing = false;
@override @override
Future resize(Rect rect) async { Future resize() async {
if (_viewer == null) { if (_viewer == null) {
throw Exception("Cannot resize without active viewer"); throw Exception("Cannot resize without active viewer");
} }
@@ -296,8 +301,6 @@ class FilamentControllerFFI extends FilamentController {
throw Exception("Resize currently underway, ignoring"); throw Exception("Resize currently underway, ignoring");
} }
rect = Rect.fromLTWH(rect.left, rect.top, rect.width * _pixelRatio, rect.height * _pixelRatio);
_resizing = true; _resizing = true;
_lib.set_rendering_ffi(_viewer!, false); _lib.set_rendering_ffi(_viewer!, false);
@@ -313,11 +316,15 @@ class FilamentControllerFFI extends FilamentController {
} }
} else if (Platform.isWindows) { } else if (Platform.isWindows) {
print("Resizing window with rect $rect"); print("Resizing window with rect $rect");
await _channel.invokeMethod( await _channel.invokeMethod("resizeWindow", [
"resizeWindow", [rect.width, rect.height, rect.left, rect.top]); rect.value!.width,
rect.value!.height,
rect.value!.left,
rect.value!.top
]);
} }
var renderingSurface = await _createRenderingSurface(rect); var renderingSurface = await _createRenderingSurface();
if (_viewer!.address == 0) { if (_viewer!.address == 0) {
throw Exception("Failed to create viewer. Check logs for details"); throw Exception("Failed to create viewer. Check logs for details");
@@ -327,23 +334,23 @@ class FilamentControllerFFI extends FilamentController {
if (!_usesBackingWindow) { if (!_usesBackingWindow) {
_lib.create_swap_chain_ffi(_viewer!, renderingSurface.surface, _lib.create_swap_chain_ffi(_viewer!, renderingSurface.surface,
rect.width.toInt(), rect.height.toInt()); rect.value!.width.toInt(), rect.value!.height.toInt());
} }
if (renderingSurface.textureHandle != 0) { if (renderingSurface.textureHandle != 0) {
print( print(
"Creating render target from native texture ${renderingSurface.textureHandle}"); "Creating render target from native texture ${renderingSurface.textureHandle}");
_lib.create_render_target_ffi(_viewer!, renderingSurface.textureHandle, _lib.create_render_target_ffi(_viewer!, renderingSurface.textureHandle,
rect.width.toInt(), rect.height.toInt()); rect.value!.width.toInt(), rect.value!.height.toInt());
} }
textureDetails.value = TextureDetails( textureDetails.value = TextureDetails(
textureId: renderingSurface.flutterTextureId!, textureId: renderingSurface.flutterTextureId!,
width: rect.width.toInt(), width: rect.value!.width.toInt(),
height: rect.height.toInt()); height: rect.value!.height.toInt());
_lib.update_viewport_and_camera_projection_ffi( _lib.update_viewport_and_camera_projection_ffi(
_viewer!, rect.width.toInt(), rect.height.toInt(), 1.0); _viewer!, rect.value!.width.toInt(), rect.value!.height.toInt(), 1.0);
await setRendering(_rendering); await setRendering(_rendering);
@@ -739,6 +746,7 @@ class FilamentControllerFFI extends FilamentController {
_assetManager!, asset, index, loop, reverse, replaceActive, crossfade); _assetManager!, asset, index, loop, reverse, replaceActive, crossfade);
} }
@override
Future setAnimationFrame( Future setAnimationFrame(
FilamentEntity asset, int index, int animationFrame) async { FilamentEntity asset, int index, int animationFrame) async {
if (_viewer == null) { if (_viewer == null) {
@@ -747,6 +755,7 @@ class FilamentControllerFFI extends FilamentController {
_lib.set_animation_frame(_assetManager!, asset, index, animationFrame); _lib.set_animation_frame(_assetManager!, asset, index, animationFrame);
} }
@override
Future stopAnimation(FilamentEntity asset, int animationIndex) async { Future stopAnimation(FilamentEntity asset, int animationIndex) async {
if (_viewer == null) { if (_viewer == null) {
throw Exception("No viewer available, ignoring"); throw Exception("No viewer available, ignoring");
@@ -792,6 +801,7 @@ class FilamentControllerFFI extends FilamentController {
_lib.set_bloom_ffi(_viewer!, bloom); _lib.set_bloom_ffi(_viewer!, bloom);
} }
@override
Future setCameraFocalLength(double focalLength) async { Future setCameraFocalLength(double focalLength) async {
if (_viewer == null) { if (_viewer == null) {
throw Exception("No viewer available, ignoring"); throw Exception("No viewer available, ignoring");
@@ -799,6 +809,7 @@ class FilamentControllerFFI extends FilamentController {
_lib.set_camera_focal_length(_viewer!, focalLength); _lib.set_camera_focal_length(_viewer!, focalLength);
} }
@override
Future setCameraFocusDistance(double focusDistance) async { Future setCameraFocusDistance(double focusDistance) async {
if (_viewer == null) { if (_viewer == null) {
throw Exception("No viewer available, ignoring"); throw Exception("No viewer available, ignoring");
@@ -806,6 +817,7 @@ class FilamentControllerFFI extends FilamentController {
_lib.set_camera_focus_distance(_viewer!, focusDistance); _lib.set_camera_focus_distance(_viewer!, focusDistance);
} }
@override
Future setCameraPosition(double x, double y, double z) async { Future setCameraPosition(double x, double y, double z) async {
if (_viewer == null) { if (_viewer == null) {
throw Exception("No viewer available, ignoring"); throw Exception("No viewer available, ignoring");
@@ -813,6 +825,7 @@ class FilamentControllerFFI extends FilamentController {
_lib.set_camera_position(_viewer!, x, y, z); _lib.set_camera_position(_viewer!, x, y, z);
} }
@override
Future moveCameraToAsset(FilamentEntity asset) async { Future moveCameraToAsset(FilamentEntity asset) async {
if (_viewer == null) { if (_viewer == null) {
throw Exception("No viewer available, ignoring"); throw Exception("No viewer available, ignoring");
@@ -828,6 +841,7 @@ class FilamentControllerFFI extends FilamentController {
_lib.set_view_frustum_culling(_viewer!, enabled); _lib.set_view_frustum_culling(_viewer!, enabled);
} }
@override
Future setCameraExposure( Future setCameraExposure(
double aperture, double shutterSpeed, double sensitivity) async { double aperture, double shutterSpeed, double sensitivity) async {
if (_viewer == null) { if (_viewer == null) {
@@ -836,6 +850,7 @@ class FilamentControllerFFI extends FilamentController {
_lib.set_camera_exposure(_viewer!, aperture, shutterSpeed, sensitivity); _lib.set_camera_exposure(_viewer!, aperture, shutterSpeed, sensitivity);
} }
@override
Future setCameraRotation(double rads, double x, double y, double z) async { Future setCameraRotation(double rads, double x, double y, double z) async {
if (_viewer == null) { if (_viewer == null) {
throw Exception("No viewer available, ignoring"); throw Exception("No viewer available, ignoring");
@@ -843,6 +858,7 @@ class FilamentControllerFFI extends FilamentController {
_lib.set_camera_rotation(_viewer!, rads, x, y, z); _lib.set_camera_rotation(_viewer!, rads, x, y, z);
} }
@override
Future setCameraModelMatrix(List<double> matrix) async { Future setCameraModelMatrix(List<double> matrix) async {
if (_viewer == null) { if (_viewer == null) {
throw Exception("No viewer available, ignoring"); throw Exception("No viewer available, ignoring");
@@ -856,6 +872,7 @@ class FilamentControllerFFI extends FilamentController {
calloc.free(ptr); calloc.free(ptr);
} }
@override
Future setMaterialColor(FilamentEntity asset, String meshName, Future setMaterialColor(FilamentEntity asset, String meshName,
int materialIndex, Color color) async { int materialIndex, Color color) async {
if (_viewer == null) { if (_viewer == null) {
@@ -875,6 +892,7 @@ class FilamentControllerFFI extends FilamentController {
} }
} }
@override
Future transformToUnitCube(FilamentEntity asset) async { Future transformToUnitCube(FilamentEntity asset) async {
if (_viewer == null) { if (_viewer == null) {
throw Exception("No viewer available, ignoring"); throw Exception("No viewer available, ignoring");
@@ -882,6 +900,7 @@ class FilamentControllerFFI extends FilamentController {
_lib.transform_to_unit_cube(_assetManager!, asset); _lib.transform_to_unit_cube(_assetManager!, asset);
} }
@override
Future setPosition(FilamentEntity asset, double x, double y, double z) async { Future setPosition(FilamentEntity asset, double x, double y, double z) async {
if (_viewer == null) { if (_viewer == null) {
throw Exception("No viewer available, ignoring"); throw Exception("No viewer available, ignoring");
@@ -889,6 +908,7 @@ class FilamentControllerFFI extends FilamentController {
_lib.set_position(_assetManager!, asset, x, y, z); _lib.set_position(_assetManager!, asset, x, y, z);
} }
@override
Future setScale(FilamentEntity asset, double scale) async { Future setScale(FilamentEntity asset, double scale) async {
if (_viewer == null) { if (_viewer == null) {
throw Exception("No viewer available, ignoring"); throw Exception("No viewer available, ignoring");
@@ -896,6 +916,7 @@ class FilamentControllerFFI extends FilamentController {
_lib.set_scale(_assetManager!, asset, scale); _lib.set_scale(_assetManager!, asset, scale);
} }
@override
Future setRotation( Future setRotation(
FilamentEntity asset, double rads, double x, double y, double z) async { FilamentEntity asset, double rads, double x, double y, double z) async {
if (_viewer == null) { if (_viewer == null) {
@@ -904,6 +925,7 @@ class FilamentControllerFFI extends FilamentController {
_lib.set_rotation(_assetManager!, asset, rads, x, y, z); _lib.set_rotation(_assetManager!, asset, rads, x, y, z);
} }
@override
Future hide(FilamentEntity asset, String meshName) async { Future hide(FilamentEntity asset, String meshName) async {
if (_viewer == null) { if (_viewer == null) {
throw Exception("No viewer available, ignoring"); throw Exception("No viewer available, ignoring");
@@ -913,6 +935,7 @@ class FilamentControllerFFI extends FilamentController {
1) {} 1) {}
} }
@override
Future reveal(FilamentEntity asset, String meshName) async { Future reveal(FilamentEntity asset, String meshName) async {
if (_viewer == null) { if (_viewer == null) {
throw Exception("No viewer available, ignoring"); throw Exception("No viewer available, ignoring");
@@ -924,6 +947,7 @@ class FilamentControllerFFI extends FilamentController {
} }
} }
@override
String? getNameForEntity(FilamentEntity entity) { String? getNameForEntity(FilamentEntity entity) {
final result = _lib.get_name_for_entity(_assetManager!, entity); final result = _lib.get_name_for_entity(_assetManager!, entity);
if (result == nullptr) { if (result == nullptr) {
@@ -932,6 +956,7 @@ class FilamentControllerFFI extends FilamentController {
return result.cast<Utf8>().toDartString(); return result.cast<Utf8>().toDartString();
} }
@override
void pick(int x, int y) async { void pick(int x, int y) async {
if (_viewer == null) { if (_viewer == null) {
throw Exception("No viewer available, ignoring"); throw Exception("No viewer available, ignoring");

View File

@@ -121,11 +121,12 @@ class _SizedFilamentWidget extends StatefulWidget {
} }
class _SizedFilamentWidgetState extends State<_SizedFilamentWidget> { class _SizedFilamentWidgetState extends State<_SizedFilamentWidget> {
String? _error; String? _error;
late final AppLifecycleListener _appLifecycleListener; late final AppLifecycleListener _appLifecycleListener;
late double _pixelRatio;
Rect get _rect { Rect get _rect {
final renderBox = (context.findRenderObject()) as RenderBox; final renderBox = (context.findRenderObject()) as RenderBox;
final size = renderBox.size; final size = renderBox.size;
@@ -139,13 +140,12 @@ class _SizedFilamentWidgetState extends State<_SizedFilamentWidget> {
onStateChange: _handleStateChange, onStateChange: _handleStateChange,
); );
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
try { try {
widget.controller.setPixelRatio(MediaQuery.of(context).devicePixelRatio); _pixelRatio = MediaQuery.of(context).devicePixelRatio;
await widget.controller.createViewer(_rect); widget.controller.setDimensions(_rect, _pixelRatio);
} catch (err) { } catch (err) {
print("Fatal error : $err");
_error = err.toString(); _error = err.toString();
} }
setState(() {}); setState(() {});
@@ -167,8 +167,9 @@ class _SizedFilamentWidgetState extends State<_SizedFilamentWidget> {
// debug mode does need a longer timeout. // debug mode does need a longer timeout.
_resizeTimer?.cancel(); _resizeTimer?.cancel();
_resizeTimer = _resizeTimer = Timer(
Timer(Duration(milliseconds: (kReleaseMode || Platform.isWindows) ? 10 : 100), () async { Duration(milliseconds: (kReleaseMode || Platform.isWindows) ? 10 : 100),
() async {
if (!mounted) { if (!mounted) {
return; return;
} }
@@ -177,7 +178,8 @@ class _SizedFilamentWidgetState extends State<_SizedFilamentWidget> {
} }
_resizing = true; _resizing = true;
await widget.controller.resize(_rect); await widget.controller.setDimensions(_rect, _pixelRatio);
await widget.controller.resize();
_resizeTimer = null; _resizeTimer = null;
setState(() {}); setState(() {});
_resizing = false; _resizing = false;