add (very rough) gizmo, restructure Dart package into library, add EntityListWidget
This commit is contained in:
@@ -7,6 +7,8 @@
|
|||||||
* `setCameraRotation` now accepts a quaternion instead of an axis/angle
|
* `setCameraRotation` now accepts a quaternion instead of an axis/angle
|
||||||
* instancing is now supported.
|
* instancing is now supported.
|
||||||
* `setBoneTransform` has been removed. To set the transform for a bone, just `addBoneAnimation` with a single frame.
|
* `setBoneTransform` has been removed. To set the transform for a bone, just `addBoneAnimation` with a single frame.
|
||||||
|
* the Dart library has been restructured to expose a cleaner API surface. Import `package:flutter_filament/flutter_filament.dart`
|
||||||
|
* created a separate `Scene` class to hold lights/entities. For now, this is simply a singleton that holds all `getScene`
|
||||||
|
|
||||||
## 0.6.0
|
## 0.6.0
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_filament/filament_controller.dart';
|
import 'package:flutter_filament/flutter_filament.dart';
|
||||||
import 'package:vector_math/vector_math_64.dart' as v;
|
import 'package:vector_math/vector_math_64.dart' as v;
|
||||||
|
|
||||||
class CameraMatrixOverlay extends StatefulWidget {
|
class CameraMatrixOverlay extends StatefulWidget {
|
||||||
@@ -96,7 +96,7 @@ class _CameraMatrixOverlayState extends State<CameraMatrixOverlay> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text("Camera position : $_cameraPosition $_cameraRotation",
|
Text("Camera : $_cameraPosition $_cameraRotation",
|
||||||
style:
|
style:
|
||||||
const TextStyle(color: Colors.white, fontSize: 10)),
|
const TextStyle(color: Colors.white, fontSize: 10)),
|
||||||
// widget.showProjectionMatrices
|
// widget.showProjectionMatrices
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_filament/entities/entity_transform_controller.dart';
|
import 'package:flutter_filament/flutter_filament.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 {
|
class ExampleViewport extends StatelessWidget {
|
||||||
final FilamentController? controller;
|
final FilamentController? controller;
|
||||||
|
|||||||
@@ -1,22 +1,19 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
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/camera_matrix_overlay.dart';
|
||||||
import 'package:flutter_filament_example/menus/controller_menu.dart';
|
import 'package:flutter_filament_example/menus/controller_menu.dart';
|
||||||
import 'package:flutter_filament_example/example_viewport.dart';
|
import 'package:flutter_filament_example/example_viewport.dart';
|
||||||
import 'package:flutter_filament_example/picker_result_widget.dart';
|
import 'package:flutter_filament_example/picker_result_widget.dart';
|
||||||
import 'package:flutter_filament_example/menus/scene_menu.dart';
|
import 'package:flutter_filament_example/menus/scene_menu.dart';
|
||||||
|
|
||||||
import 'package:flutter_filament/filament_controller.dart';
|
import 'package:flutter_filament/flutter_filament.dart';
|
||||||
|
|
||||||
const loadDefaultScene = bool.hasEnvironment('--load-default-scene');
|
const loadDefaultScene = bool.hasEnvironment('--load-default-scene');
|
||||||
const foo = String.fromEnvironment('foo');
|
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
print(loadDefaultScene);
|
print(loadDefaultScene);
|
||||||
print(foo);
|
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,7 +28,15 @@ class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
theme: ThemeData(useMaterial3: true),
|
theme: ThemeData(
|
||||||
|
useMaterial3: true,
|
||||||
|
textTheme: const TextTheme(
|
||||||
|
labelLarge: TextStyle(fontSize: 12),
|
||||||
|
displayMedium: TextStyle(fontSize: 12),
|
||||||
|
headlineMedium: const TextStyle(fontSize: 12),
|
||||||
|
titleMedium: TextStyle(fontSize: 12),
|
||||||
|
bodyLarge: TextStyle(fontSize: 14),
|
||||||
|
bodyMedium: TextStyle(fontSize: 12))),
|
||||||
// showPerformanceOverlay: true,
|
// showPerformanceOverlay: true,
|
||||||
home: const Scaffold(body: ExampleWidget()));
|
home: const Scaffold(body: ExampleWidget()));
|
||||||
}
|
}
|
||||||
@@ -69,12 +74,6 @@ class ExampleWidgetState extends State<ExampleWidget> {
|
|||||||
static bool hasSkybox = false;
|
static bool hasSkybox = false;
|
||||||
static bool coneHidden = false;
|
static bool coneHidden = false;
|
||||||
|
|
||||||
static final assets = <FilamentEntity>[];
|
|
||||||
static FilamentEntity? flightHelmet;
|
|
||||||
static FilamentEntity? buster;
|
|
||||||
|
|
||||||
static FilamentEntity? directionalLight;
|
|
||||||
|
|
||||||
static bool loop = false;
|
static bool loop = false;
|
||||||
static final showProjectionMatrices = ValueNotifier<bool>(false);
|
static final showProjectionMatrices = ValueNotifier<bool>(false);
|
||||||
|
|
||||||
@@ -90,13 +89,13 @@ class ExampleWidgetState extends State<ExampleWidget> {
|
|||||||
});
|
});
|
||||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||||
await _filamentController!.createViewer();
|
await _filamentController!.createViewer();
|
||||||
_createEntityLoadListener();
|
|
||||||
await _filamentController!
|
await _filamentController!
|
||||||
.loadSkybox("assets/default_env/default_env_skybox.ktx");
|
.loadSkybox("assets/default_env/default_env_skybox.ktx");
|
||||||
|
|
||||||
await _filamentController!.setRendering(true);
|
await _filamentController!.setRendering(true);
|
||||||
assets.add(
|
|
||||||
await _filamentController!.loadGlb("assets/shapes/shapes.glb"));
|
await _filamentController!.loadGlb("assets/shapes/shapes.glb");
|
||||||
|
|
||||||
await _filamentController!
|
await _filamentController!
|
||||||
.setCameraManipulatorOptions(zoomSpeed: 1.0);
|
.setCameraManipulatorOptions(zoomSpeed: 1.0);
|
||||||
@@ -114,82 +113,9 @@ class ExampleWidgetState extends State<ExampleWidget> {
|
|||||||
_listener.cancel();
|
_listener.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _createEntityLoadListener() {
|
|
||||||
_listener =
|
|
||||||
_filamentController!.onLoad.listen((FilamentEntity entity) async {
|
|
||||||
assets.add(entity);
|
|
||||||
if (mounted) {
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
print(_filamentController!.getNameForEntity(entity) ?? "NAME NOT FOUND");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
EntityTransformController? _transformController;
|
EntityTransformController? _transformController;
|
||||||
FilamentEntity? _controlled;
|
|
||||||
final _sharedFocusNode = FocusNode();
|
|
||||||
|
|
||||||
Widget _assetEntry(FilamentEntity entity) {
|
final _sharedFocusNode = FocusNode();
|
||||||
return FutureBuilder(
|
|
||||||
future: _filamentController!.getAnimationNames(entity),
|
|
||||||
builder: (_, animations) {
|
|
||||||
if (animations.data == null) {
|
|
||||||
return Container();
|
|
||||||
}
|
|
||||||
return Row(children: [
|
|
||||||
Text("Asset ${entity}"),
|
|
||||||
IconButton(
|
|
||||||
iconSize: 14,
|
|
||||||
tooltip: "Transform to unit cube",
|
|
||||||
onPressed: () async {
|
|
||||||
await _filamentController!.transformToUnitCube(entity);
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.settings_overscan_outlined)),
|
|
||||||
IconButton(
|
|
||||||
iconSize: 14,
|
|
||||||
tooltip: "Attach mouse control",
|
|
||||||
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(
|
|
||||||
iconSize: 14,
|
|
||||||
tooltip: "Remove",
|
|
||||||
onPressed: () async {
|
|
||||||
await _filamentController!.removeEntity(entity);
|
|
||||||
assets.remove(entity);
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.cancel_sharp)),
|
|
||||||
if (animations.data!.isNotEmpty)
|
|
||||||
PopupMenuButton(
|
|
||||||
tooltip: "Animations",
|
|
||||||
itemBuilder: (_) => animations.data!
|
|
||||||
.map((a) => PopupMenuItem<String>(
|
|
||||||
value: a,
|
|
||||||
child: Text(a),
|
|
||||||
))
|
|
||||||
.toList(),
|
|
||||||
onSelected: (value) async {
|
|
||||||
print("Playing animation $value");
|
|
||||||
await _filamentController!
|
|
||||||
.playAnimation(entity, animations.data!.indexOf(value!));
|
|
||||||
},
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -201,26 +127,14 @@ class ExampleWidgetState extends State<ExampleWidget> {
|
|||||||
padding: _viewportMargin,
|
padding: _viewportMargin,
|
||||||
keyboardFocusNode: _sharedFocusNode),
|
keyboardFocusNode: _sharedFocusNode),
|
||||||
),
|
),
|
||||||
|
EntityListWidget(controller: _filamentController),
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: 80,
|
bottom: 0,
|
||||||
left: 10,
|
left: 0,
|
||||||
height: 200,
|
|
||||||
width: 300,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 10),
|
|
||||||
height: 100,
|
|
||||||
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,
|
right: 10,
|
||||||
height: 60,
|
height: 30,
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 60,
|
height: 30,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(30),
|
borderRadius: BorderRadius.circular(30),
|
||||||
color: Colors.white.withOpacity(0.25),
|
color: Colors.white.withOpacity(0.25),
|
||||||
@@ -231,11 +145,17 @@ class ExampleWidgetState extends State<ExampleWidget> {
|
|||||||
ControllerMenu(
|
ControllerMenu(
|
||||||
sharedFocusNode: _sharedFocusNode,
|
sharedFocusNode: _sharedFocusNode,
|
||||||
controller: _filamentController,
|
controller: _filamentController,
|
||||||
|
onToggleViewport: () {
|
||||||
|
setState(() {
|
||||||
|
_viewportMargin = (_viewportMargin == EdgeInsets.zero)
|
||||||
|
? const EdgeInsets.all(30)
|
||||||
|
: EdgeInsets.zero;
|
||||||
|
});
|
||||||
|
},
|
||||||
onControllerDestroyed: () {},
|
onControllerDestroyed: () {},
|
||||||
onControllerCreated: (controller) {
|
onControllerCreated: (controller) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_filamentController = controller;
|
_filamentController = controller;
|
||||||
_createEntityLoadListener();
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
SceneMenu(
|
SceneMenu(
|
||||||
@@ -251,21 +171,21 @@ class ExampleWidgetState extends State<ExampleWidget> {
|
|||||||
child: Container(
|
child: Container(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: const Text("shapes.glb"))),
|
child: const Text("shapes.glb"))),
|
||||||
SizedBox(width: 5),
|
const SizedBox(width: 5),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await _filamentController!.loadGlb('assets/1.glb');
|
await _filamentController!.loadGlb('assets/1.glb');
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Colors.transparent, child: const Text("1.glb"))),
|
color: Colors.transparent, child: const Text("1.glb"))),
|
||||||
SizedBox(width: 5),
|
const SizedBox(width: 5),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await _filamentController!.loadGlb('assets/2.glb');
|
await _filamentController!.loadGlb('assets/2.glb');
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Colors.transparent, child: const Text("2.glb"))),
|
color: Colors.transparent, child: const Text("2.glb"))),
|
||||||
SizedBox(width: 5),
|
const SizedBox(width: 5),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await _filamentController!.loadGlb('assets/3.glb');
|
await _filamentController!.loadGlb('assets/3.glb');
|
||||||
@@ -273,21 +193,11 @@ class ExampleWidgetState extends State<ExampleWidget> {
|
|||||||
child: Container(
|
child: Container(
|
||||||
color: Colors.transparent, child: const Text("3.glb"))),
|
color: Colors.transparent, child: const Text("3.glb"))),
|
||||||
Expanded(child: Container()),
|
Expanded(child: Container()),
|
||||||
TextButton(
|
|
||||||
child: const Text("Toggle viewport size"),
|
|
||||||
onPressed: () {
|
|
||||||
setState(() {
|
|
||||||
_viewportMargin = (_viewportMargin == EdgeInsets.zero)
|
|
||||||
? const EdgeInsets.all(30)
|
|
||||||
: EdgeInsets.zero;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
)
|
|
||||||
]))),
|
]))),
|
||||||
_filamentController == null
|
_filamentController == null
|
||||||
? Container()
|
? Container()
|
||||||
: Padding(
|
: Padding(
|
||||||
padding: const EdgeInsets.only(top: 40, left: 20, right: 20),
|
padding: const EdgeInsets.only(top: 10, left: 20, right: 20),
|
||||||
child: ValueListenableBuilder(
|
child: ValueListenableBuilder(
|
||||||
valueListenable: showProjectionMatrices,
|
valueListenable: showProjectionMatrices,
|
||||||
builder: (ctx, value, child) => CameraMatrixOverlay(
|
builder: (ctx, value, child) => CameraMatrixOverlay(
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter_filament/flutter_filament.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:flutter_filament_example/main.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
@@ -30,8 +26,8 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
|
|||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
closeOnActivate: false,
|
closeOnActivate: false,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
var entity = await widget.controller
|
var entity = await widget.controller.getChildEntity(
|
||||||
.getChildEntity(ExampleWidgetState.assets.last, "Cylinder");
|
widget.controller.scene.listEntities().last, "Cylinder");
|
||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
@@ -44,7 +40,7 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
|
|||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await widget.controller.addBoneAnimation(
|
await widget.controller.addBoneAnimation(
|
||||||
ExampleWidgetState.assets.last,
|
widget.controller.scene.listEntities().last,
|
||||||
BoneAnimationData([
|
BoneAnimationData([
|
||||||
"Bone"
|
"Bone"
|
||||||
], [
|
], [
|
||||||
@@ -59,13 +55,14 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
|
|||||||
const Text('Set bone transform for Cylinder (pi/2 rotation X)')),
|
const Text('Set bone transform for Cylinder (pi/2 rotation X)')),
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await widget.controller.resetBones(ExampleWidgetState.assets.last);
|
await widget.controller
|
||||||
|
.resetBones(widget.controller.scene.listEntities().last);
|
||||||
},
|
},
|
||||||
child: const Text('Reset bones for Cylinder')),
|
child: const Text('Reset bones for Cylinder')),
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await widget.controller.addBoneAnimation(
|
await widget.controller.addBoneAnimation(
|
||||||
ExampleWidgetState.assets.last,
|
widget.controller.scene.listEntities().last,
|
||||||
BoneAnimationData(
|
BoneAnimationData(
|
||||||
["Bone"],
|
["Bone"],
|
||||||
["Cylinder"],
|
["Cylinder"],
|
||||||
@@ -84,7 +81,7 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
|
|||||||
closeOnActivate: false,
|
closeOnActivate: false,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
var names = await widget.controller.getMorphTargetNames(
|
var names = await widget.controller.getMorphTargetNames(
|
||||||
ExampleWidgetState.assets.last, "Cylinder");
|
widget.controller.scene.listEntities().last, "Cylinder");
|
||||||
print("NAMES : $names");
|
print("NAMES : $names");
|
||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -100,7 +97,7 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
|
|||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
widget.controller.setMorphTargetWeights(
|
widget.controller.setMorphTargetWeights(
|
||||||
ExampleWidgetState.assets.last,
|
widget.controller.scene.listEntities().last,
|
||||||
"Cylinder",
|
"Cylinder",
|
||||||
List.filled(4, 1.0));
|
List.filled(4, 1.0));
|
||||||
},
|
},
|
||||||
@@ -108,24 +105,26 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
|
|||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
widget.controller.setMorphTargetWeights(
|
widget.controller.setMorphTargetWeights(
|
||||||
ExampleWidgetState.assets.last,
|
widget.controller.scene.listEntities().last,
|
||||||
"Cylinder",
|
"Cylinder",
|
||||||
List.filled(4, 0.0));
|
List.filled(4, 0.0));
|
||||||
},
|
},
|
||||||
child: const Text("Set Cylinder morph weights to 0")),
|
child: const Text("Set Cylinder morph weights to 0")),
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
widget.controller
|
widget.controller.setPosition(
|
||||||
.setPosition(ExampleWidgetState.assets.last, 1.0, 1.0, -1.0);
|
widget.controller.scene.listEntities().last, 1.0, 1.0, -1.0);
|
||||||
},
|
},
|
||||||
child: const Text('Set position to 1, 1, -1'),
|
child: const Text('Set position to 1, 1, -1'),
|
||||||
),
|
),
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
if (ExampleWidgetState.coneHidden) {
|
if (ExampleWidgetState.coneHidden) {
|
||||||
widget.controller.reveal(ExampleWidgetState.assets.last, "Cone");
|
widget.controller
|
||||||
|
.reveal(widget.controller.scene.listEntities().last, "Cone");
|
||||||
} else {
|
} else {
|
||||||
widget.controller.hide(ExampleWidgetState.assets.last, "Cone");
|
widget.controller
|
||||||
|
.hide(widget.controller.scene.listEntities().last, "Cone");
|
||||||
}
|
}
|
||||||
|
|
||||||
ExampleWidgetState.coneHidden = !ExampleWidgetState.coneHidden;
|
ExampleWidgetState.coneHidden = !ExampleWidgetState.coneHidden;
|
||||||
@@ -135,7 +134,10 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
|
|||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
widget.controller.setMaterialColor(
|
widget.controller.setMaterialColor(
|
||||||
ExampleWidgetState.assets.last, "Cone", 0, Colors.purple);
|
widget.controller.scene.listEntities().last,
|
||||||
|
"Cone",
|
||||||
|
0,
|
||||||
|
Colors.purple);
|
||||||
},
|
},
|
||||||
child: const Text("Set cone material color to purple")),
|
child: const Text("Set cone material color to purple")),
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
@@ -150,31 +152,47 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _geometrySubmenu() {
|
Widget _geometrySubmenu() {
|
||||||
return MenuItemButton(
|
return SubmenuButton(
|
||||||
onPressed: () async {
|
menuChildren: [
|
||||||
await widget.controller.createGeometry([
|
MenuItemButton(
|
||||||
-1,
|
onPressed: () async {
|
||||||
0,
|
var verts = [
|
||||||
-1,
|
-1.0,
|
||||||
-1,
|
0.0,
|
||||||
0,
|
-1.0,
|
||||||
1,
|
-1.0,
|
||||||
1,
|
0.0,
|
||||||
0,
|
1.0,
|
||||||
1,
|
1.0,
|
||||||
1,
|
0.0,
|
||||||
0,
|
1.0,
|
||||||
-1,
|
1.0,
|
||||||
], [
|
0.0,
|
||||||
0,
|
-1.0,
|
||||||
1,
|
];
|
||||||
2,
|
var indices = [0, 1, 2, 2, 3, 0];
|
||||||
2,
|
var geom = await widget.controller.createGeometry(verts, indices,
|
||||||
3,
|
materialPath: "asset://assets/solidcolor.filamat");
|
||||||
0
|
},
|
||||||
], "asset://assets/solidcolor.filamat");
|
child: const Text("Quad")),
|
||||||
},
|
MenuItemButton(
|
||||||
child: const Text("Custom geometry"));
|
onPressed: () async {
|
||||||
|
await widget.controller.createGeometry([
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
], [
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
], primitiveType: PrimitiveType.LINES);
|
||||||
|
},
|
||||||
|
child: const Text("Line"))
|
||||||
|
],
|
||||||
|
child: const Text("Custom Geometry"),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -185,50 +203,24 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
|
|||||||
_geometrySubmenu(),
|
_geometrySubmenu(),
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
ExampleWidgetState.directionalLight = await widget.controller
|
await widget.controller
|
||||||
.addLight(1, 6500, 150000, 0, 1, 0, 0, -1, 0, true);
|
.addLight(1, 6500, 150000, 0, 1, 0, 0, -1, 0, true);
|
||||||
},
|
},
|
||||||
child: const Text("Add directional light"),
|
child: const Text("Add directional light"),
|
||||||
),
|
),
|
||||||
|
MenuItemButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await widget.controller
|
||||||
|
.addLight(2, 6500, 150000, 0, 1, 0, 0, -1, 0, true);
|
||||||
|
},
|
||||||
|
child: const Text("Add point light"),
|
||||||
|
),
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await widget.controller.clearLights();
|
await widget.controller.clearLights();
|
||||||
},
|
},
|
||||||
child: const Text("Clear lights"),
|
child: const Text("Clear lights"),
|
||||||
),
|
),
|
||||||
MenuItemButton(
|
|
||||||
onPressed: () async {
|
|
||||||
if (ExampleWidgetState.buster == null) {
|
|
||||||
ExampleWidgetState.buster = await widget.controller.loadGltf(
|
|
||||||
"assets/BusterDrone/scene.gltf", "assets/BusterDrone",
|
|
||||||
force: true);
|
|
||||||
await widget.controller
|
|
||||||
.playAnimation(ExampleWidgetState.buster!, 0, loop: true);
|
|
||||||
} else {
|
|
||||||
await widget.controller
|
|
||||||
.removeEntity(ExampleWidgetState.buster!);
|
|
||||||
ExampleWidgetState.buster = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Text(ExampleWidgetState.buster == null
|
|
||||||
? 'Load buster'
|
|
||||||
: 'Remove buster')),
|
|
||||||
MenuItemButton(
|
|
||||||
onPressed: () async {
|
|
||||||
if (ExampleWidgetState.flightHelmet == null) {
|
|
||||||
ExampleWidgetState.flightHelmet ??= await widget.controller
|
|
||||||
.loadGltf('assets/FlightHelmet/FlightHelmet.gltf',
|
|
||||||
'assets/FlightHelmet',
|
|
||||||
force: true);
|
|
||||||
} else {
|
|
||||||
await widget.controller
|
|
||||||
.removeEntity(ExampleWidgetState.flightHelmet!);
|
|
||||||
ExampleWidgetState.flightHelmet = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Text(ExampleWidgetState.flightHelmet == null
|
|
||||||
? 'Load flight helmet'
|
|
||||||
: 'Remove flight helmet')),
|
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
widget.controller.setBackgroundColor(const Color(0xAA73C9FA));
|
widget.controller.setBackgroundColor(const Color(0xAA73C9FA));
|
||||||
@@ -272,9 +264,6 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
|
|||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await widget.controller.clearEntities();
|
await widget.controller.clearEntities();
|
||||||
ExampleWidgetState.flightHelmet = null;
|
|
||||||
ExampleWidgetState.buster = null;
|
|
||||||
ExampleWidgetState.assets.clear();
|
|
||||||
},
|
},
|
||||||
child: const Text('Clear assets')),
|
child: const Text('Clear assets')),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import 'dart:math';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:vector_math/vector_math_64.dart' as v;
|
import 'package:vector_math/vector_math_64.dart' as v;
|
||||||
import 'package:flutter_filament/filament_controller.dart';
|
import 'package:flutter_filament/flutter_filament.dart';
|
||||||
|
|
||||||
import 'package:flutter_filament_example/main.dart';
|
import 'package:flutter_filament_example/main.dart';
|
||||||
|
|
||||||
class CameraSubmenu extends StatefulWidget {
|
class CameraSubmenu extends StatefulWidget {
|
||||||
@@ -104,20 +105,6 @@ class _CameraSubmenuState extends State<CameraSubmenu> {
|
|||||||
},
|
},
|
||||||
child: const Text('Move to 0,0,0, facing towards 0,0,-1'),
|
child: const Text('Move to 0,0,0, facing towards 0,0,-1'),
|
||||||
),
|
),
|
||||||
MenuItemButton(
|
|
||||||
onPressed: () async {
|
|
||||||
await widget.controller
|
|
||||||
.setCamera(ExampleWidgetState.assets.last!, null);
|
|
||||||
},
|
|
||||||
child: const Text('Set to first camera in last added asset'),
|
|
||||||
),
|
|
||||||
MenuItemButton(
|
|
||||||
onPressed: () async {
|
|
||||||
await widget.controller
|
|
||||||
.moveCameraToAsset(ExampleWidgetState.assets.last!);
|
|
||||||
},
|
|
||||||
child: const Text("Move to last added asset"),
|
|
||||||
),
|
|
||||||
MenuItemButton(
|
MenuItemButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
widget.controller.setCameraRotation(
|
widget.controller.setCameraRotation(
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_filament/filament_controller.dart';
|
|
||||||
import 'package:flutter_filament/filament_controller_ffi.dart';
|
import 'package:flutter_filament/flutter_filament.dart';
|
||||||
|
|
||||||
class ControllerMenu extends StatefulWidget {
|
class ControllerMenu extends StatefulWidget {
|
||||||
final FilamentController? controller;
|
final FilamentController? controller;
|
||||||
|
final void Function() onToggleViewport;
|
||||||
final void Function(FilamentController controller) onControllerCreated;
|
final void Function(FilamentController controller) onControllerCreated;
|
||||||
final void Function() onControllerDestroyed;
|
final void Function() onControllerDestroyed;
|
||||||
final FocusNode sharedFocusNode;
|
final FocusNode sharedFocusNode;
|
||||||
@@ -15,7 +16,8 @@ class ControllerMenu extends StatefulWidget {
|
|||||||
{this.controller,
|
{this.controller,
|
||||||
required this.onControllerCreated,
|
required this.onControllerCreated,
|
||||||
required this.onControllerDestroyed,
|
required this.onControllerDestroyed,
|
||||||
required this.sharedFocusNode});
|
required this.sharedFocusNode,
|
||||||
|
required this.onToggleViewport});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StatefulWidget> createState() => _ControllerMenuState();
|
State<StatefulWidget> createState() => _ControllerMenuState();
|
||||||
@@ -60,6 +62,7 @@ class _ControllerMenuState extends State<ControllerMenu> {
|
|||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
await _filamentController!.createViewer();
|
await _filamentController!.createViewer();
|
||||||
|
|
||||||
await _filamentController!
|
await _filamentController!
|
||||||
.setCameraManipulatorOptions(zoomSpeed: 1.0);
|
.setCameraManipulatorOptions(zoomSpeed: 1.0);
|
||||||
},
|
},
|
||||||
@@ -100,7 +103,13 @@ class _ControllerMenuState extends State<ControllerMenu> {
|
|||||||
}
|
}
|
||||||
return MenuAnchor(
|
return MenuAnchor(
|
||||||
childFocusNode: widget.sharedFocusNode,
|
childFocusNode: widget.sharedFocusNode,
|
||||||
menuChildren: items,
|
menuChildren: items +
|
||||||
|
[
|
||||||
|
TextButton(
|
||||||
|
child: const Text("Toggle viewport size"),
|
||||||
|
onPressed: widget.onToggleViewport,
|
||||||
|
)
|
||||||
|
],
|
||||||
builder:
|
builder:
|
||||||
(BuildContext context, MenuController controller, Widget? child) {
|
(BuildContext context, MenuController controller, Widget? child) {
|
||||||
return TextButton(
|
return TextButton(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_filament/filament_controller.dart';
|
import 'package:flutter_filament/flutter_filament.dart';
|
||||||
import 'package:flutter_filament_example/main.dart';
|
import 'package:flutter_filament_example/main.dart';
|
||||||
|
|
||||||
class RenderingSubmenu extends StatefulWidget {
|
class RenderingSubmenu extends StatefulWidget {
|
||||||
@@ -64,6 +64,13 @@ class _RenderingSubmenuState extends State<RenderingSubmenu> {
|
|||||||
child: Text(
|
child: Text(
|
||||||
"Turn recording ${ExampleWidgetState.recording ? "OFF" : "ON"}) "),
|
"Turn recording ${ExampleWidgetState.recording ? "OFF" : "ON"}) "),
|
||||||
),
|
),
|
||||||
|
MenuItemButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await widget.controller
|
||||||
|
.addLight(2, 6000, 100000, 0, 0, 0, 0, 1, 0, false);
|
||||||
|
},
|
||||||
|
child: Text("Add light"),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: const Text("Rendering"),
|
child: const Text("Rendering"),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:flutter_filament/filament_controller.dart';
|
import 'package:flutter_filament/flutter_filament.dart';
|
||||||
import 'package:flutter_filament_example/menus/asset_submenu.dart';
|
import 'package:flutter_filament_example/menus/asset_submenu.dart';
|
||||||
import 'package:flutter_filament_example/menus/camera_submenu.dart';
|
import 'package:flutter_filament_example/menus/camera_submenu.dart';
|
||||||
import 'package:flutter_filament_example/menus/rendering_submenu.dart';
|
import 'package:flutter_filament_example/menus/rendering_submenu.dart';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_filament/filament_controller.dart';
|
import 'package:flutter_filament/flutter_filament.dart';
|
||||||
|
|
||||||
class PickerResultWidget extends StatelessWidget {
|
class PickerResultWidget extends StatelessWidget {
|
||||||
final FilamentController controller;
|
final FilamentController controller;
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ namespace flutter_filament
|
|||||||
void setAntiAliasing(bool msaaEnabled, bool fxaaEnabled, bool taaEnabled);
|
void setAntiAliasing(bool msaaEnabled, bool fxaaEnabled, bool taaEnabled);
|
||||||
void setDepthOfField();
|
void setDepthOfField();
|
||||||
|
|
||||||
EntityId createGeometry(float* vertices, uint32_t numVertices, uint16_t* indices, uint32_t numIndices, const char* materialPath);
|
EntityId createGeometry(float* vertices, uint32_t numVertices, uint16_t* indices, uint32_t numIndices, filament::RenderableManager::PrimitiveType primitiveType = RenderableManager::PrimitiveType::TRIANGLES, const char* materialPath = nullptr);
|
||||||
|
|
||||||
SceneManager *const getSceneManager()
|
SceneManager *const getSceneManager()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -197,9 +197,11 @@ extern "C"
|
|||||||
FLUTTER_PLUGIN_EXPORT void remove_collision_component(void *const sceneManager, EntityId entityId);
|
FLUTTER_PLUGIN_EXPORT void remove_collision_component(void *const sceneManager, EntityId entityId);
|
||||||
FLUTTER_PLUGIN_EXPORT void add_animation_component(void *const sceneManager, EntityId entityId);
|
FLUTTER_PLUGIN_EXPORT void add_animation_component(void *const sceneManager, EntityId entityId);
|
||||||
|
|
||||||
FLUTTER_PLUGIN_EXPORT EntityId create_geometry(void *const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath);
|
FLUTTER_PLUGIN_EXPORT EntityId create_geometry(void *const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, int primitiveType, const char* materialPath);
|
||||||
FLUTTER_PLUGIN_EXPORT void set_parent(void *const sceneManager, EntityId child, EntityId parent);
|
FLUTTER_PLUGIN_EXPORT void set_parent(void *const sceneManager, EntityId child, EntityId parent);
|
||||||
FLUTTER_PLUGIN_EXPORT void test_collisions(void *const sceneManager, EntityId entity);
|
FLUTTER_PLUGIN_EXPORT void test_collisions(void *const sceneManager, EntityId entity);
|
||||||
|
FLUTTER_PLUGIN_EXPORT void set_priority(void *const sceneManager, EntityId entityId, int priority);
|
||||||
|
FLUTTER_PLUGIN_EXPORT void get_gizmo(void *const sceneManager, EntityId* out);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ extern "C"
|
|||||||
FLUTTER_PLUGIN_EXPORT void set_post_processing_ffi(void *const viewer, bool enabled);
|
FLUTTER_PLUGIN_EXPORT void set_post_processing_ffi(void *const viewer, bool enabled);
|
||||||
FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose_ffi(void *const sceneManager, EntityId entityId);
|
FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose_ffi(void *const sceneManager, EntityId entityId);
|
||||||
FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi();
|
FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi();
|
||||||
FLUTTER_PLUGIN_EXPORT void create_geometry_ffi(void *const viewer, float *vertices, int numVertices, uint16_t *indices, int numIndices, const char *materialPath, void (*callback)(EntityId));
|
FLUTTER_PLUGIN_EXPORT void create_geometry_ffi(void *const viewer, float *vertices, int numVertices, uint16_t *indices, int numIndices, int primitiveType, const char *materialPath, void (*callback)(EntityId));
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,10 @@
|
|||||||
#include <gltfio/FilamentInstance.h>
|
#include <gltfio/FilamentInstance.h>
|
||||||
#include <gltfio/ResourceLoader.h>
|
#include <gltfio/ResourceLoader.h>
|
||||||
|
|
||||||
|
#include <filament/IndexBuffer.h>
|
||||||
|
#include <filament/InstanceBuffer.h>
|
||||||
|
|
||||||
|
#include "material/gizmo.h"
|
||||||
#include "utils/NameComponentManager.h"
|
#include "utils/NameComponentManager.h"
|
||||||
#include "ResourceBuffer.hpp"
|
#include "ResourceBuffer.hpp"
|
||||||
#include "components/CollisionComponentManager.hpp"
|
#include "components/CollisionComponentManager.hpp"
|
||||||
@@ -141,6 +145,19 @@ namespace flutter_filament
|
|||||||
/// @param entityId
|
/// @param entityId
|
||||||
void getInstances(EntityId entityId, EntityId* out);
|
void getInstances(EntityId entityId, EntityId* out);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Sets the draw priority for the given entity. See RenderableManager.h for more details.
|
||||||
|
///
|
||||||
|
void setPriority(EntityId entity, int priority);
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief returns the gizmo entity, used to manipulate the global transform for entities
|
||||||
|
/// @param out a pointer to an array of three EntityId {x, y, z}
|
||||||
|
/// @return
|
||||||
|
///
|
||||||
|
void getGizmo(EntityId* out);
|
||||||
|
|
||||||
|
|
||||||
friend class FilamentViewer;
|
friend class FilamentViewer;
|
||||||
|
|
||||||
|
|
||||||
@@ -154,6 +171,7 @@ namespace flutter_filament
|
|||||||
gltfio::TextureProvider *_stbDecoder = nullptr;
|
gltfio::TextureProvider *_stbDecoder = nullptr;
|
||||||
gltfio::TextureProvider *_ktxDecoder = nullptr;
|
gltfio::TextureProvider *_ktxDecoder = nullptr;
|
||||||
std::mutex _mutex;
|
std::mutex _mutex;
|
||||||
|
Material* _gizmoMaterial;
|
||||||
|
|
||||||
utils::NameComponentManager* _ncm;
|
utils::NameComponentManager* _ncm;
|
||||||
|
|
||||||
@@ -173,5 +191,10 @@ namespace flutter_filament
|
|||||||
const gltfio::FilamentInstance* instance,
|
const gltfio::FilamentInstance* instance,
|
||||||
const char *entityName);
|
const char *entityName);
|
||||||
|
|
||||||
|
EntityId addGizmo();
|
||||||
|
utils::Entity _gizmoX;
|
||||||
|
utils::Entity _gizmoY;
|
||||||
|
utils::Entity _gizmoZ;
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,6 +115,8 @@ namespace flutter_filament {
|
|||||||
{
|
{
|
||||||
auto now = high_resolution_clock::now();
|
auto now = high_resolution_clock::now();
|
||||||
|
|
||||||
|
// Log("animation component count : %d", )
|
||||||
|
|
||||||
for(auto it = begin(); it < end(); it++) {
|
for(auto it = begin(); it < end(); it++) {
|
||||||
const auto& entity = getEntity(it);
|
const auto& entity = getEntity(it);
|
||||||
|
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
#ifndef FILE_MATERIAL_PROVIDER
|
|
||||||
#define FILE_MATERIAL_PROVIDER
|
|
||||||
|
|
||||||
#include <filament/gltfio/MaterialProvider.h>
|
|
||||||
#include <filament/Texture.h>
|
|
||||||
#include <filament/TextureSampler.h>
|
|
||||||
#include <math/mat4.h>
|
|
||||||
#include <math/vec3.h>
|
|
||||||
#include <math/vec4.h>
|
|
||||||
#include <math/mat3.h>
|
|
||||||
#include <math/norm.h>
|
|
||||||
#include "Log.hpp"
|
|
||||||
|
|
||||||
namespace flutter_filament {
|
|
||||||
|
|
||||||
|
|
||||||
class FileMaterialProvider : public filament::gltfio::MaterialProvider {
|
|
||||||
|
|
||||||
filament::Material* _m;
|
|
||||||
const filament::Material* _ms[1];
|
|
||||||
filament::Texture* mDummyTexture = nullptr;
|
|
||||||
|
|
||||||
public:
|
|
||||||
FileMaterialProvider(filament::Engine* engine, const void* const data, const size_t size) {
|
|
||||||
_m = filament::Material::Builder()
|
|
||||||
.package(data, size)
|
|
||||||
.build(*engine);
|
|
||||||
_ms[0] = _m;
|
|
||||||
unsigned char texels[4] = {};
|
|
||||||
mDummyTexture = filament::Texture::Builder()
|
|
||||||
.width(1).height(1)
|
|
||||||
.format(filament::Texture::InternalFormat::RGBA8)
|
|
||||||
.build(*engine);
|
|
||||||
filament::Texture::PixelBufferDescriptor pbd(texels, sizeof(texels), filament::Texture::Format::RGBA,
|
|
||||||
filament::Texture::Type::UBYTE);
|
|
||||||
mDummyTexture->setImage(*engine, 0, std::move(pbd));
|
|
||||||
}
|
|
||||||
|
|
||||||
filament::MaterialInstance* createMaterialInstance(filament::gltfio::MaterialKey* config, filament::gltfio::UvMap* uvmap,
|
|
||||||
const char* label = "material", const char* extras = nullptr) {
|
|
||||||
|
|
||||||
auto getUvIndex = [uvmap](uint8_t srcIndex, bool hasTexture) -> int {
|
|
||||||
return hasTexture ? int(uvmap->at(srcIndex)) - 1 : -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto instance = _m->createInstance();
|
|
||||||
filament::math::mat3f identity;
|
|
||||||
instance->setParameter("baseColorUvMatrix", identity);
|
|
||||||
instance->setParameter("normalUvMatrix", identity);
|
|
||||||
|
|
||||||
instance->setParameter("baseColorIndex", getUvIndex(config->baseColorUV, config->hasBaseColorTexture));
|
|
||||||
instance->setParameter("normalIndex", getUvIndex(config->normalUV, config->hasNormalTexture));
|
|
||||||
if(config->hasNormalTexture) {
|
|
||||||
filament::TextureSampler sampler;
|
|
||||||
instance->setParameter("normalMap", mDummyTexture, sampler);
|
|
||||||
instance->setParameter("baseColorMap", mDummyTexture, sampler);
|
|
||||||
} else {
|
|
||||||
Log("No normal texture for specified material.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates or fetches a compiled Filament material corresponding to the given config.
|
|
||||||
*/
|
|
||||||
virtual filament::Material* getMaterial(filament::gltfio::MaterialKey* config, filament::gltfio::UvMap* uvmap, const char* label = "material") {
|
|
||||||
return _m;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a weak reference to the array of cached materials.
|
|
||||||
*/
|
|
||||||
const filament::Material* const* getMaterials() const noexcept {
|
|
||||||
return _ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the number of cached materials.
|
|
||||||
*/
|
|
||||||
size_t getMaterialsCount() const noexcept {
|
|
||||||
return (size_t)1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroyMaterials() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bool needsDummyData(filament::VertexAttribute attrib) const noexcept {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
12
ios/include/material/gizmo.S
Normal file
12
ios/include/material/gizmo.S
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
.global GIZMO_GIZMO_OFFSET;
|
||||||
|
.global GIZMO_GIZMO_SIZE;
|
||||||
|
|
||||||
|
.global GIZMO_PACKAGE
|
||||||
|
.section .rodata
|
||||||
|
GIZMO_PACKAGE:
|
||||||
|
.incbin "gizmo.bin"
|
||||||
|
GIZMO_GIZMO_OFFSET:
|
||||||
|
.int 0
|
||||||
|
GIZMO_GIZMO_SIZE:
|
||||||
|
.int 36751
|
||||||
|
|
||||||
12
ios/include/material/gizmo.apple.S
Normal file
12
ios/include/material/gizmo.apple.S
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
.global _GIZMO_GIZMO_OFFSET;
|
||||||
|
.global _GIZMO_GIZMO_SIZE;
|
||||||
|
|
||||||
|
.global _GIZMO_PACKAGE
|
||||||
|
.section __TEXT,__const
|
||||||
|
_GIZMO_PACKAGE:
|
||||||
|
.incbin "gizmo.bin"
|
||||||
|
_GIZMO_GIZMO_OFFSET:
|
||||||
|
.int 0
|
||||||
|
_GIZMO_GIZMO_SIZE:
|
||||||
|
.int 36751
|
||||||
|
|
||||||
3
ios/include/material/gizmo.bin
Normal file
3
ios/include/material/gizmo.bin
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:bbb413c69ac6f77243a02049d2ccf494a1f20ea88fb8ca159b22c5bc0a4fdd95
|
||||||
|
size 36751
|
||||||
1847
ios/include/material/gizmo.c
Normal file
1847
ios/include/material/gizmo.c
Normal file
File diff suppressed because it is too large
Load Diff
13
ios/include/material/gizmo.h
Normal file
13
ios/include/material/gizmo.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#ifndef GIZMO_H_
|
||||||
|
#define GIZMO_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
extern const uint8_t GIZMO_PACKAGE[];
|
||||||
|
extern int GIZMO_GIZMO_OFFSET;
|
||||||
|
extern int GIZMO_GIZMO_SIZE;
|
||||||
|
}
|
||||||
|
#define GIZMO_GIZMO_DATA (GIZMO_PACKAGE + GIZMO_GIZMO_OFFSET)
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -248,6 +248,7 @@ namespace flutter_filament
|
|||||||
.build(*_engine, imageEntity);
|
.build(*_engine, imageEntity);
|
||||||
_imageEntity = &imageEntity;
|
_imageEntity = &imageEntity;
|
||||||
_scene->addEntity(imageEntity);
|
_scene->addEntity(imageEntity);
|
||||||
|
Log("Added imageEntity %d", imageEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilamentViewer::setAntiAliasing(bool msaa, bool fxaa, bool taa) {
|
void FilamentViewer::setAntiAliasing(bool msaa, bool fxaa, bool taa) {
|
||||||
@@ -320,6 +321,9 @@ namespace flutter_filament
|
|||||||
int32_t FilamentViewer::addLight(LightManager::Type t, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows)
|
int32_t FilamentViewer::addLight(LightManager::Type t, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows)
|
||||||
{
|
{
|
||||||
auto light = EntityManager::get().create();
|
auto light = EntityManager::get().create();
|
||||||
|
auto& transformManager = _engine->getTransformManager();
|
||||||
|
transformManager.create(light);
|
||||||
|
auto parent = transformManager.getInstance(light);
|
||||||
auto builder = LightManager::Builder(t)
|
auto builder = LightManager::Builder(t)
|
||||||
.color(Color::cct(colour))
|
.color(Color::cct(colour))
|
||||||
.intensity(intensity)
|
.intensity(intensity)
|
||||||
@@ -329,6 +333,7 @@ namespace flutter_filament
|
|||||||
.build(*_engine, light);
|
.build(*_engine, light);
|
||||||
_scene->addEntity(light);
|
_scene->addEntity(light);
|
||||||
_lights.push_back(light);
|
_lights.push_back(light);
|
||||||
|
|
||||||
auto entityId = Entity::smuggle(light);
|
auto entityId = Entity::smuggle(light);
|
||||||
Log("Added light under entity ID %d of type %d with colour %f intensity %f at (%f, %f, %f) with direction (%f, %f, %f) with shadows %d", entityId, t, colour, intensity, posX, posY, posZ, dirX, dirY, dirZ, shadows);
|
Log("Added light under entity ID %d of type %d with colour %f intensity %f at (%f, %f, %f) with direction (%f, %f, %f) with shadows %d", entityId, t, colour, intensity, posX, posY, posZ, dirX, dirY, dirZ, shadows);
|
||||||
return entityId;
|
return entityId;
|
||||||
@@ -1450,7 +1455,7 @@ namespace flutter_filament
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityId FilamentViewer::createGeometry(float *vertices, uint32_t numVertices, uint16_t *indices, uint32_t numIndices, const char* materialPath)
|
EntityId FilamentViewer::createGeometry(float *vertices, uint32_t numVertices, uint16_t *indices, uint32_t numIndices, RenderableManager::PrimitiveType primitiveType, const char* materialPath)
|
||||||
{
|
{
|
||||||
|
|
||||||
float *verticesCopy = (float*)malloc(numVertices * sizeof(float));
|
float *verticesCopy = (float*)malloc(numVertices * sizeof(float));
|
||||||
@@ -1499,7 +1504,7 @@ namespace flutter_filament
|
|||||||
RenderableManager::Builder builder = RenderableManager::Builder(1);
|
RenderableManager::Builder builder = RenderableManager::Builder(1);
|
||||||
builder
|
builder
|
||||||
.boundingBox({{minX, minY, minZ}, {maxX, maxY, maxZ}})
|
.boundingBox({{minX, minY, minZ}, {maxX, maxY, maxZ}})
|
||||||
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES,
|
.geometry(0, primitiveType,
|
||||||
vb, ib, 0, numIndices)
|
vb, ib, 0, numIndices)
|
||||||
.culling(false)
|
.culling(false)
|
||||||
.receiveShadows(false)
|
.receiveShadows(false)
|
||||||
@@ -1510,7 +1515,8 @@ namespace flutter_filament
|
|||||||
auto result = builder.build(*_engine, renderable);
|
auto result = builder.build(*_engine, renderable);
|
||||||
|
|
||||||
_scene->addEntity(renderable);
|
_scene->addEntity(renderable);
|
||||||
Log("Created geometry with result %d", result);
|
|
||||||
|
Log("Created geometry with primitive type %d (result %d)", primitiveType, result);
|
||||||
|
|
||||||
return Entity::smuggle(renderable);
|
return Entity::smuggle(renderable);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -597,8 +597,15 @@ extern "C"
|
|||||||
((SceneManager*)sceneManager)->addAnimationComponent(entityId);
|
((SceneManager*)sceneManager)->addAnimationComponent(entityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
FLUTTER_PLUGIN_EXPORT EntityId create_geometry(void *const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath) {
|
FLUTTER_PLUGIN_EXPORT EntityId create_geometry(void *const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, int primitiveType, const char* materialPath) {
|
||||||
return ((FilamentViewer*)viewer)->createGeometry(vertices, (uint32_t)numVertices, indices, numIndices, materialPath);
|
return ((FilamentViewer*)viewer)->createGeometry(
|
||||||
|
vertices,
|
||||||
|
(uint32_t)numVertices,
|
||||||
|
indices,
|
||||||
|
numIndices,
|
||||||
|
(filament::RenderableManager::PrimitiveType)primitiveType,
|
||||||
|
materialPath
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
FLUTTER_PLUGIN_EXPORT EntityId find_child_entity_by_name(void *const sceneManager, const EntityId parent, const char* name) {
|
FLUTTER_PLUGIN_EXPORT EntityId find_child_entity_by_name(void *const sceneManager, const EntityId parent, const char* name) {
|
||||||
@@ -614,4 +621,12 @@ extern "C"
|
|||||||
((SceneManager*)sceneManager)->testCollisions(entity);
|
((SceneManager*)sceneManager)->testCollisions(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FLUTTER_PLUGIN_EXPORT void set_priority(void *const sceneManager, EntityId entity, int priority) {
|
||||||
|
((SceneManager*)sceneManager)->setPriority(entity, priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
FLUTTER_PLUGIN_EXPORT void get_gizmo(void *const sceneManager, EntityId* out) {
|
||||||
|
return ((SceneManager*)sceneManager)->getGizmo(out);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ public:
|
|||||||
_t = new std::thread([this]() {
|
_t = new std::thread([this]() {
|
||||||
auto last = std::chrono::high_resolution_clock::now();
|
auto last = std::chrono::high_resolution_clock::now();
|
||||||
while (!_stop) {
|
while (!_stop) {
|
||||||
last = std::chrono::high_resolution_clock::now();
|
|
||||||
|
|
||||||
if (_rendering) {
|
if (_rendering) {
|
||||||
// auto frameStart = std::chrono::high_resolution_clock::now();
|
// auto frameStart = std::chrono::high_resolution_clock::now();
|
||||||
@@ -48,6 +47,8 @@ public:
|
|||||||
// auto frameEnd = std::chrono::high_resolution_clock::now();
|
// auto frameEnd = std::chrono::high_resolution_clock::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
last = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
auto now = std::chrono::high_resolution_clock::now();
|
auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
float elapsed = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - last).count());
|
float elapsed = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - last).count());
|
||||||
@@ -58,7 +59,6 @@ public:
|
|||||||
|
|
||||||
if(_tasks.empty()) {
|
if(_tasks.empty()) {
|
||||||
_cond.wait_for(lock, std::chrono::duration<float, std::milli>(1));
|
_cond.wait_for(lock, std::chrono::duration<float, std::milli>(1));
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
while(!_tasks.empty()) {
|
while(!_tasks.empty()) {
|
||||||
task = std::move(_tasks.front());
|
task = std::move(_tasks.front());
|
||||||
@@ -571,10 +571,10 @@ FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi(
|
|||||||
|
|
||||||
FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi() { Log("Dummy called"); }
|
FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi() { Log("Dummy called"); }
|
||||||
|
|
||||||
FLUTTER_PLUGIN_EXPORT void create_geometry_ffi(void* const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath, void (*callback)(EntityId) ) {
|
FLUTTER_PLUGIN_EXPORT void create_geometry_ffi(void* const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, int primitiveType, const char* materialPath, void (*callback)(EntityId) ) {
|
||||||
std::packaged_task<EntityId()> lambda(
|
std::packaged_task<EntityId()> lambda(
|
||||||
[=] {
|
[=] {
|
||||||
auto entity = create_geometry(viewer, vertices, numVertices, indices, numIndices, materialPath);
|
auto entity = create_geometry(viewer, vertices, numVertices, indices, numIndices, primitiveType, materialPath);
|
||||||
callback(entity);
|
callback(entity);
|
||||||
return entity;
|
return entity;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
#include "Log.hpp"
|
#include "Log.hpp"
|
||||||
#include "SceneManager.hpp"
|
#include "SceneManager.hpp"
|
||||||
|
|
||||||
#include "material/FileMaterialProvider.hpp"
|
|
||||||
#include "gltfio/materials/uberarchive.h"
|
#include "gltfio/materials/uberarchive.h"
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
@@ -88,6 +87,8 @@ namespace flutter_filament
|
|||||||
_collisionComponentManager = new CollisionComponentManager(tm);
|
_collisionComponentManager = new CollisionComponentManager(tm);
|
||||||
_animationComponentManager = new AnimationComponentManager(tm, _engine->getRenderableManager());
|
_animationComponentManager = new AnimationComponentManager(tm, _engine->getRenderableManager());
|
||||||
|
|
||||||
|
addGizmo();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SceneManager::~SceneManager()
|
SceneManager::~SceneManager()
|
||||||
@@ -307,8 +308,10 @@ namespace flutter_filament
|
|||||||
if(asset) {
|
if(asset) {
|
||||||
instance = asset->getInstance();
|
instance = asset->getInstance();
|
||||||
} else {
|
} else {
|
||||||
return false;
|
// Log("Failed to find glTF instance under entityID %d, hiding as regular entity", entityId);
|
||||||
}
|
_scene->remove(Entity::import(entityId));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::Entity entity;
|
utils::Entity entity;
|
||||||
@@ -342,7 +345,9 @@ namespace flutter_filament
|
|||||||
if(asset) {
|
if(asset) {
|
||||||
instance = asset->getInstance();
|
instance = asset->getInstance();
|
||||||
} else {
|
} else {
|
||||||
return false;
|
// Log("Failed to find glTF instance under entityID %d, revealing as regular entity", entityId);
|
||||||
|
_scene->addEntity(Entity::import(entityId));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1563,4 +1568,161 @@ namespace flutter_filament
|
|||||||
return _ncm->getName(inst);
|
return _ncm->getName(inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SceneManager::setPriority(EntityId entityId, int priority) {
|
||||||
|
auto& rm = _engine->getRenderableManager();
|
||||||
|
auto renderableInstance = rm.getInstance(Entity::import(entityId));
|
||||||
|
if(!renderableInstance.isValid()) {
|
||||||
|
Log("Error: invalid renderable, did you pass the correct entity?", priority);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rm.setPriority(renderableInstance, priority);
|
||||||
|
Log("Set instance renderable priority to %d", priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityId SceneManager::addGizmo() {
|
||||||
|
auto mat = _resourceLoaderWrapper->load("file:///Users/nickfisher/Documents/polyvox/flutter/flutter_filament/materials/gizmo.filamat");
|
||||||
|
_gizmoMaterial =
|
||||||
|
Material::Builder()
|
||||||
|
.package(mat.data, mat.size)
|
||||||
|
// .package(GIZMO_GIZMO_DATA, GIZMO_GIZMO_SIZE)
|
||||||
|
.build(*_engine);
|
||||||
|
|
||||||
|
auto vertexCount = 9;
|
||||||
|
|
||||||
|
float* vertices = new float[vertexCount * 3] {
|
||||||
|
-0.05, 0.0f, 0.05f,
|
||||||
|
0.05f, 0.0f, 0.05f,
|
||||||
|
0.05f, 0.0f, -0.05f,
|
||||||
|
-0.05f, 0.0f, -0.05f,
|
||||||
|
-0.05f, 1.0f, 0.05f,
|
||||||
|
0.05f, 1.0f, 0.05f,
|
||||||
|
0.05f, 1.0f, -0.05f,
|
||||||
|
-0.05f, 1.0f, -0.05f,
|
||||||
|
0.00f, 1.1f, 0.0f
|
||||||
|
};
|
||||||
|
|
||||||
|
VertexBuffer::BufferDescriptor::Callback vertexCallback = [](void *buf, size_t,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
free((void*)buf);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto indexCount = 42;
|
||||||
|
uint16_t* indices = new uint16_t[indexCount] {
|
||||||
|
//bottom quad
|
||||||
|
0,1,2,
|
||||||
|
0,2,3,
|
||||||
|
// top "cone"
|
||||||
|
4,5,8,
|
||||||
|
5,6,8,
|
||||||
|
4,7,8,
|
||||||
|
6,7,8,
|
||||||
|
// front
|
||||||
|
0,1,4,
|
||||||
|
1,5,4,
|
||||||
|
// right
|
||||||
|
1,2,5,
|
||||||
|
2,6,5,
|
||||||
|
// back
|
||||||
|
2,6,7,
|
||||||
|
7,3,2,
|
||||||
|
// left
|
||||||
|
0,4,7,
|
||||||
|
7,3,0
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
IndexBuffer::BufferDescriptor::Callback indexCallback = [](void *buf, size_t,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
free((void*)buf);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto vb = VertexBuffer::Builder()
|
||||||
|
.vertexCount(vertexCount)
|
||||||
|
.bufferCount(1)
|
||||||
|
.attribute(
|
||||||
|
VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3)
|
||||||
|
.build(*_engine);
|
||||||
|
|
||||||
|
vb->setBufferAt(
|
||||||
|
*_engine,
|
||||||
|
0,
|
||||||
|
VertexBuffer::BufferDescriptor(vertices, vb->getVertexCount() * sizeof(filament::math::float3), 0, vertexCallback)
|
||||||
|
);
|
||||||
|
|
||||||
|
auto ib = IndexBuffer::Builder().indexCount(indexCount).bufferType(IndexBuffer::IndexType::USHORT).build(*_engine);
|
||||||
|
ib->setBuffer(*_engine, IndexBuffer::BufferDescriptor(indices, ib->getIndexCount() * sizeof(uint16_t), 0, indexCallback));
|
||||||
|
|
||||||
|
auto &entityManager = EntityManager::get();
|
||||||
|
|
||||||
|
_gizmoY = entityManager.create();
|
||||||
|
auto materialY = _gizmoMaterial->createInstance();
|
||||||
|
materialY->setParameter("color", math::float3 { 1.0f, 0.0f, 0.0f });
|
||||||
|
RenderableManager::Builder(1)
|
||||||
|
.boundingBox({{}, {1.0f, 1.0f, 1.0f}})
|
||||||
|
.material(0, materialY)
|
||||||
|
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb,
|
||||||
|
ib, 0, indexCount)
|
||||||
|
.culling(false)
|
||||||
|
.build(*_engine, _gizmoY);
|
||||||
|
|
||||||
|
_gizmoX = entityManager.create();
|
||||||
|
auto materialX = _gizmoMaterial->createInstance();
|
||||||
|
materialX->setParameter("color", math::float3 { 0.0f, 1.0f, 0.0f });
|
||||||
|
auto xTransform = math::mat4f::translation(math::float3 { 0.0f, 0.05f, -0.05f}) * math::mat4f::rotation(-math::F_PI_2, math::float3 { 0, 0, 1 });
|
||||||
|
auto* instanceBufferX = InstanceBuffer::Builder(1).localTransforms(&xTransform).build(*_engine);
|
||||||
|
RenderableManager::Builder(1)
|
||||||
|
.boundingBox({{}, {1.0f, 1.0f, 1.0f}})
|
||||||
|
.instances(1, instanceBufferX)
|
||||||
|
.material(0, materialX)
|
||||||
|
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb,
|
||||||
|
ib, 0, indexCount)
|
||||||
|
.culling(false)
|
||||||
|
.build(*_engine, _gizmoX);
|
||||||
|
|
||||||
|
_gizmoZ = entityManager.create();
|
||||||
|
auto materialZ = _gizmoMaterial->createInstance();
|
||||||
|
materialZ->setParameter("color", math::float3 { 0.0f, 0.0f, 1.0f });
|
||||||
|
auto zTransform = math::mat4f::translation(math::float3 { 0.0f, 0.05f, -0.05f}) * math::mat4f::rotation(3 * math::F_PI_2, math::float3 { 1, 0, 0 });
|
||||||
|
auto* instanceBufferZ = InstanceBuffer::Builder(1).localTransforms(&zTransform).build(*_engine);
|
||||||
|
RenderableManager::Builder(1)
|
||||||
|
.boundingBox({{}, {1.0f, 1.0f, 1.0f}})
|
||||||
|
.instances(1, instanceBufferZ)
|
||||||
|
.material(0, materialZ)
|
||||||
|
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb,
|
||||||
|
ib, 0, indexCount)
|
||||||
|
.culling(false)
|
||||||
|
.build(*_engine, _gizmoZ);
|
||||||
|
|
||||||
|
|
||||||
|
// auto localTransforms = math::mat4f[3] {
|
||||||
|
// math::mat4f(),
|
||||||
|
// math::mat4f::translation(math::float3 { 0.0f, 0.05f, -0.05f}) * math::mat4f::rotation(3 * math::F_PI_2, math::float3 { 1, 0, 0 }) ,
|
||||||
|
// math::mat4f::translation(math::float3 { 0.0f, 0.05f, -0.05f}) * math::mat4f::rotation(math::F_PI_2, math::float3 { 0, 0, 1 })
|
||||||
|
// };
|
||||||
|
|
||||||
|
|
||||||
|
// RenderableManager::Builder(1)
|
||||||
|
// .boundingBox({{}, {1.0f, 1.0f, 1.0f}})
|
||||||
|
// .instances(3, instanceBuffer)
|
||||||
|
// .material(0, _gizmoMaterial->getDefaultInstance())
|
||||||
|
// .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb,
|
||||||
|
// ib, 0, indexCount)
|
||||||
|
// .culling(false)
|
||||||
|
// .build(*_engine, _gizmo);
|
||||||
|
|
||||||
|
auto& rm = _engine->getRenderableManager();
|
||||||
|
rm.setPriority(rm.getInstance(_gizmoX), 7);
|
||||||
|
rm.setPriority(rm.getInstance(_gizmoY), 7);
|
||||||
|
rm.setPriority(rm.getInstance(_gizmoZ), 7);
|
||||||
|
return Entity::smuggle(_gizmoX);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneManager::getGizmo(EntityId* out) {
|
||||||
|
out[0] = Entity::smuggle(_gizmoX);
|
||||||
|
out[1] = Entity::smuggle(_gizmoY);
|
||||||
|
out[2] = Entity::smuggle(_gizmoZ);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace flutter_filament
|
} // namespace flutter_filament
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import 'dart:html';
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
import 'dart:web_gl';
|
import 'dart:web_gl';
|
||||||
import 'package:wasm_ffi/wasm_ffi.dart';
|
import 'package:wasm_ffi/wasm_ffi.dart';
|
||||||
import 'generated_bindings_web.dart';
|
import 'filament/generated_bindings_web.dart';
|
||||||
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
|
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:flutter_filament/animations/animation_data.dart';
|
import 'package:flutter_filament/filament/animations/animation_data.dart';
|
||||||
import 'package:vector_math/vector_math.dart';
|
import 'package:vector_math/vector_math.dart';
|
||||||
|
|
||||||
class AnimationBuilder {
|
class AnimationBuilder {
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'dart:io';
|
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'dart:ui';
|
|
||||||
import 'package:flutter_filament/animations/animation_data.dart';
|
import 'package:flutter_filament/filament/animations/animation_data.dart';
|
||||||
import 'package:vector_math/vector_math_64.dart';
|
import 'package:vector_math/vector_math_64.dart';
|
||||||
|
|
||||||
enum RotationMode { ZYX, XYZ }
|
enum RotationMode { ZYX, XYZ }
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter_filament/filament/filament_controller.dart';
|
||||||
import 'package:flutter_filament/filament_controller.dart';
|
import 'package:flutter_filament/filament/utils/hardware_keyboard_listener.dart';
|
||||||
import 'package:vector_math/vector_math_64.dart' as v;
|
import 'package:vector_math/vector_math_64.dart' as v;
|
||||||
|
|
||||||
class EntityTransformController {
|
class EntityTransformController {
|
||||||
@@ -178,4 +178,26 @@ class EntityTransformController {
|
|||||||
void mouse2Up() async {}
|
void mouse2Up() async {}
|
||||||
|
|
||||||
void mouse2Down() async {}
|
void mouse2Down() async {}
|
||||||
|
static HardwareKeyboardListener? _keyboardListener;
|
||||||
|
|
||||||
|
static Future<EntityTransformController> create(
|
||||||
|
FilamentController controller, FilamentEntity entity,
|
||||||
|
{double? translationSpeed, String? forwardAnimation}) async {
|
||||||
|
int? forwardAnimationIndex;
|
||||||
|
if (forwardAnimation != null) {
|
||||||
|
final animationNames = await controller.getAnimationNames(entity);
|
||||||
|
forwardAnimationIndex = animationNames.indexOf(forwardAnimation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forwardAnimationIndex == -1) {
|
||||||
|
throw Exception("Invalid animation : $forwardAnimation");
|
||||||
|
}
|
||||||
|
|
||||||
|
_keyboardListener?.dispose();
|
||||||
|
var transformController = EntityTransformController(controller, entity,
|
||||||
|
translationSpeed: translationSpeed ?? 1.0,
|
||||||
|
forwardAnimationIndex: forwardAnimationIndex);
|
||||||
|
_keyboardListener = HardwareKeyboardListener(transformController);
|
||||||
|
return transformController;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
72
lib/filament/entities/gizmo.dart
Normal file
72
lib/filament/entities/gizmo.dart
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:vector_math/vector_math_64.dart';
|
||||||
|
|
||||||
|
import '../filament_controller.dart';
|
||||||
|
|
||||||
|
class Gizmo {
|
||||||
|
final FilamentEntity x;
|
||||||
|
Vector3 _x = Vector3(0.1, 0, 0);
|
||||||
|
final FilamentEntity y;
|
||||||
|
Vector3 _y = Vector3(0.0, 0.1, 0);
|
||||||
|
final FilamentEntity z;
|
||||||
|
Vector3 _z = Vector3(0.0, 0.0, 0.1);
|
||||||
|
|
||||||
|
final FilamentController controller;
|
||||||
|
|
||||||
|
FilamentEntity? _activeAxis;
|
||||||
|
FilamentEntity? _activeEntity;
|
||||||
|
bool get isActive => _activeAxis != null;
|
||||||
|
|
||||||
|
Gizmo(this.x, this.y, this.z, this.controller) {
|
||||||
|
controller.pickResult.listen(_onPickResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future _reveal() async {
|
||||||
|
await controller.reveal(x, null);
|
||||||
|
await controller.reveal(y, null);
|
||||||
|
await controller.reveal(z, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
void translate(Offset offset) async {
|
||||||
|
late Vector3 vec;
|
||||||
|
if (_activeAxis == x) {
|
||||||
|
vec = _x;
|
||||||
|
} else if (_activeAxis == y) {
|
||||||
|
vec = _y;
|
||||||
|
} else if (_activeAxis == z) {
|
||||||
|
vec = _z;
|
||||||
|
}
|
||||||
|
await controller.queuePositionUpdate(_activeEntity!, offset.dx * vec.x,
|
||||||
|
-offset.dy * vec.y, -offset.dx * vec.z,
|
||||||
|
relative: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
_activeAxis = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onPickResult(FilamentPickResult result) async {
|
||||||
|
if (result.entity == x || result.entity == y || result.entity == z) {
|
||||||
|
_activeAxis = result.entity;
|
||||||
|
} else {
|
||||||
|
attach(result.entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void attach(FilamentEntity entity) async {
|
||||||
|
print("Attaching to $entity");
|
||||||
|
_activeAxis = null;
|
||||||
|
_activeEntity = entity;
|
||||||
|
await _reveal();
|
||||||
|
await controller.setParent(x, entity);
|
||||||
|
await controller.setParent(y, entity);
|
||||||
|
await controller.setParent(z, entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void detach() async {
|
||||||
|
await controller.hide(x, null);
|
||||||
|
await controller.hide(y, null);
|
||||||
|
await controller.hide(z, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,17 +5,28 @@ import 'dart:typed_data';
|
|||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
import 'package:flutter_filament/animations/animation_data.dart';
|
import 'package:flutter_filament/filament/entities/gizmo.dart';
|
||||||
import 'package:flutter_filament/entities/entity_transform_controller.dart';
|
|
||||||
import 'package:flutter_filament/generated_bindings.dart';
|
|
||||||
import 'package:vector_math/vector_math_64.dart';
|
import 'package:vector_math/vector_math_64.dart';
|
||||||
|
|
||||||
|
import 'animations/animation_data.dart';
|
||||||
|
|
||||||
// a handle that can be safely passed back to the rendering layer to manipulate an Entity
|
// a handle that can be safely passed back to the rendering layer to manipulate an Entity
|
||||||
typedef FilamentEntity = int;
|
typedef FilamentEntity = int;
|
||||||
|
|
||||||
// "picking" means clicking/tapping on the viewport, and unprojecting the X/Y coordinate to determine whether any renderable entities were present at those coordinates.
|
// "picking" means clicking/tapping on the viewport, and unprojecting the X/Y coordinate to determine whether any renderable entities were present at those coordinates.
|
||||||
typedef FilamentPickResult = ({FilamentEntity entity, double x, double y});
|
typedef FilamentPickResult = ({FilamentEntity entity, double x, double y});
|
||||||
|
|
||||||
|
// copied from filament/backened/DriverEnums.h
|
||||||
|
enum PrimitiveType {
|
||||||
|
// don't change the enums values (made to match GL)
|
||||||
|
POINTS, //!< points
|
||||||
|
LINES, //!< lines
|
||||||
|
UNUSED1,
|
||||||
|
LINE_STRIP, //!< line strip
|
||||||
|
TRIANGLES, //!< triangles
|
||||||
|
TRIANGLE_STRIP, //!< triangle strip
|
||||||
|
}
|
||||||
|
|
||||||
enum ToneMapper { ACES, FILMIC, LINEAR }
|
enum ToneMapper { ACES, FILMIC, LINEAR }
|
||||||
|
|
||||||
// see filament Manipulator.h for more details
|
// see filament Manipulator.h for more details
|
||||||
@@ -33,17 +44,6 @@ class TextureDetails {
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract class FilamentController {
|
abstract class FilamentController {
|
||||||
///
|
|
||||||
/// A Stream containing every FilamentEntity added to the scene (i.e. via [loadGlb], [loadGltf] or [addLight]).
|
|
||||||
/// This is provided for convenience so you can set listeners in front-end widgets that can respond to entity loads without manually passing around the FilamentEntity returned from those methods.
|
|
||||||
///
|
|
||||||
Stream<FilamentEntity> get onLoad;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// A Stream containing every FilamentEntity removed from the scene (i.e. via [removeEntity], [clearEntities], [removeLight] or [clearLights]).
|
|
||||||
|
|
||||||
Stream<FilamentEntity> get onUnload;
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// A [ValueNotifier] to indicate whether a FilamentViewer is currently available.
|
/// A [ValueNotifier] to indicate whether a FilamentViewer is currently available.
|
||||||
/// (FilamentViewer is a C++ type, hence why it is not referenced) here.
|
/// (FilamentViewer is a C++ type, hence why it is not referenced) here.
|
||||||
@@ -212,14 +212,6 @@ abstract class FilamentController {
|
|||||||
///
|
///
|
||||||
Future<FilamentEntity> loadGlb(String path, {int numInstances = 1});
|
Future<FilamentEntity> loadGlb(String path, {int numInstances = 1});
|
||||||
|
|
||||||
///
|
|
||||||
/// Load the .glb asset from the specified path (either Flutter asset URI or filepath) and insert into the scene.
|
|
||||||
/// If [cache] is true, the contents of the path will be cached locally and re-used for any future calls to load that asset.
|
|
||||||
/// See also [evictCache].
|
|
||||||
///
|
|
||||||
Future<FilamentEntity> loadGlbFromBuffer(String path,
|
|
||||||
{bool cache = false, int numInstances = 1});
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Create a new instance of [entity].
|
/// Create a new instance of [entity].
|
||||||
///
|
///
|
||||||
@@ -235,11 +227,6 @@ abstract class FilamentController {
|
|||||||
///
|
///
|
||||||
Future<List<FilamentEntity>> getInstances(FilamentEntity entity);
|
Future<List<FilamentEntity>> getInstances(FilamentEntity entity);
|
||||||
|
|
||||||
///
|
|
||||||
/// Frees all cached resources loaded via [loadGlbFromBuffer].
|
|
||||||
///
|
|
||||||
Future evictCache();
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Load the .gltf asset at the given path and insert into the scene.
|
/// Load the .gltf asset at the given path and insert into the scene.
|
||||||
/// [relativeResourcePath] is the folder path where the glTF resources are stored;
|
/// [relativeResourcePath] is the folder path where the glTF resources are stored;
|
||||||
@@ -558,7 +545,7 @@ abstract class FilamentController {
|
|||||||
///
|
///
|
||||||
/// Reveal the node [meshName] under [entity]. Only applicable if [hide] had previously been called; this is a no-op otherwise.
|
/// Reveal the node [meshName] under [entity]. Only applicable if [hide] had previously been called; this is a no-op otherwise.
|
||||||
///
|
///
|
||||||
Future reveal(FilamentEntity entity, String meshName);
|
Future reveal(FilamentEntity entity, String? meshName);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// If [meshName] is provided, hide the node [meshName] under [entity], otherwise hide the root node for [entity].
|
/// If [meshName] is provided, hide the node [meshName] under [entity], otherwise hide the root node for [entity].
|
||||||
@@ -613,12 +600,6 @@ abstract class FilamentController {
|
|||||||
///
|
///
|
||||||
Future setRecordingOutputDirectory(String outputDirectory);
|
Future setRecordingOutputDirectory(String outputDirectory);
|
||||||
|
|
||||||
///
|
|
||||||
/// Attach the keyboard/mouse to [entity].
|
|
||||||
///
|
|
||||||
Future<EntityTransformController> control(FilamentEntity entity,
|
|
||||||
{double? translationSpeed, String? forwardAnimation});
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// An [entity] will only be animatable after an animation component is attached.
|
/// An [entity] will only be animatable after an animation component is attached.
|
||||||
/// Any calls to [playAnimation]/[setBoneAnimation]/[setMorphAnimation] will have no visual effect until [addAnimationComponent] has been called on the instance.
|
/// Any calls to [playAnimation]/[setBoneAnimation]/[setMorphAnimation] will have no visual effect until [addAnimationComponent] has been called on the instance.
|
||||||
@@ -642,8 +623,9 @@ abstract class FilamentController {
|
|||||||
///
|
///
|
||||||
/// Creates a (renderable) entity with the specified geometry and adds to the scene.
|
/// Creates a (renderable) entity with the specified geometry and adds to the scene.
|
||||||
///
|
///
|
||||||
Future createGeometry(
|
Future createGeometry(List<double> vertices, List<int> indices,
|
||||||
List<double> vertices, List<int> indices, String? materialPath);
|
{String? materialPath,
|
||||||
|
PrimitiveType primitiveType = PrimitiveType.TRIANGLES});
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Sets the parent transform of [child] to the transform of [parent].
|
/// Sets the parent transform of [child] to the transform of [parent].
|
||||||
@@ -655,4 +637,59 @@ abstract class FilamentController {
|
|||||||
/// This method returns void; the relevant callback passed to [addCollisionComponent] will be fired if a collision is detected.
|
/// This method returns void; the relevant callback passed to [addCollisionComponent] will be fired if a collision is detected.
|
||||||
///
|
///
|
||||||
Future testCollisions(FilamentEntity entity);
|
Future testCollisions(FilamentEntity entity);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Sets the draw priority for the given entity. See RenderableManager.h for more details.
|
||||||
|
///
|
||||||
|
Future setPriority(FilamentEntity entityId, int priority);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// The Scene holds the transform gizmo and all loaded entities/lights.
|
||||||
|
///
|
||||||
|
Scene get scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// For now, this class just holds the entities that have been loaded (though not necessarily visible in the Filament Scene).
|
||||||
|
///
|
||||||
|
abstract class Scene {
|
||||||
|
///
|
||||||
|
/// The last entity clicked/tapped in the viewport (internally, the result of calling pick);
|
||||||
|
FilamentEntity? selected;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// A Stream updated whenever an entity is added/removed from the scene.
|
||||||
|
///
|
||||||
|
Stream<bool> get onUpdated;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// A Stream containing every FilamentEntity added to the scene (i.e. via [loadGlb], [loadGltf] or [addLight]).
|
||||||
|
/// This is provided for convenience so you can set listeners in front-end widgets that can respond to entity loads without manually passing around the FilamentEntity returned from those methods.
|
||||||
|
///
|
||||||
|
Stream<FilamentEntity> get onLoad;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// A Stream containing every FilamentEntity removed from the scene (i.e. via [removeEntity], [clearEntities], [removeLight] or [clearLights]).
|
||||||
|
|
||||||
|
Stream<FilamentEntity> get onUnload;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Lists all light entities currently loaded (not necessarily active in the scene). Does not account for instances.
|
||||||
|
///
|
||||||
|
Iterable<FilamentEntity> listLights();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Lists all entities currently loaded (not necessarily active in the scene). Does not account for instances.
|
||||||
|
///
|
||||||
|
Iterable<FilamentEntity> listEntities();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Attach the gizmo to the specified entity.
|
||||||
|
///
|
||||||
|
void select(FilamentEntity entity);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// The transform gizmo.
|
||||||
|
///
|
||||||
|
Gizmo get gizmo;
|
||||||
}
|
}
|
||||||
@@ -1,24 +1,20 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:ffi';
|
import 'dart:ffi';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
import 'dart:developer' as dev;
|
import 'dart:developer' as dev;
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
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/widgets.dart';
|
||||||
import 'package:flutter_filament/entities/entity_transform_controller.dart';
|
import 'package:flutter_filament/filament/animations/animation_data.dart';
|
||||||
|
import 'package:flutter_filament/filament/entities/gizmo.dart';
|
||||||
import 'package:flutter_filament/filament_controller.dart';
|
import 'package:flutter_filament/filament/filament_controller.dart';
|
||||||
|
import 'package:flutter_filament/filament/generated_bindings.dart';
|
||||||
import 'package:flutter_filament/animations/animation_data.dart';
|
import 'package:flutter_filament/filament/generated_bindings.dart' as gb;
|
||||||
import 'package:flutter_filament/generated_bindings.dart';
|
import 'package:flutter_filament/filament/rendering_surface.dart';
|
||||||
import 'package:flutter_filament/generated_bindings.dart' as gb;
|
|
||||||
import 'package:flutter_filament/hardware/hardware_keyboard_listener.dart';
|
|
||||||
|
|
||||||
import 'package:flutter_filament/rendering_surface.dart';
|
|
||||||
import 'package:vector_math/vector_math_64.dart';
|
import 'package:vector_math/vector_math_64.dart';
|
||||||
|
import 'scene.dart';
|
||||||
|
|
||||||
// ignore: constant_identifier_names
|
// ignore: constant_identifier_names
|
||||||
const FilamentEntity _FILAMENT_ASSET_ERROR = 0;
|
const FilamentEntity _FILAMENT_ASSET_ERROR = 0;
|
||||||
@@ -26,6 +22,9 @@ const FilamentEntity _FILAMENT_ASSET_ERROR = 0;
|
|||||||
class FilamentControllerFFI extends FilamentController {
|
class FilamentControllerFFI extends FilamentController {
|
||||||
final _channel = const MethodChannel("app.polyvox.filament/event");
|
final _channel = const MethodChannel("app.polyvox.filament/event");
|
||||||
|
|
||||||
|
late SceneImpl _scene;
|
||||||
|
Scene get scene => _scene;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// This will be set on constructor invocation.
|
/// This will be set on constructor invocation.
|
||||||
/// On Windows, this will be set to the value returned by the [usesBackingWindow] method call.
|
/// On Windows, this will be set to the value returned by the [usesBackingWindow] method call.
|
||||||
@@ -64,22 +63,8 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
|
|
||||||
Timer? _resizeTimer;
|
Timer? _resizeTimer;
|
||||||
|
|
||||||
final _lights = <FilamentEntity>{};
|
|
||||||
final _entities = <FilamentEntity>{};
|
|
||||||
|
|
||||||
final _onLoadController = StreamController<FilamentEntity>.broadcast();
|
|
||||||
Stream<FilamentEntity> get onLoad => _onLoadController.stream;
|
|
||||||
|
|
||||||
final _onUnloadController = StreamController<FilamentEntity>.broadcast();
|
|
||||||
Stream<FilamentEntity> get onUnload => _onUnloadController.stream;
|
|
||||||
|
|
||||||
final allocator = calloc;
|
final allocator = calloc;
|
||||||
|
|
||||||
void _using(Pointer ptr, Future Function(Pointer ptr) function) async {
|
|
||||||
await function.call(ptr);
|
|
||||||
allocator.free(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// This controller uses platform channels to bridge Dart with the C/C++ code for the Filament API.
|
/// This controller uses platform channels to bridge Dart with the C/C++ code for the Filament API.
|
||||||
/// Setting up the context/texture (since this is platform-specific) and the render ticker are platform-specific; all other methods are passed through by the platform channel to the methods specified in FlutterFilamentApi.h.
|
/// Setting up the context/texture (since this is platform-specific) and the render ticker are platform-specific; all other methods are passed through by the platform channel to the methods specified in FlutterFilamentApi.h.
|
||||||
@@ -145,7 +130,6 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
Future setFrameRate(int framerate) async {
|
Future setFrameRate(int framerate) async {
|
||||||
final interval = 1000.0 / framerate;
|
final interval = 1000.0 / framerate;
|
||||||
set_frame_interval_ffi(interval);
|
set_frame_interval_ffi(interval);
|
||||||
print("Set frame interval to $interval");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -360,7 +344,7 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
textureId: renderingSurface.flutterTextureId,
|
textureId: renderingSurface.flutterTextureId,
|
||||||
width: _rect.value!.width.toInt(),
|
width: _rect.value!.width.toInt(),
|
||||||
height: _rect.value!.height.toInt());
|
height: _rect.value!.height.toInt());
|
||||||
print("texture details ${textureDetails.value}");
|
|
||||||
await _withVoidCallback((callback) {
|
await _withVoidCallback((callback) {
|
||||||
update_viewport_and_camera_projection_ffi(
|
update_viewport_and_camera_projection_ffi(
|
||||||
_viewer!,
|
_viewer!,
|
||||||
@@ -369,6 +353,13 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
1.0,
|
1.0,
|
||||||
callback);
|
callback);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final out = allocator<Int32>(3);
|
||||||
|
get_gizmo(_sceneManager!, out);
|
||||||
|
var gizmo = Gizmo(out[0], out[1], out[2], this);
|
||||||
|
allocator.free(out);
|
||||||
|
_scene = SceneImpl(gizmo);
|
||||||
|
|
||||||
hasViewer.value = true;
|
hasViewer.value = true;
|
||||||
_creating = false;
|
_creating = false;
|
||||||
}
|
}
|
||||||
@@ -642,8 +633,8 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
dirZ,
|
dirZ,
|
||||||
castShadows,
|
castShadows,
|
||||||
callback));
|
callback));
|
||||||
_onLoadController.sink.add(entity);
|
|
||||||
_lights.add(entity);
|
_scene.registerLight(entity);
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -652,9 +643,8 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
if (_viewer == null) {
|
if (_viewer == null) {
|
||||||
throw Exception("No viewer available, ignoring");
|
throw Exception("No viewer available, ignoring");
|
||||||
}
|
}
|
||||||
_lights.remove(entity);
|
_scene.unregisterLight(entity);
|
||||||
remove_light_ffi(_viewer!, entity);
|
remove_light_ffi(_viewer!, entity);
|
||||||
_onUnloadController.add(entity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -663,48 +653,8 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
throw Exception("No viewer available, ignoring");
|
throw Exception("No viewer available, ignoring");
|
||||||
}
|
}
|
||||||
clear_lights_ffi(_viewer!);
|
clear_lights_ffi(_viewer!);
|
||||||
for (final entity in _lights) {
|
|
||||||
_onUnloadController.add(entity);
|
|
||||||
}
|
|
||||||
_lights.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
final _assetCache = <String, (Pointer<Void>, int)>{};
|
_scene.clearLights();
|
||||||
|
|
||||||
@override
|
|
||||||
Future<FilamentEntity> loadGlbFromBuffer(String path,
|
|
||||||
{bool cache = false, int numInstances = 1}) async {
|
|
||||||
late (Pointer<Void>, int) data;
|
|
||||||
|
|
||||||
if (cache && _assetCache.containsKey(path)) {
|
|
||||||
data = _assetCache[path]!;
|
|
||||||
} else {
|
|
||||||
late ByteData asset;
|
|
||||||
if (path.startsWith("file://")) {
|
|
||||||
var raw = File(path.replaceAll("file://", "")).readAsBytesSync();
|
|
||||||
asset = raw.buffer.asByteData(raw.offsetInBytes);
|
|
||||||
} else {
|
|
||||||
asset = await rootBundle.load(path.replaceAll("asset://", ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
var ptr = allocator<Char>(asset.lengthInBytes);
|
|
||||||
for (int i = 0; i < asset.lengthInBytes; i++) {
|
|
||||||
ptr[i] = asset.getUint8(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
data = (ptr.cast<Void>(), asset.lengthInBytes);
|
|
||||||
}
|
|
||||||
var entity = await _withIntCallback((callback) => load_glb_from_buffer_ffi(
|
|
||||||
_sceneManager!, data.$1, data.$2, numInstances, callback));
|
|
||||||
if (!cache) {
|
|
||||||
allocator.free(data.$1);
|
|
||||||
} else {
|
|
||||||
_assetCache[path] = data;
|
|
||||||
}
|
|
||||||
if (entity == _FILAMENT_ASSET_ERROR) {
|
|
||||||
throw Exception("Failed to load GLB from path $path");
|
|
||||||
}
|
|
||||||
return entity;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -735,14 +685,6 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
return instances;
|
return instances;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future evictCache() async {
|
|
||||||
for (final value in _assetCache.values) {
|
|
||||||
allocator.free(value.$1);
|
|
||||||
}
|
|
||||||
_assetCache.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<FilamentEntity> loadGlb(String path,
|
Future<FilamentEntity> loadGlb(String path,
|
||||||
{bool unlit = false, int numInstances = 1}) async {
|
{bool unlit = false, int numInstances = 1}) async {
|
||||||
@@ -759,8 +701,8 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
if (entity == _FILAMENT_ASSET_ERROR) {
|
if (entity == _FILAMENT_ASSET_ERROR) {
|
||||||
throw Exception("An error occurred loading the asset at $path");
|
throw Exception("An error occurred loading the asset at $path");
|
||||||
}
|
}
|
||||||
_entities.add(entity);
|
_scene.registerEntity(entity);
|
||||||
_onLoadController.sink.add(entity);
|
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -784,8 +726,8 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
if (entity == _FILAMENT_ASSET_ERROR) {
|
if (entity == _FILAMENT_ASSET_ERROR) {
|
||||||
throw Exception("An error occurred loading the asset at $path");
|
throw Exception("An error occurred loading the asset at $path");
|
||||||
}
|
}
|
||||||
_entities.add(entity);
|
_scene.registerEntity(entity);
|
||||||
_onLoadController.sink.add(entity);
|
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1021,10 +963,10 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
if (_viewer == null) {
|
if (_viewer == null) {
|
||||||
throw Exception("No viewer available, ignoring");
|
throw Exception("No viewer available, ignoring");
|
||||||
}
|
}
|
||||||
_entities.remove(entity);
|
_scene.unregisterEntity(entity);
|
||||||
|
|
||||||
await _withVoidCallback(
|
await _withVoidCallback(
|
||||||
(callback) => remove_entity_ffi(_viewer!, entity, callback));
|
(callback) => remove_entity_ffi(_viewer!, entity, callback));
|
||||||
_onUnloadController.add(entity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1034,11 +976,7 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
}
|
}
|
||||||
await _withVoidCallback(
|
await _withVoidCallback(
|
||||||
(callback) => clear_entities_ffi(_viewer!, callback));
|
(callback) => clear_entities_ffi(_viewer!, callback));
|
||||||
|
_scene.clearEntities();
|
||||||
for (final entity in _entities) {
|
|
||||||
_onUnloadController.add(entity);
|
|
||||||
}
|
|
||||||
_entities.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1078,6 +1016,14 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
_sceneManager!, entity, index, loop, reverse, replaceActive, crossfade);
|
_sceneManager!, entity, index, loop, reverse, replaceActive, crossfade);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future stopAnimation(FilamentEntity entity, int animationIndex) async {
|
||||||
|
if (_viewer == null) {
|
||||||
|
throw Exception("No viewer available, ignoring");
|
||||||
|
}
|
||||||
|
stop_animation(_sceneManager!, entity, animationIndex);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future stopAnimationByName(FilamentEntity entity, String name) async {
|
Future stopAnimationByName(FilamentEntity entity, String name) async {
|
||||||
var animations = await getAnimationNames(entity);
|
var animations = await getAnimationNames(entity);
|
||||||
@@ -1107,14 +1053,6 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
set_animation_frame(_sceneManager!, entity, index, animationFrame);
|
set_animation_frame(_sceneManager!, entity, index, animationFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future stopAnimation(FilamentEntity entity, int animationIndex) async {
|
|
||||||
if (_viewer == null) {
|
|
||||||
throw Exception("No viewer available, ignoring");
|
|
||||||
}
|
|
||||||
stop_animation(_sceneManager!, entity, animationIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future setMainCamera() async {
|
Future setMainCamera() async {
|
||||||
set_main_camera(_viewer!);
|
set_main_camera(_viewer!);
|
||||||
@@ -1303,16 +1241,6 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
set_position(_sceneManager!, entity, x, y, z);
|
set_position(_sceneManager!, entity, x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future setRotation(
|
|
||||||
FilamentEntity entity, double rads, double x, double y, double z) async {
|
|
||||||
if (_viewer == null) {
|
|
||||||
throw Exception("No viewer available, ignoring");
|
|
||||||
}
|
|
||||||
var quat = Quaternion.axisAngle(Vector3(x, y, z), rads);
|
|
||||||
await setRotationQuat(entity, quat);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future setRotationQuat(FilamentEntity entity, Quaternion rotation,
|
Future setRotationQuat(FilamentEntity entity, Quaternion rotation,
|
||||||
{bool relative = false}) async {
|
{bool relative = false}) async {
|
||||||
@@ -1323,6 +1251,16 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
rotation.y, rotation.z, rotation.w);
|
rotation.y, rotation.z, rotation.w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future setRotation(
|
||||||
|
FilamentEntity entity, double rads, double x, double y, double z) async {
|
||||||
|
if (_viewer == null) {
|
||||||
|
throw Exception("No viewer available, ignoring");
|
||||||
|
}
|
||||||
|
var quat = Quaternion.axisAngle(Vector3(x, y, z), rads);
|
||||||
|
await setRotationQuat(entity, quat);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future setScale(FilamentEntity entity, double scale) async {
|
Future setScale(FilamentEntity entity, double scale) async {
|
||||||
if (_viewer == null) {
|
if (_viewer == null) {
|
||||||
@@ -1331,15 +1269,13 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
set_scale(_sceneManager!, entity, scale);
|
set_scale(_sceneManager!, entity, scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
Future queueRotationUpdateQuat(FilamentEntity entity, Quaternion rotation,
|
||||||
Future queuePositionUpdate(
|
|
||||||
FilamentEntity entity, double x, double y, double z,
|
|
||||||
{bool relative = false}) async {
|
{bool relative = false}) async {
|
||||||
if (_viewer == null) {
|
if (_viewer == null) {
|
||||||
throw Exception("No viewer available, ignoring");
|
throw Exception("No viewer available, ignoring");
|
||||||
}
|
}
|
||||||
|
queue_rotation_update(_sceneManager!, entity, rotation.radians, rotation.x,
|
||||||
queue_position_update(_sceneManager!, entity, x, y, z, relative);
|
rotation.y, rotation.z, rotation.w, relative);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1353,13 +1289,15 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
await queueRotationUpdateQuat(entity, quat, relative: relative);
|
await queueRotationUpdateQuat(entity, quat, relative: relative);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future queueRotationUpdateQuat(FilamentEntity entity, Quaternion rotation,
|
@override
|
||||||
|
Future queuePositionUpdate(
|
||||||
|
FilamentEntity entity, double x, double y, double z,
|
||||||
{bool relative = false}) async {
|
{bool relative = false}) async {
|
||||||
if (_viewer == null) {
|
if (_viewer == null) {
|
||||||
throw Exception("No viewer available, ignoring");
|
throw Exception("No viewer available, ignoring");
|
||||||
}
|
}
|
||||||
queue_rotation_update(_sceneManager!, entity, rotation.radians, rotation.x,
|
|
||||||
rotation.y, rotation.z, rotation.w, relative);
|
queue_position_update(_sceneManager!, entity, x, y, z, relative);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1395,14 +1333,13 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
return result.cast<Utf8>().toDartString();
|
return result.cast<Utf8>().toDartString();
|
||||||
}
|
}
|
||||||
|
|
||||||
final _pick = <int, Completer<int>>{};
|
|
||||||
|
|
||||||
void _onPickResult(FilamentEntity entityId, int x, int y) {
|
void _onPickResult(FilamentEntity entityId, int x, int y) {
|
||||||
_pickResultController.add((
|
_pickResultController.add((
|
||||||
entity: entityId,
|
entity: entityId,
|
||||||
x: (x / _pixelRatio).toDouble(),
|
x: (x / _pixelRatio).toDouble(),
|
||||||
y: (textureDetails.value!.height - y) / _pixelRatio
|
y: (textureDetails.value!.height - y) / _pixelRatio
|
||||||
));
|
));
|
||||||
|
_scene.registerSelected(entityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
late NativeCallable<Void Function(Int32 entityId, Int x, Int y)>
|
late NativeCallable<Void Function(Int32 entityId, Int x, Int y)>
|
||||||
@@ -1414,6 +1351,8 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
throw Exception("No viewer available, ignoring");
|
throw Exception("No viewer available, ignoring");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_scene.unregisterSelected();
|
||||||
|
|
||||||
gb.pick(
|
gb.pick(
|
||||||
_viewer!,
|
_viewer!,
|
||||||
(x * _pixelRatio).toInt(),
|
(x * _pixelRatio).toInt(),
|
||||||
@@ -1592,28 +1531,6 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
allocator.free(pathPtr);
|
allocator.free(pathPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
HardwareKeyboardListener? _keyboardListener;
|
|
||||||
@override
|
|
||||||
Future<EntityTransformController> control(FilamentEntity entity,
|
|
||||||
{double? translationSpeed, String? forwardAnimation}) async {
|
|
||||||
int? forwardAnimationIndex;
|
|
||||||
if (forwardAnimation != null) {
|
|
||||||
final animationNames = await getAnimationNames(entity);
|
|
||||||
forwardAnimationIndex = animationNames.indexOf(forwardAnimation);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forwardAnimationIndex == -1) {
|
|
||||||
throw Exception("Invalid animation : $forwardAnimation");
|
|
||||||
}
|
|
||||||
|
|
||||||
_keyboardListener?.dispose();
|
|
||||||
var transformController = EntityTransformController(this, entity,
|
|
||||||
translationSpeed: translationSpeed ?? 1.0,
|
|
||||||
forwardAnimationIndex: forwardAnimationIndex);
|
|
||||||
_keyboardListener = HardwareKeyboardListener(transformController);
|
|
||||||
return transformController;
|
|
||||||
}
|
|
||||||
|
|
||||||
final _collisions = <FilamentEntity, NativeCallable>{};
|
final _collisions = <FilamentEntity, NativeCallable>{};
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1649,7 +1566,9 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<FilamentEntity> createGeometry(
|
Future<FilamentEntity> createGeometry(
|
||||||
List<double> vertices, List<int> indices, String? materialPath) async {
|
List<double> vertices, List<int> indices,
|
||||||
|
{String? materialPath,
|
||||||
|
PrimitiveType primitiveType = PrimitiveType.TRIANGLES}) async {
|
||||||
if (_viewer == null) {
|
if (_viewer == null) {
|
||||||
throw Exception("Viewer must not be null");
|
throw Exception("Viewer must not be null");
|
||||||
}
|
}
|
||||||
@@ -1672,14 +1591,14 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
vertices.length,
|
vertices.length,
|
||||||
indicesPtr,
|
indicesPtr,
|
||||||
indices.length,
|
indices.length,
|
||||||
|
primitiveType.index,
|
||||||
materialPathPtr.cast<Char>(),
|
materialPathPtr.cast<Char>(),
|
||||||
callback));
|
callback));
|
||||||
if (entity == _FILAMENT_ASSET_ERROR) {
|
if (entity == _FILAMENT_ASSET_ERROR) {
|
||||||
throw Exception("Failed to create geometry");
|
throw Exception("Failed to create geometry");
|
||||||
}
|
}
|
||||||
|
|
||||||
_entities.add(entity);
|
_scene.registerEntity(entity);
|
||||||
_onLoadController.sink.add(entity);
|
|
||||||
|
|
||||||
allocator.free(materialPathPtr);
|
allocator.free(materialPathPtr);
|
||||||
allocator.free(vertexPtr);
|
allocator.free(vertexPtr);
|
||||||
@@ -1700,4 +1619,9 @@ class FilamentControllerFFI extends FilamentController {
|
|||||||
Future testCollisions(FilamentEntity entity) async {
|
Future testCollisions(FilamentEntity entity) async {
|
||||||
test_collisions(_sceneManager!, entity);
|
test_collisions(_sceneManager!, entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future setPriority(FilamentEntity entityId, int priority) async {
|
||||||
|
set_priority(_sceneManager!, entityId, priority);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -955,8 +955,14 @@ external void add_animation_component(
|
|||||||
);
|
);
|
||||||
|
|
||||||
@ffi.Native<
|
@ffi.Native<
|
||||||
EntityId Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Float>,
|
EntityId Function(
|
||||||
ffi.Int, ffi.Pointer<ffi.Uint16>, ffi.Int, ffi.Pointer<ffi.Char>)>(
|
ffi.Pointer<ffi.Void>,
|
||||||
|
ffi.Pointer<ffi.Float>,
|
||||||
|
ffi.Int,
|
||||||
|
ffi.Pointer<ffi.Uint16>,
|
||||||
|
ffi.Int,
|
||||||
|
ffi.Int,
|
||||||
|
ffi.Pointer<ffi.Char>)>(
|
||||||
symbol: 'create_geometry', assetId: 'flutter_filament_plugin')
|
symbol: 'create_geometry', assetId: 'flutter_filament_plugin')
|
||||||
external int create_geometry(
|
external int create_geometry(
|
||||||
ffi.Pointer<ffi.Void> viewer,
|
ffi.Pointer<ffi.Void> viewer,
|
||||||
@@ -964,6 +970,7 @@ external int create_geometry(
|
|||||||
int numVertices,
|
int numVertices,
|
||||||
ffi.Pointer<ffi.Uint16> indices,
|
ffi.Pointer<ffi.Uint16> indices,
|
||||||
int numIndices,
|
int numIndices,
|
||||||
|
int primitiveType,
|
||||||
ffi.Pointer<ffi.Char> materialPath,
|
ffi.Pointer<ffi.Char> materialPath,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -982,6 +989,21 @@ external void test_collisions(
|
|||||||
int entity,
|
int entity,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId, ffi.Int)>(
|
||||||
|
symbol: 'set_priority', assetId: 'flutter_filament_plugin')
|
||||||
|
external void set_priority(
|
||||||
|
ffi.Pointer<ffi.Void> sceneManager,
|
||||||
|
int entityId,
|
||||||
|
int priority,
|
||||||
|
);
|
||||||
|
|
||||||
|
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, ffi.Pointer<EntityId>)>(
|
||||||
|
symbol: 'get_gizmo', assetId: 'flutter_filament_plugin')
|
||||||
|
external void get_gizmo(
|
||||||
|
ffi.Pointer<ffi.Void> sceneManager,
|
||||||
|
ffi.Pointer<EntityId> out,
|
||||||
|
);
|
||||||
|
|
||||||
@ffi.Native<
|
@ffi.Native<
|
||||||
ffi.Void Function(
|
ffi.Void Function(
|
||||||
ffi.Pointer<ffi.Void>,
|
ffi.Pointer<ffi.Void>,
|
||||||
@@ -1510,6 +1532,7 @@ external void ios_dummy_ffi();
|
|||||||
ffi.Int,
|
ffi.Int,
|
||||||
ffi.Pointer<ffi.Uint16>,
|
ffi.Pointer<ffi.Uint16>,
|
||||||
ffi.Int,
|
ffi.Int,
|
||||||
|
ffi.Int,
|
||||||
ffi.Pointer<ffi.Char>,
|
ffi.Pointer<ffi.Char>,
|
||||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>>)>(
|
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>>)>(
|
||||||
symbol: 'create_geometry_ffi', assetId: 'flutter_filament_plugin')
|
symbol: 'create_geometry_ffi', assetId: 'flutter_filament_plugin')
|
||||||
@@ -1519,6 +1542,7 @@ external void create_geometry_ffi(
|
|||||||
int numVertices,
|
int numVertices,
|
||||||
ffi.Pointer<ffi.Uint16> indices,
|
ffi.Pointer<ffi.Uint16> indices,
|
||||||
int numIndices,
|
int numIndices,
|
||||||
|
int primitiveType,
|
||||||
ffi.Pointer<ffi.Char> materialPath,
|
ffi.Pointer<ffi.Char> materialPath,
|
||||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>> callback,
|
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>> callback,
|
||||||
);
|
);
|
||||||
118
lib/filament/scene.dart
Normal file
118
lib/filament/scene.dart
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'package:flutter_filament/filament/entities/gizmo.dart';
|
||||||
|
import 'package:flutter_filament/filament/filament_controller.dart';
|
||||||
|
|
||||||
|
///
|
||||||
|
/// For now, this class just holds the entities that have been loaded (though not necessarily visible in the Filament Scene).
|
||||||
|
///
|
||||||
|
class SceneImpl extends Scene {
|
||||||
|
final Gizmo _gizmo;
|
||||||
|
Gizmo get gizmo => _gizmo;
|
||||||
|
|
||||||
|
SceneImpl(this._gizmo);
|
||||||
|
|
||||||
|
@override
|
||||||
|
FilamentEntity? selected;
|
||||||
|
|
||||||
|
final _onUpdatedController = StreamController<bool>.broadcast();
|
||||||
|
@override
|
||||||
|
Stream<bool> get onUpdated => _onUpdatedController.stream;
|
||||||
|
|
||||||
|
final _onLoadController = StreamController<FilamentEntity>.broadcast();
|
||||||
|
@override
|
||||||
|
Stream<FilamentEntity> get onLoad => _onLoadController.stream;
|
||||||
|
|
||||||
|
final _onUnloadController = StreamController<FilamentEntity>.broadcast();
|
||||||
|
@override
|
||||||
|
Stream<FilamentEntity> get onUnload => _onUnloadController.stream;
|
||||||
|
|
||||||
|
final _lights = <FilamentEntity>{};
|
||||||
|
final _entities = <FilamentEntity>{};
|
||||||
|
|
||||||
|
void registerLight(FilamentEntity entity) {
|
||||||
|
_lights.add(entity);
|
||||||
|
_onLoadController.sink.add(entity);
|
||||||
|
_onUpdatedController.add(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unregisterLight(FilamentEntity entity) {
|
||||||
|
if (selected == entity) {
|
||||||
|
selected = null;
|
||||||
|
_gizmo.detach();
|
||||||
|
}
|
||||||
|
_lights.remove(entity);
|
||||||
|
_onUnloadController.add(entity);
|
||||||
|
_onUpdatedController.add(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unregisterEntity(FilamentEntity entity) {
|
||||||
|
if (selected == entity) {
|
||||||
|
selected = null;
|
||||||
|
_gizmo.detach();
|
||||||
|
}
|
||||||
|
_entities.remove(entity);
|
||||||
|
_onUnloadController.add(entity);
|
||||||
|
_onUpdatedController.add(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerEntity(FilamentEntity entity) {
|
||||||
|
_entities.add(entity);
|
||||||
|
_entities.add(entity);
|
||||||
|
_onLoadController.sink.add(entity);
|
||||||
|
_onUpdatedController.add(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearLights() {
|
||||||
|
for (final light in _lights) {
|
||||||
|
if (selected == light) {
|
||||||
|
selected = null;
|
||||||
|
_gizmo.detach();
|
||||||
|
}
|
||||||
|
_onUnloadController.add(light);
|
||||||
|
}
|
||||||
|
|
||||||
|
_lights.clear();
|
||||||
|
_onUpdatedController.add(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearEntities() {
|
||||||
|
for (final entity in _entities) {
|
||||||
|
if (selected == entity) {
|
||||||
|
selected = null;
|
||||||
|
_gizmo.detach();
|
||||||
|
}
|
||||||
|
_onUnloadController.add(entity);
|
||||||
|
}
|
||||||
|
_entities.clear();
|
||||||
|
_onUpdatedController.add(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Lists all entities currently loaded (not necessarily active in the scene).
|
||||||
|
///
|
||||||
|
Iterable<FilamentEntity> listLights() {
|
||||||
|
return _lights;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<FilamentEntity> listEntities() {
|
||||||
|
return _entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerSelected(FilamentEntity entity) {
|
||||||
|
selected = entity;
|
||||||
|
_onUpdatedController.add(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unregisterSelected() {
|
||||||
|
selected = null;
|
||||||
|
_onUpdatedController.add(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void select(FilamentEntity entity) {
|
||||||
|
selected = entity;
|
||||||
|
_gizmo.attach(entity);
|
||||||
|
_onUpdatedController.add(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_filament/entities/entity_transform_controller.dart';
|
import 'package:flutter_filament/filament/entities/entity_transform_controller.dart';
|
||||||
import 'package:flutter_filament/filament_controller.dart';
|
|
||||||
|
|
||||||
class HardwareKeyboardListener {
|
class HardwareKeyboardListener {
|
||||||
final EntityTransformController _controller;
|
final EntityTransformController _controller;
|
||||||
@@ -1,15 +1,13 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_filament/entities/entity_transform_controller.dart';
|
import 'package:flutter_filament/filament/entities/entity_transform_controller.dart';
|
||||||
import 'package:flutter_filament/filament_controller.dart';
|
|
||||||
|
|
||||||
class HardwareKeyboardPoll {
|
class HardwareKeyboardPoll {
|
||||||
final EntityTransformController _controller;
|
final EntityTransformController _controller;
|
||||||
late Timer _timer;
|
late Timer _timer;
|
||||||
HardwareKeyboardPoll(this._controller) {
|
HardwareKeyboardPoll(this._controller) {
|
||||||
_timer = Timer.periodic(const Duration(milliseconds: 16), (_) {
|
_timer = Timer.periodic(const Duration(milliseconds: 16), (_) {
|
||||||
print(RawKeyboard.instance.keysPressed);
|
|
||||||
if (RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.keyW)) {
|
if (RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.keyW)) {
|
||||||
_controller.forwardPressed();
|
_controller.forwardPressed();
|
||||||
} else {
|
} else {
|
||||||
10
lib/filament/utils/using_pointer.dart
Normal file
10
lib/filament/utils/using_pointer.dart
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import 'dart:ffi';
|
||||||
|
|
||||||
|
import 'package:ffi/ffi.dart';
|
||||||
|
|
||||||
|
final allocator = calloc;
|
||||||
|
|
||||||
|
void using(Pointer ptr, Future Function(Pointer ptr) function) async {
|
||||||
|
await function.call(ptr);
|
||||||
|
allocator.free(ptr);
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_filament/camera/camera_orientation.dart';
|
|
||||||
import 'package:flutter_filament/filament_controller.dart';
|
import 'package:flutter_filament/filament/filament_controller.dart';
|
||||||
|
import 'package:flutter_filament/filament/utils/camera_orientation.dart';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:vector_math/vector_math_64.dart' as v64;
|
import 'package:vector_math/vector_math_64.dart' as v64;
|
||||||
|
|
||||||
147
lib/filament/widgets/debug/entity_list_widget.dart
Normal file
147
lib/filament/widgets/debug/entity_list_widget.dart
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_filament/filament/entities/gizmo.dart';
|
||||||
|
import 'package:flutter_filament/filament/filament_controller.dart';
|
||||||
|
|
||||||
|
class EntityListWidget extends StatefulWidget {
|
||||||
|
final FilamentController? controller;
|
||||||
|
|
||||||
|
const EntityListWidget({super.key, required this.controller});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => _EntityListWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _EntityListWidget extends State<EntityListWidget> {
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(EntityListWidget oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _entity(FilamentEntity entity) {
|
||||||
|
return FutureBuilder(
|
||||||
|
future: widget.controller!.getAnimationNames(entity),
|
||||||
|
builder: (_, animations) {
|
||||||
|
if (animations.data == null) {
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
final menuController = MenuController();
|
||||||
|
return Row(children: [
|
||||||
|
Expanded(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
widget.controller!.scene.select(entity);
|
||||||
|
},
|
||||||
|
child: Text(entity.toString(),
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight:
|
||||||
|
entity == widget.controller!.scene.selected
|
||||||
|
? FontWeight.bold
|
||||||
|
: FontWeight.normal)))),
|
||||||
|
MenuAnchor(
|
||||||
|
controller: menuController,
|
||||||
|
child: Container(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.arrow_drop_down,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
menuController.open();
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
menuChildren: [
|
||||||
|
MenuItemButton(
|
||||||
|
child: const Text("Remove"),
|
||||||
|
onPressed: () async {
|
||||||
|
await widget.controller!.removeEntity(entity);
|
||||||
|
}),
|
||||||
|
MenuItemButton(
|
||||||
|
child: const Text("Transform to unit cube"),
|
||||||
|
onPressed: () async {
|
||||||
|
await widget.controller!.transformToUnitCube(entity);
|
||||||
|
}),
|
||||||
|
SubmenuButton(
|
||||||
|
child: const Text("Animations"),
|
||||||
|
menuChildren: animations.data!
|
||||||
|
.map((a) => MenuItemButton(
|
||||||
|
child: Text(a),
|
||||||
|
onPressed: () {
|
||||||
|
widget.controller!.playAnimation(
|
||||||
|
entity, animations.data!.indexOf(a));
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.toList())
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _light(FilamentEntity entity) {
|
||||||
|
final controller = MenuController();
|
||||||
|
return Row(children: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
widget.controller!.scene.select(entity);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Text("Light $entity",
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: entity == widget.controller!.scene.selected
|
||||||
|
? FontWeight.bold
|
||||||
|
: FontWeight.normal)))),
|
||||||
|
MenuAnchor(
|
||||||
|
controller: controller,
|
||||||
|
child: Container(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.arrow_drop_down,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
controller.open();
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
menuChildren: [
|
||||||
|
MenuItemButton(
|
||||||
|
child: const Text("Remove"),
|
||||||
|
onPressed: () async {
|
||||||
|
await widget.controller!.removeLight(entity);
|
||||||
|
})
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (widget.controller == null) {
|
||||||
|
return Container();
|
||||||
|
}
|
||||||
|
return ValueListenableBuilder(
|
||||||
|
valueListenable: widget.controller!.hasViewer,
|
||||||
|
builder: (_, bool hasViewer, __) => !hasViewer
|
||||||
|
? Container()
|
||||||
|
: StreamBuilder(
|
||||||
|
stream: widget.controller!.scene.onUpdated,
|
||||||
|
builder: (_, __) => Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 30, vertical: 10),
|
||||||
|
height: 100,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(30),
|
||||||
|
color: Colors.white.withOpacity(0.25),
|
||||||
|
),
|
||||||
|
child: ListView(
|
||||||
|
reverse: true,
|
||||||
|
children: widget.controller!.scene
|
||||||
|
.listLights()
|
||||||
|
.map(_light)
|
||||||
|
.followedBy(widget.controller!.scene
|
||||||
|
.listEntities()
|
||||||
|
.map(_entity))
|
||||||
|
.cast<Widget>()
|
||||||
|
.toList()))));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_filament/entities/entity_transform_controller.dart';
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter_filament/filament/entities/entity_transform_controller.dart';
|
||||||
|
|
||||||
///
|
///
|
||||||
/// A widget that translates mouse gestures to zoom/pan/rotate actions.
|
/// A widget that translates mouse gestures to zoom/pan/rotate actions.
|
||||||
///
|
///
|
||||||
@@ -2,8 +2,8 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_filament/widgets/filament_gesture_detector_desktop.dart';
|
import 'package:flutter_filament/filament/widgets/filament_gesture_detector_desktop.dart';
|
||||||
import 'package:flutter_filament/widgets/filament_gesture_detector_mobile.dart';
|
import 'package:flutter_filament/filament/widgets/filament_gesture_detector_mobile.dart';
|
||||||
import '../filament_controller.dart';
|
import '../filament_controller.dart';
|
||||||
|
|
||||||
enum GestureType { rotateCamera, panCamera, panBackground }
|
enum GestureType { rotateCamera, panCamera, panBackground }
|
||||||
@@ -59,26 +59,35 @@ class FilamentGestureDetector extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (kIsWeb) {
|
return ValueListenableBuilder(
|
||||||
throw Exception("TODO");
|
valueListenable: controller.hasViewer,
|
||||||
} else if (Platform.isLinux || Platform.isWindows || Platform.isMacOS) {
|
builder: (_, bool hasViewer, __) {
|
||||||
return FilamentGestureDetectorDesktop(
|
if (!hasViewer) {
|
||||||
controller: controller,
|
return Container(child: child);
|
||||||
child: child,
|
}
|
||||||
showControlOverlay: showControlOverlay,
|
if (kIsWeb) {
|
||||||
enableCamera: enableCamera,
|
throw Exception("TODO");
|
||||||
enablePicking: enablePicking,
|
} else if (Platform.isLinux ||
|
||||||
);
|
Platform.isWindows ||
|
||||||
} else {
|
Platform.isMacOS) {
|
||||||
return FilamentGestureDetectorMobile(
|
return FilamentGestureDetectorDesktop(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
child: child,
|
child: child,
|
||||||
showControlOverlay: showControlOverlay,
|
showControlOverlay: showControlOverlay,
|
||||||
enableCamera: enableCamera,
|
enableCamera: enableCamera,
|
||||||
enablePicking: enablePicking,
|
enablePicking: enablePicking,
|
||||||
onScaleStart: onScaleStart,
|
);
|
||||||
onScaleUpdate: onScaleUpdate,
|
} else {
|
||||||
onScaleEnd: onScaleEnd);
|
return FilamentGestureDetectorMobile(
|
||||||
}
|
controller: controller,
|
||||||
|
child: child,
|
||||||
|
showControlOverlay: showControlOverlay,
|
||||||
|
enableCamera: enableCamera,
|
||||||
|
enablePicking: enablePicking,
|
||||||
|
onScaleStart: onScaleStart,
|
||||||
|
onScaleUpdate: onScaleUpdate,
|
||||||
|
onScaleEnd: onScaleEnd);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_filament/filament/entities/gizmo.dart';
|
||||||
import '../filament_controller.dart';
|
import '../filament_controller.dart';
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -60,6 +61,13 @@ class _FilamentGestureDetectorDesktopState
|
|||||||
|
|
||||||
bool _pointerMoving = false;
|
bool _pointerMoving = false;
|
||||||
|
|
||||||
|
Gizmo get _gizmo => widget.controller.scene.gizmo;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(FilamentGestureDetectorDesktop oldWidget) {
|
void didUpdateWidget(FilamentGestureDetectorDesktop oldWidget) {
|
||||||
if (widget.showControlOverlay != oldWidget.showControlOverlay ||
|
if (widget.showControlOverlay != oldWidget.showControlOverlay ||
|
||||||
@@ -91,9 +99,21 @@ class _FilamentGestureDetectorDesktopState
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Timer? _pickTimer;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Listener(
|
return Listener(
|
||||||
|
// onPointerHover: (event) async {
|
||||||
|
// if (_gizmo.isActive) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// _pickTimer?.cancel();
|
||||||
|
// _pickTimer = Timer(const Duration(milliseconds: 100), () async {
|
||||||
|
// widget.controller
|
||||||
|
// .pick(event.position.dx.toInt(), event.position.dy.toInt());
|
||||||
|
// });
|
||||||
|
// },
|
||||||
onPointerSignal: (PointerSignalEvent pointerSignal) async {
|
onPointerSignal: (PointerSignalEvent pointerSignal) async {
|
||||||
if (pointerSignal is PointerScrollEvent) {
|
if (pointerSignal is PointerScrollEvent) {
|
||||||
if (widget.enableCamera) {
|
if (widget.enableCamera) {
|
||||||
@@ -106,10 +126,10 @@ class _FilamentGestureDetectorDesktopState
|
|||||||
onPointerPanZoomStart: (pzs) {
|
onPointerPanZoomStart: (pzs) {
|
||||||
throw Exception("TODO - is this a pinch zoom on laptop trackpad?");
|
throw Exception("TODO - is this a pinch zoom on laptop trackpad?");
|
||||||
},
|
},
|
||||||
// ignore all pointer down events
|
|
||||||
// so we can wait to see if the pointer will be held/moved (interpreted as rotate/pan),
|
|
||||||
// or if this is a single mousedown event (interpreted as viewport pick)
|
|
||||||
onPointerDown: (d) async {
|
onPointerDown: (d) async {
|
||||||
|
if (_gizmo.isActive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (d.buttons != kTertiaryButton && widget.enablePicking) {
|
if (d.buttons != kTertiaryButton && widget.enablePicking) {
|
||||||
widget.controller
|
widget.controller
|
||||||
.pick(d.localPosition.dx.toInt(), d.localPosition.dy.toInt());
|
.pick(d.localPosition.dx.toInt(), d.localPosition.dy.toInt());
|
||||||
@@ -118,6 +138,10 @@ class _FilamentGestureDetectorDesktopState
|
|||||||
},
|
},
|
||||||
// holding/moving the left mouse button is interpreted as a pan, middle mouse button as a rotate
|
// holding/moving the left mouse button is interpreted as a pan, middle mouse button as a rotate
|
||||||
onPointerMove: (PointerMoveEvent d) async {
|
onPointerMove: (PointerMoveEvent d) async {
|
||||||
|
if (_gizmo.isActive) {
|
||||||
|
_gizmo.translate(d.delta);
|
||||||
|
return;
|
||||||
|
}
|
||||||
// if this is the first move event, we need to call rotateStart/panStart to set the first coordinates
|
// if this is the first move event, we need to call rotateStart/panStart to set the first coordinates
|
||||||
if (!_pointerMoving) {
|
if (!_pointerMoving) {
|
||||||
if (d.buttons == kTertiaryButton && widget.enableCamera) {
|
if (d.buttons == kTertiaryButton && widget.enableCamera) {
|
||||||
@@ -142,6 +166,11 @@ class _FilamentGestureDetectorDesktopState
|
|||||||
// 2) if _pointerMoving is false, this is interpreted as a pick
|
// 2) if _pointerMoving is false, this is interpreted as a pick
|
||||||
// same applies to middle mouse button, but this is ignored as a pick
|
// same applies to middle mouse button, but this is ignored as a pick
|
||||||
onPointerUp: (PointerUpEvent d) async {
|
onPointerUp: (PointerUpEvent d) async {
|
||||||
|
if (_gizmo.isActive) {
|
||||||
|
_gizmo.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (d.buttons == kTertiaryButton && widget.enableCamera) {
|
if (d.buttons == kTertiaryButton && widget.enableCamera) {
|
||||||
widget.controller.rotateEnd();
|
widget.controller.rotateEnd();
|
||||||
} else {
|
} else {
|
||||||
@@ -6,7 +6,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
|
|
||||||
import 'package:flutter_filament/filament_controller.dart';
|
import 'package:flutter_filament/filament/filament_controller.dart';
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
@@ -2,7 +2,7 @@ import 'dart:math';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_filament/filament_controller.dart';
|
import 'package:flutter_filament/filament/filament_controller.dart';
|
||||||
import 'package:vector_math/vector_math_64.dart' as v;
|
import 'package:vector_math/vector_math_64.dart' as v;
|
||||||
|
|
||||||
class IblRotationSliderWidget extends StatefulWidget {
|
class IblRotationSliderWidget extends StatefulWidget {
|
||||||
@@ -2,8 +2,8 @@ import 'dart:math';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_filament/filament_controller.dart';
|
import 'package:flutter_filament/filament/filament_controller.dart';
|
||||||
import 'package:flutter_filament/lights/light_options.dart';
|
import 'package:flutter_filament/filament/utils/light_options.dart';
|
||||||
import 'package:vector_math/vector_math_64.dart' as v;
|
import 'package:vector_math/vector_math_64.dart' as v;
|
||||||
|
|
||||||
class LightSliderWidget extends StatefulWidget {
|
class LightSliderWidget extends StatefulWidget {
|
||||||
12
lib/flutter_filament.dart
Normal file
12
lib/flutter_filament.dart
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
library flutter_filament;
|
||||||
|
|
||||||
|
export 'filament/filament_controller.dart';
|
||||||
|
export 'filament/filament_controller_ffi.dart';
|
||||||
|
export 'filament/animations/animation_builder.dart';
|
||||||
|
export 'filament/animations/animation_data.dart';
|
||||||
|
export 'filament/widgets/camera_options_widget.dart';
|
||||||
|
export 'filament/widgets/filament_gesture_detector.dart';
|
||||||
|
export 'filament/widgets/filament_widget.dart';
|
||||||
|
export 'filament/widgets/debug/entity_list_widget.dart';
|
||||||
|
export 'filament/entities/entity_transform_controller.dart';
|
||||||
|
export 'filament/widgets/entity_controller_mouse_widget.dart';
|
||||||
40
materials/gizmo.mat
Normal file
40
materials/gizmo.mat
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
material {
|
||||||
|
name : Gizmo,
|
||||||
|
parameters : [
|
||||||
|
{
|
||||||
|
type : mat4,
|
||||||
|
name : transform,
|
||||||
|
precision : high
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type : float3,
|
||||||
|
name : color,
|
||||||
|
precision : low
|
||||||
|
}
|
||||||
|
],
|
||||||
|
depthWrite : true,
|
||||||
|
depthCulling : false,
|
||||||
|
shadingModel : unlit,
|
||||||
|
variantFilter : [ skinning, shadowReceiver, vsm ],
|
||||||
|
culling: none,
|
||||||
|
instanced: false,
|
||||||
|
vertexDomain: object
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex {
|
||||||
|
void materialVertex(inout MaterialVertexInputs material) {
|
||||||
|
vec4 modelSpace = getPosition();
|
||||||
|
vec4 worldSpace = getWorldFromModelMatrix() * modelSpace;
|
||||||
|
vec4 clipSpace = getClipFromWorldMatrix() * worldSpace;
|
||||||
|
clipSpace.z = 0.99f;
|
||||||
|
material.worldPosition = getWorldFromClipMatrix() * clipSpace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment {
|
||||||
|
void material(inout MaterialInputs material) {
|
||||||
|
prepareMaterial(material);
|
||||||
|
material.baseColor = float4(materialParams.color, 1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user