diff --git a/example/lib/example_viewport.dart b/example/lib/example_viewport.dart index 4cfe2ee5..1e9e8bf2 100644 --- a/example/lib/example_viewport.dart +++ b/example/lib/example_viewport.dart @@ -1,26 +1,36 @@ import 'package:flutter/widgets.dart'; +import 'package:flutter_filament/entities/entity_transform_controller.dart'; import 'package:flutter_filament/filament_controller.dart'; +import 'package:flutter_filament/widgets/entity_controller_mouse_widget.dart'; import 'package:flutter_filament/widgets/filament_gesture_detector.dart'; import 'package:flutter_filament/widgets/filament_widget.dart'; class ExampleViewport extends StatelessWidget { final FilamentController? controller; + final EntityTransformController? entityTransformController; final EdgeInsets padding; + final FocusNode keyboardFocusNode; const ExampleViewport( - {super.key, required this.controller, required this.padding}); + {super.key, + required this.controller, + required this.padding, + required this.keyboardFocusNode, + this.entityTransformController}); @override Widget build(BuildContext context) { return controller != null ? Padding( padding: padding, - child: FilamentGestureDetector( - showControlOverlay: true, - controller: controller!, - child: FilamentWidget( - controller: controller!, - ))) + child: EntityTransformMouseControllerWidget( + transformController: entityTransformController, + child: FilamentGestureDetector( + showControlOverlay: true, + controller: controller!, + child: FilamentWidget( + controller: controller!, + )))) : Container(); } } diff --git a/example/lib/main.dart b/example/lib/main.dart index ef06ecef..79231291 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:flutter_filament/entities/entity_transform_controller.dart'; import 'package:flutter_filament/filament_controller_ffi.dart'; import 'package:flutter_filament_example/camera_matrix_overlay.dart'; import 'package:flutter_filament_example/menus/controller_menu.dart'; @@ -11,9 +12,11 @@ import 'package:flutter_filament_example/menus/scene_menu.dart'; import 'package:flutter_filament/filament_controller.dart'; const loadDefaultScene = bool.hasEnvironment('--load-default-scene'); +const foo = String.fromEnvironment('foo'); void main() async { print(loadDefaultScene); + print(foo); runApp(const MyApp()); } @@ -99,6 +102,9 @@ class ExampleWidgetState extends State { await _filamentController!.loadGlb("assets/shapes/shapes.glb")); ExampleWidgetState.animations = await _filamentController!.getAnimationNames(assets.first); + await _filamentController! + .setCameraManipulatorOptions(zoomSpeed: 1.0); + hasSkybox = true; rendering = true; }); @@ -112,6 +118,10 @@ class ExampleWidgetState extends State { _listener.cancel(); } + EntityTransformController? _transformController; + FilamentEntity? _controlled; + final _sharedFocusNode = FocusNode(); + Widget _assetEntry(FilamentEntity entity) { return Row(children: [ Text("Asset ${entity}"), @@ -121,13 +131,28 @@ class ExampleWidgetState extends State { await _filamentController!.transformToUnitCube(entity); }, icon: const Icon(Icons.settings_overscan_outlined)), + IconButton( + onPressed: () async { + _transformController?.dispose(); + if (_controlled == entity) { + _controlled = null; + } else { + _controlled = entity; + _transformController?.dispose(); + _transformController = + EntityTransformController(_filamentController!, entity); + } + setState(() {}); + }, + icon: Icon(Icons.control_camera, + color: _controlled == entity ? Colors.green : Colors.black)), IconButton( onPressed: () async { await _filamentController!.removeAsset(entity); assets.remove(entity); setState(() {}); }, - icon: const Icon(Icons.cancel_sharp)) + icon: const Icon(Icons.cancel_sharp)), ]); ; } @@ -137,9 +162,10 @@ class ExampleWidgetState extends State { return Stack(children: [ Positioned.fill( child: ExampleViewport( - controller: _filamentController, - padding: _viewportMargin, - ), + controller: _filamentController, + entityTransformController: _transformController, + padding: _viewportMargin, + keyboardFocusNode: _sharedFocusNode), ), Positioned( bottom: 80, @@ -147,7 +173,7 @@ class ExampleWidgetState extends State { height: 200, width: 300, child: Container( - padding: EdgeInsets.symmetric(horizontal: 30, vertical: 10), + padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 10), height: 100, decoration: BoxDecoration( borderRadius: BorderRadius.circular(30), @@ -169,6 +195,7 @@ class ExampleWidgetState extends State { child: Row(crossAxisAlignment: CrossAxisAlignment.center, children: [ ControllerMenu( + sharedFocusNode: _sharedFocusNode, controller: _filamentController, onControllerDestroyed: () {}, onControllerCreated: (controller) { @@ -188,6 +215,7 @@ class ExampleWidgetState extends State { }); }), SceneMenu( + sharedFocusNode: _sharedFocusNode, controller: _filamentController, ), GestureDetector( diff --git a/example/lib/menus/asset_submenu.dart b/example/lib/menus/asset_submenu.dart index ab757a71..d0a452eb 100644 --- a/example/lib/menus/asset_submenu.dart +++ b/example/lib/menus/asset_submenu.dart @@ -4,6 +4,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_filament/animations/animation_data.dart'; + import 'package:flutter_filament/filament_controller.dart'; import 'package:flutter_filament_example/main.dart'; import 'package:permission_handler/permission_handler.dart'; @@ -27,8 +28,22 @@ class _AssetSubmenuState extends State { Widget _shapesSubmenu() { var children = [ MenuItemButton( + closeOnActivate: false, onPressed: () async { - Timer.periodic(Duration(milliseconds: 50), (_) async { + var entity = await widget.controller + .getChildEntity(ExampleWidgetState.assets.last, "Cylinder"); + await showDialog( + context: context, + builder: (BuildContext context) { + return Center( + child: Container( + color: Colors.white, child: Text(entity.toString()))); + }); + }, + child: const Text('Find Cylinder entity by name')), + MenuItemButton( + onPressed: () async { + Timer.periodic(const Duration(milliseconds: 50), (_) async { await widget.controller.setBoneTransform( ExampleWidgetState.assets.last, "Cylinder", @@ -143,11 +158,40 @@ class _AssetSubmenuState extends State { return SubmenuButton(menuChildren: children, child: const Text("Shapes")); } + Widget _geometrySubmenu() { + return MenuItemButton( + onPressed: () async { + await widget.controller.createGeometry([ + -1, + 0, + -1, + -1, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + -1, + ], [ + 0, + 1, + 2, + 2, + 3, + 0 + ], "asset://assets/solidcolor.filamat"); + }, + child: const Text("Custom geometry")); + } + @override Widget build(BuildContext context) { return SubmenuButton( menuChildren: [ _shapesSubmenu(), + _geometrySubmenu(), MenuItemButton( onPressed: () async { ExampleWidgetState.directionalLight = await widget.controller diff --git a/example/lib/menus/controller_menu.dart b/example/lib/menus/controller_menu.dart index 007b0236..69ee210a 100644 --- a/example/lib/menus/controller_menu.dart +++ b/example/lib/menus/controller_menu.dart @@ -9,11 +9,13 @@ class ControllerMenu extends StatefulWidget { final FilamentController? controller; final void Function(FilamentController controller) onControllerCreated; final void Function() onControllerDestroyed; + final FocusNode sharedFocusNode; ControllerMenu( {this.controller, required this.onControllerCreated, - required this.onControllerDestroyed}); + required this.onControllerDestroyed, + required this.sharedFocusNode}); @override State createState() => _ControllerMenuState(); @@ -21,7 +23,6 @@ class ControllerMenu extends StatefulWidget { class _ControllerMenuState extends State { FilamentController? _filamentController; - final FocusNode _buttonFocusNode = FocusNode(debugLabel: 'Camera Menu'); void _createController({String? uberArchivePath}) { if (_filamentController != null) { @@ -57,8 +58,10 @@ class _ControllerMenuState extends State { child: const Text("Create FilamentViewer"), onPressed: _filamentController == null ? null - : () { - _filamentController!.createViewer(); + : () async { + await _filamentController!.createViewer(); + await _filamentController! + .setCameraManipulatorOptions(zoomSpeed: 1.0); }, ), MenuItemButton( @@ -96,7 +99,7 @@ class _ControllerMenuState extends State { ]); } return MenuAnchor( - childFocusNode: _buttonFocusNode, + childFocusNode: widget.sharedFocusNode, menuChildren: items, builder: (BuildContext context, MenuController controller, Widget? child) { diff --git a/example/lib/menus/scene_menu.dart b/example/lib/menus/scene_menu.dart index d52a66d3..d2c95f1c 100644 --- a/example/lib/menus/scene_menu.dart +++ b/example/lib/menus/scene_menu.dart @@ -7,8 +7,10 @@ import 'package:flutter_filament_example/menus/rendering_submenu.dart'; class SceneMenu extends StatefulWidget { final FilamentController? controller; + final FocusNode sharedFocusNode; - const SceneMenu({super.key, required this.controller}); + const SceneMenu( + {super.key, required this.controller, required this.sharedFocusNode}); @override State createState() { @@ -34,6 +36,8 @@ class _SceneMenuState extends State { widget.controller?.hasViewer ?? ValueNotifier(false), builder: (BuildContext ctx, bool hasViewer, Widget? child) { return MenuAnchor( + onClose: () {}, + childFocusNode: widget.sharedFocusNode, menuChildren: widget.controller == null ? [] : [