diff --git a/example/assets/1.glb b/example/assets/1.glb new file mode 100644 index 00000000..f6e5c4cf --- /dev/null +++ b/example/assets/1.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a1d72d69da8b933bde5453ef2000612b561f8ddfa536a7ad89288ef11eb876d +size 1624 diff --git a/example/assets/2.glb b/example/assets/2.glb new file mode 100644 index 00000000..a5bd07f7 --- /dev/null +++ b/example/assets/2.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fe0151ced8fd8609f9692f3d62dbd30890dada97cb6995572e37c7e7b4440d86 +size 21304 diff --git a/example/assets/3.glb b/example/assets/3.glb new file mode 100644 index 00000000..de666f7c --- /dev/null +++ b/example/assets/3.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0f8b793f6513cf83f80ce9c63c7b51d329908b1991f17ca642911634b15b32de +size 34172 diff --git a/example/lib/main.dart b/example/lib/main.dart index 6f7409d6..ef06ecef 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -14,7 +14,7 @@ const loadDefaultScene = bool.hasEnvironment('--load-default-scene'); void main() async { print(loadDefaultScene); - runApp(MyApp()); + runApp(const MyApp()); } class MyApp extends StatefulWidget { @@ -63,12 +63,10 @@ class ExampleWidgetState extends State { static double orbitSpeedX = 0.01; static double orbitSpeedY = 0.01; - static FilamentEntity? last; - static bool hasSkybox = false; static bool coneHidden = false; - static FilamentEntity? shapes; + static final assets = []; static FilamentEntity? flightHelmet; static FilamentEntity? buster; @@ -90,16 +88,17 @@ class ExampleWidgetState extends State { _filamentController = FilamentControllerFFI(); }); await Future.delayed(const Duration(milliseconds: 100)); + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { await _filamentController!.createViewer(); await _filamentController! .loadSkybox("assets/default_env/default_env_skybox.ktx"); await _filamentController!.setRendering(true); - shapes = - await _filamentController!.loadGlb("assets/shapes/shapes.glb"); - ExampleWidgetState.animations = await _filamentController! - .getAnimationNames(ExampleWidgetState.shapes!); + assets.add( + await _filamentController!.loadGlb("assets/shapes/shapes.glb")); + ExampleWidgetState.animations = + await _filamentController!.getAnimationNames(assets.first); hasSkybox = true; rendering = true; }); @@ -113,6 +112,26 @@ class ExampleWidgetState extends State { _listener.cancel(); } + Widget _assetEntry(FilamentEntity entity) { + return Row(children: [ + Text("Asset ${entity}"), + IconButton( + tooltip: "Transform to unit cube", + onPressed: () async { + await _filamentController!.transformToUnitCube(entity); + }, + icon: const Icon(Icons.settings_overscan_outlined)), + IconButton( + onPressed: () async { + await _filamentController!.removeAsset(entity); + assets.remove(entity); + setState(() {}); + }, + icon: const Icon(Icons.cancel_sharp)) + ]); + ; + } + @override Widget build(BuildContext context) { return Stack(children: [ @@ -122,13 +141,33 @@ class ExampleWidgetState extends State { padding: _viewportMargin, ), ), - Align( - alignment: Alignment.bottomCenter, + Positioned( + bottom: 80, + left: 10, + height: 200, + width: 300, child: Container( - padding: const EdgeInsets.only(bottom: 30), + padding: EdgeInsets.symmetric(horizontal: 30, vertical: 10), height: 100, - color: Colors.white, - child: Row(crossAxisAlignment: CrossAxisAlignment.end, children: [ + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30), + color: Colors.white.withOpacity(0.25), + ), + child: ListView(children: assets.map(_assetEntry).toList()))), + Positioned( + bottom: 10, + left: 10, + right: 10, + height: 60, + child: Container( + height: 60, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30), + color: Colors.white.withOpacity(0.25), + ), + padding: const EdgeInsets.symmetric(horizontal: 10), + child: + Row(crossAxisAlignment: CrossAxisAlignment.center, children: [ ControllerMenu( controller: _filamentController, onControllerDestroyed: () {}, @@ -136,9 +175,10 @@ class ExampleWidgetState extends State { setState(() { _filamentController = controller; _listener = _filamentController!.onLoad - .listen((FilamentEntity entity) { - print("Set last to $entity"); - last = entity; + .listen((FilamentEntity entity) async { + assets.add(entity); + animations = await _filamentController! + .getAnimationNames(entity); if (mounted) { setState(() {}); } @@ -150,6 +190,35 @@ class ExampleWidgetState extends State { SceneMenu( controller: _filamentController, ), + GestureDetector( + onTap: () async { + await _filamentController! + .loadGlb('assets/shapes/shapes.glb'); + }, + child: Container( + color: Colors.transparent, + child: const Text("shapes.glb"))), + SizedBox(width: 5), + GestureDetector( + onTap: () async { + await _filamentController!.loadGlb('assets/1.glb'); + }, + child: Container( + color: Colors.transparent, child: const Text("1.glb"))), + SizedBox(width: 5), + GestureDetector( + onTap: () async { + await _filamentController!.loadGlb('assets/2.glb'); + }, + child: Container( + color: Colors.transparent, child: const Text("2.glb"))), + SizedBox(width: 5), + GestureDetector( + onTap: () async { + await _filamentController!.loadGlb('assets/3.glb'); + }, + child: Container( + color: Colors.transparent, child: const Text("3.glb"))), Expanded(child: Container()), TextButton( child: const Text("Toggle viewport size"), diff --git a/example/lib/menus/asset_submenu.dart b/example/lib/menus/asset_submenu.dart index d6c904e3..752fd7c6 100644 --- a/example/lib/menus/asset_submenu.dart +++ b/example/lib/menus/asset_submenu.dart @@ -28,72 +28,35 @@ class _AssetSubmenuState extends State { var children = [ MenuItemButton( onPressed: () async { - if (ExampleWidgetState.shapes == null) { - ExampleWidgetState.shapes = - await widget.controller.loadGlb('assets/shapes/shapes.glb'); - ExampleWidgetState.animations = await widget.controller - .getAnimationNames(ExampleWidgetState.shapes!); - } else { - await widget.controller.removeAsset(ExampleWidgetState.shapes!); - ExampleWidgetState.shapes = null; - ExampleWidgetState.animations = null; - } + Timer.periodic(Duration(milliseconds: 50), (_) async { + await widget.controller.setBoneTransform( + ExampleWidgetState.assets.last, + "Cylinder", + "Bone", + Matrix4.rotationX(pi / 2)); + }); }, - child: const Text('Load GLB')), - MenuItemButton( - onPressed: ExampleWidgetState.shapes != null - ? null - : () async { - if (ExampleWidgetState.shapes != null) { - widget.controller.removeAsset(ExampleWidgetState.shapes!); - } - ExampleWidgetState.shapes = await widget.controller - .loadGltf('assets/shapes/shapes.gltf', 'assets/shapes'); - }, - child: const Text('Load GLTF')), - MenuItemButton( - onPressed: ExampleWidgetState.shapes == null - ? null - : () async { - await widget.controller - .transformToUnitCube(ExampleWidgetState.shapes!); - }, - child: const Text('Transform to unit cube')), - MenuItemButton( - onPressed: ExampleWidgetState.shapes == null - ? null - : () async { - Timer.periodic(Duration(milliseconds: 50), (_) async { - await widget.controller.setBoneTransform( - ExampleWidgetState.shapes!, - "Cylinder", - "Bone", - Matrix4.rotationX(pi / 2)); - }); - }, child: const Text('Set bone transform for Cylinder (pi/2 rotation X)')), MenuItemButton( - onPressed: ExampleWidgetState.shapes == null - ? null - : () async { - await widget.controller.addBoneAnimation( - ExampleWidgetState.shapes!, - BoneAnimationData( - "Bone", - ["Cylinder"], - List.generate( - 60, - (idx) => v.Quaternion.axisAngle( - v.Vector3(0, 0, 1), pi * 8 * (idx / 60)) - .normalized()), - 1000.0 / 60.0)); - }, + onPressed: () async { + await widget.controller.addBoneAnimation( + ExampleWidgetState.assets.last, + BoneAnimationData( + "Bone", + ["Cylinder"], + List.generate( + 60, + (idx) => v.Quaternion.axisAngle( + v.Vector3(0, 0, 1), pi * 8 * (idx / 60)) + .normalized()), + 1000.0 / 60.0)); + }, child: const Text('Set bone transform animation for Cylinder')), MenuItemButton( onPressed: () async { - var names = await widget.controller - .getMorphTargetNames(ExampleWidgetState.shapes!, "Cylinder"); + var names = await widget.controller.getMorphTargetNames( + ExampleWidgetState.assets.last, "Cylinder"); await showDialog( context: context, builder: (ctx) { @@ -108,28 +71,32 @@ class _AssetSubmenuState extends State { MenuItemButton( onPressed: () async { widget.controller.setMorphTargetWeights( - ExampleWidgetState.shapes!, "Cylinder", List.filled(4, 1.0)); + ExampleWidgetState.assets.last, + "Cylinder", + List.filled(4, 1.0)); }, child: const Text("set Cylinder morph weights to 1")), MenuItemButton( onPressed: () async { widget.controller.setMorphTargetWeights( - ExampleWidgetState.shapes!, "Cylinder", List.filled(4, 0.0)); + ExampleWidgetState.assets.last, + "Cylinder", + List.filled(4, 0.0)); }, child: const Text("Set Cylinder morph weights to 0")), MenuItemButton( onPressed: () async { widget.controller - .setPosition(ExampleWidgetState.shapes!, 1.0, 1.0, -1.0); + .setPosition(ExampleWidgetState.assets.last, 1.0, 1.0, -1.0); }, child: const Text('Set position to 1, 1, -1'), ), MenuItemButton( onPressed: () async { if (ExampleWidgetState.coneHidden) { - widget.controller.reveal(ExampleWidgetState.shapes!, "Cone"); + widget.controller.reveal(ExampleWidgetState.assets.last, "Cone"); } else { - widget.controller.hide(ExampleWidgetState.shapes!, "Cone"); + widget.controller.hide(ExampleWidgetState.assets.last, "Cone"); } ExampleWidgetState.coneHidden = !ExampleWidgetState.coneHidden; @@ -137,12 +104,10 @@ class _AssetSubmenuState extends State { child: Text(ExampleWidgetState.coneHidden ? 'show cone' : 'hide cone')), MenuItemButton( - onPressed: ExampleWidgetState.shapes == null - ? null - : () async { - widget.controller.setMaterialColor( - ExampleWidgetState.shapes!, "Cone", 0, Colors.purple); - }, + onPressed: () async { + widget.controller.setMaterialColor( + ExampleWidgetState.assets.last, "Cone", 0, Colors.purple); + }, child: const Text("Set cone material color to purple")), MenuItemButton( onPressed: () async { @@ -154,7 +119,7 @@ class _AssetSubmenuState extends State { if (ExampleWidgetState.animations != null) { children.addAll(ExampleWidgetState.animations!.map((a) => MenuItemButton( onPressed: () { - widget.controller.playAnimation(ExampleWidgetState.shapes!, + widget.controller.playAnimation(ExampleWidgetState.assets.last, ExampleWidgetState.animations!.indexOf(a), replaceActive: true, crossfade: 0.5, @@ -164,7 +129,7 @@ class _AssetSubmenuState extends State { "play animation ${ExampleWidgetState.animations!.indexOf(a)} (replace/fade)")))); children.addAll(ExampleWidgetState.animations!.map((a) => MenuItemButton( onPressed: () { - widget.controller.playAnimation(ExampleWidgetState.shapes!, + widget.controller.playAnimation(ExampleWidgetState.assets.last, ExampleWidgetState.animations!.indexOf(a), replaceActive: false, loop: ExampleWidgetState.loop); }, @@ -202,7 +167,7 @@ class _AssetSubmenuState extends State { await widget.controller .playAnimation(ExampleWidgetState.buster!, 0, loop: true); ExampleWidgetState.animations = await widget.controller - .getAnimationNames(ExampleWidgetState.shapes!); + .getAnimationNames(ExampleWidgetState.assets.last); } else { await widget.controller.removeAsset(ExampleWidgetState.buster!); ExampleWidgetState.buster = null; @@ -272,7 +237,7 @@ class _AssetSubmenuState extends State { await widget.controller.clearAssets(); ExampleWidgetState.flightHelmet = null; ExampleWidgetState.buster = null; - ExampleWidgetState.shapes = null; + ExampleWidgetState.assets.clear(); }, child: const Text('Clear assets')), ], diff --git a/example/lib/menus/camera_submenu.dart b/example/lib/menus/camera_submenu.dart index 8ce199b8..988edfdc 100644 --- a/example/lib/menus/camera_submenu.dart +++ b/example/lib/menus/camera_submenu.dart @@ -82,21 +82,17 @@ class _CameraSubmenuState extends State { child: const Text('Move to 1, 1, -1'), ), MenuItemButton( - onPressed: ExampleWidgetState.last == null - ? null - : () async { - await widget.controller - .setCamera(ExampleWidgetState.last!, null); - }, + onPressed: () async { + await widget.controller + .setCamera(ExampleWidgetState.assets.last!, null); + }, child: const Text('Set to first camera in last added asset'), ), MenuItemButton( - onPressed: ExampleWidgetState.last == null - ? null - : () async { - await widget.controller - .moveCameraToAsset(ExampleWidgetState.last!); - }, + onPressed: () async { + await widget.controller + .moveCameraToAsset(ExampleWidgetState.assets.last!); + }, child: const Text("Move to last added asset"), ), MenuItemButton( diff --git a/example/lib/menus/controller_menu.dart b/example/lib/menus/controller_menu.dart index b18c3461..007b0236 100644 --- a/example/lib/menus/controller_menu.dart +++ b/example/lib/menus/controller_menu.dart @@ -100,18 +100,16 @@ class _ControllerMenuState extends State { menuChildren: items, builder: (BuildContext context, MenuController controller, Widget? child) { - return Align( - alignment: Alignment.bottomLeft, - child: TextButton( - onPressed: () { - if (controller.isOpen) { - controller.close(); - } else { - controller.open(); - } - }, - child: const Text("Controller / Viewer"), - )); + return TextButton( + onPressed: () { + if (controller.isOpen) { + controller.close(); + } else { + controller.open(); + } + }, + child: const Text("Controller / Viewer"), + ); }); } } diff --git a/example/lib/menus/scene_menu.dart b/example/lib/menus/scene_menu.dart index 56367c14..d52a66d3 100644 --- a/example/lib/menus/scene_menu.dart +++ b/example/lib/menus/scene_menu.dart @@ -21,8 +21,8 @@ class _SceneMenuState extends State { void didUpdateWidget(SceneMenu oldWidget) { super.didUpdateWidget(oldWidget); if (widget.controller != null && - widget.controller != oldWidget.controller || - widget.controller!.hasViewer != oldWidget.controller!.hasViewer) { + (widget.controller != oldWidget.controller || + widget.controller!.hasViewer != oldWidget.controller!.hasViewer)) { setState(() {}); } } @@ -47,20 +47,18 @@ class _SceneMenuState extends State { ], builder: (BuildContext context, MenuController controller, Widget? child) { - return Align( - alignment: Alignment.bottomLeft, - child: TextButton( - onPressed: !hasViewer - ? null - : () { - if (controller.isOpen) { - controller.close(); - } else { - controller.open(); - } - }, - child: const Text("Scene"), - )); + return TextButton( + onPressed: !hasViewer + ? null + : () { + if (controller.isOpen) { + controller.close(); + } else { + controller.open(); + } + }, + child: const Text("Scene"), + ); }, ); }); diff --git a/example/pubspec.lock b/example/pubspec.lock index 6877f7cb..13549bc4 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -93,10 +93,10 @@ packages: dependency: transitive description: name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "7.0.0" flutter: dependency: "direct main" description: flutter @@ -158,6 +158,14 @@ packages: description: flutter source: sdk version: "0.0.0" + intl: + dependency: transitive + description: + name: intl + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + url: "https://pub.dev" + source: hosted + version: "0.18.1" js: dependency: transitive description: @@ -166,6 +174,30 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: f38a2c91c12f31726ca13015fbab3d2e9440edcb7c17b8b36ed9b85ed6eee6a2 + url: "https://pub.dev" + source: hosted + version: "9.0.11" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "23770c69594f5260a79fe9d84e29f8b175d1b05d128e751c904b3cdf910e5dfc" + url: "https://pub.dev" + source: hosted + version: "1.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: b06739349ec2477e943055aea30172c5c7000225f79dad4702e2ec0eda79a6ff + url: "https://pub.dev" + source: hosted + version: "1.0.5" lints: dependency: transitive description: @@ -186,18 +218,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" path: dependency: transitive description: @@ -306,10 +338,10 @@ packages: dependency: transitive description: name: platform - sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 + sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59" url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.3" plugin_platform_interface: dependency: transitive description: @@ -330,10 +362,10 @@ packages: dependency: transitive description: name: process - sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + sha256: "266ca5be5820feefc777793d0a583acfc8c40834893c87c00c6c09e2cf58ea42" url: "https://pub.dev" source: hosted - version: "4.2.4" + version: "5.0.1" sky_engine: dependency: transitive description: flutter @@ -431,26 +463,34 @@ packages: dependency: transitive description: name: vm_service - sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 + sha256: a13d5503b4facefc515c8c587ce3cf69577a7b064a9f1220e005449cf1f64aad url: "https://pub.dev" source: hosted - version: "11.10.0" + version: "12.0.0" web: dependency: transitive description: name: web - sha256: "14f1f70c51119012600c5f1f60ca68efda5a9b6077748163c6af2893ec5df8fc" + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.2.1-beta" + version: "0.3.0" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + url: "https://pub.dev" + source: hosted + version: "2.4.0" webdriver: dependency: transitive description: name: webdriver - sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49" + sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" win32: dependency: transitive description: @@ -476,5 +516,5 @@ packages: source: hosted version: "6.4.2" sdks: - dart: ">=3.2.0-157.0.dev <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=3.16.0-0.2.pre"