feat: camera and resizing improvements

This commit is contained in:
Nick Fisher
2024-10-02 16:47:55 +08:00
parent d294938a2c
commit 562ecf2ee5
27 changed files with 840 additions and 613 deletions

View File

@@ -9,30 +9,40 @@ import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_in
/// Call [createViewerWithOptions] to create an instance of [ThermionViewer].
///
class ThermionFlutterPlugin {
ThermionFlutterPlugin._();
static bool _initializing = false;
static ThermionViewer? _viewer;
static ThermionFlutterOptions? options;
static Future<ThermionViewer> createViewer(
{ThermionFlutterOptions options =
const ThermionFlutterOptions.empty()}) async {
if (_initializing) {
throw Exception("Existing call to createViewer has not completed.");
}
_initializing = true;
if (_viewer != null) {
throw Exception(
"Instance of ThermionViewer has already been created. Ensure you call dispose() on that instance.");
}
options = options;
_viewer =
await ThermionFlutterPlatform.instance.createViewer(options: options);
var camera = await _viewer!.getActiveCamera();
camera.setLensProjection();
_viewer!.onDispose(() async {
_viewer = null;
ThermionFlutterPlugin.options = null;
});
_initializing = false;
return _viewer!;
}
}

View File

@@ -7,14 +7,32 @@ import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dar
import 'package:vector_math/vector_math_64.dart' hide Colors;
class ThermionTextureWidget extends StatefulWidget {
///
///
///
final ThermionViewer viewer;
///
///
///
final t.View view;
///
///
///
final Widget? initial;
///
/// A callback that will be invoked whenever this widget (and the underlying texture is resized).
///
final Future Function(Size size, t.View view, double pixelRatio)? onResize;
const ThermionTextureWidget(
{super.key, required this.viewer, required this.view, this.initial});
{super.key,
required this.viewer,
required this.view,
this.initial,
this.onResize});
@override
State<StatefulWidget> createState() {
@@ -54,9 +72,16 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
.createTexture(widget.view, width, height);
await widget.view.updateViewport(_texture!.width, _texture!.height);
var camera = await widget.view.getCamera();
await camera.setLensProjection(
aspect: _texture!.width / _texture!.height);
try {
await widget.onResize?.call(
Size(_texture!.width.toDouble(), _texture!.height.toDouble()),
widget.view,
dpr);
} catch (err, st) {
print(err);
print(st);
}
if (mounted) {
setState(() {});
@@ -124,7 +149,9 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
_resizing.add(completer.future);
newSize *= MediaQuery.of(context).devicePixelRatio;
final dpr = MediaQuery.of(context).devicePixelRatio;
newSize *= dpr;
var newWidth = newSize.width.ceil();
var newHeight = newSize.height.ceil();
@@ -136,11 +163,13 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
0,
);
await widget.view.updateViewport(_texture!.width, _texture!.height);
var camera = await widget.view.getCamera();
await camera.setLensProjection(
aspect: _texture!.width.toDouble() / _texture!.height.toDouble());
await widget.onResize?.call(
Size(_texture!.width.toDouble(), _texture!.height.toDouble()),
widget.view,
dpr);
if (!mounted) {
return;
}
@@ -169,108 +198,3 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
]));
}
}
// class _ThermionWidgetState extends State<ThermionWidget> {
// ThermionFlutterTexture? _texture;
// @override
// void initState() {
// WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
// await widget.viewer.initialized;
// widget.viewer.onDispose(() async {
// _rendering = false;
// if (_texture != null) {
// var texture = _texture;
// _texture = null;
// if (mounted) {
// setState(() {});
// }
// await ThermionFlutterPlugin.destroyTexture(texture!);
// }
// });
// var dpr = MediaQuery.of(context).devicePixelRatio;
// var size = ((context.findRenderObject()) as RenderBox).size;
// _texture = await ThermionFlutterPlugin.createTexture(
// size.width, size.height, 0, 0, dpr);
// if (mounted) {
// setState(() {});
// }
// _requestFrame();
// });
// super.initState();
// }
// bool _rendering = false;
// void _requestFrame() {
// WidgetsBinding.instance.scheduleFrameCallback((d) async {
// if (!_rendering) {
// _rendering = true;
// await widget.viewer.requestFrame();
// _rendering = false;
// }
// _requestFrame();
// });
// }
// bool _resizing = false;
// Timer? _resizeTimer;
// Future _resizeTexture(Size newSize) async {
// _resizeTimer?.cancel();
// _resizeTimer = Timer(const Duration(milliseconds: 500), () async {
// if (_resizing || !mounted) {
// return;
// }
// _resizeTimer!.cancel();
// _resizing = true;
// if (!mounted) {
// return;
// }
// var dpr = MediaQuery.of(context).devicePixelRatio;
// _texture.resize(newSize.width.ceil(), newSize.height.ceil(), 0, 0, dpr);
// setState(() {});
// _resizing = false;
// });
// }
// @override
// Widget build(BuildContext context) {
// if (_texture == null || _resizing) {
// return widget.initial ??
// Container(
// color:
// kIsWeb ? const Color.fromARGB(0, 170, 129, 129) : Colors.red);
// }
// var textureWidget = Texture(
// key: ObjectKey("texture_${_texture!.flutterId}"),
// textureId: _texture!.flutterId!,
// filterQuality: FilterQuality.none,
// freeze: false,
// );
// return ResizeObserver(
// onResized: _resizeTexture,
// child: Stack(children: [
// Positioned.fill(
// child: Platform.isLinux || Platform.isWindows
// ? Transform(
// alignment: Alignment.center,
// transform: Matrix4.rotationX(
// pi), // TODO - this rotation is due to OpenGL texture coordinate working in a different space from Flutter, can we move this to the C++ side somewhere?
// child: textureWidget)
// : textureWidget)
// ]));
// }
// }

View File

@@ -8,6 +8,16 @@ import 'package:thermion_flutter_web/thermion_flutter_web_options.dart';
import 'package:thermion_dart/src/viewer/src/shared_types/view.dart' as t;
import 'thermion_widget_windows.dart';
Future kDefaultResizeCallback(Size size, t.View view, double pixelRatio) async {
var camera = await view.getCamera();
var near = await camera.getNear();
var far = await camera.getCullingFar();
var focalLength = await camera.getFocalLength();
await camera.setLensProjection(near:near, far:far, focalLength: focalLength,
aspect: size.width.toDouble() / size.height.toDouble());
}
class ThermionWidget extends StatefulWidget {
///
/// The viewer.
@@ -15,14 +25,25 @@ class ThermionWidget extends StatefulWidget {
final ThermionViewer viewer;
///
/// The view.
/// The [View] associated with this widget. If null, the default View will be used.
///
final t.View? view;
///
/// The options to use when creating this widget.
/// A callback to invoke whenever this widget and the underlying surface are
/// resized. If a callback is not explicitly provided, the default callback
/// will be run, which changes the aspect ratio for the active camera in
/// the View managed by this widget. If you specify your own callback,
/// you probably want to preserve this behaviour (otherwise the aspect ratio)
/// will be incorrect.
///
/// To completely disable the resize callback, pass [null].
///
/// IMPORTANT - size is specified in physical pixels, not logical pixels.
/// If you need to work with Flutter dimensions, divide [size] by
/// [pixelRatio].
///
final ThermionFlutterOptions? options;
final Future Function(Size size, t.View view, double pixelRatio)? onResize;
///
/// The content to render before the texture widget is available.
@@ -31,7 +52,11 @@ class ThermionWidget extends StatefulWidget {
final Widget? initial;
const ThermionWidget(
{Key? key, this.initial, required this.viewer, this.view, this.options})
{Key? key,
this.initial,
required this.viewer,
this.view,
this.onResize = kDefaultResizeCallback})
: super(key: key);
@override
@@ -66,7 +91,7 @@ class _ThermionWidgetState extends State<ThermionWidget> {
if (kIsWeb) {
return ThermionWidgetWeb(
viewer: widget.viewer,
options: widget.options as ThermionFlutterWebOptions);
options: ThermionFlutterPlugin.options as ThermionFlutterWebOptions?);
}
if (Platform.isWindows) {
@@ -77,6 +102,7 @@ class _ThermionWidgetState extends State<ThermionWidget> {
key: ObjectKey(view!),
initial: widget.initial,
viewer: widget.viewer,
view: view!);
view: view!,
onResize: widget.onResize);
}
}

View File

@@ -3,3 +3,4 @@ library;
export 'src/thermion_widget.dart';
export 'src/thermion_listener_widget.dart';
export 'src/camera/camera_selector_widget.dart';
export 'src/camera/camera_orientation_widget.dart';