Merge branch 'feature/instancing' into develop

This commit is contained in:
Nick Fisher
2024-03-25 22:23:36 +08:00
92 changed files with 5713 additions and 2342 deletions

View File

@@ -4,6 +4,11 @@
* added basic collision detection + callbacks
* added keyboard/mouse widgets + controls
* `createViewer` now `awaits` the insertion of `FilamentWidget` so you no longer need to manually defer calling until after FilamentWidget has been rendered
* `setCameraRotation` now accepts a quaternion instead of an axis/angle
* instancing is now supported.
* `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

View File

@@ -422,11 +422,27 @@ make generate-background-material
# Known issues
On Windows, loading a glTF (but NOT a glb) may crash due to a race condition between uploading resource data to GPU memory and being freed on the host side.
## Windows
This has been fixed in recent versions of Filament, but other bugs on Windows prevent upgrading.
Loading a glTF (but NOT a glb) may crash due to a race condition between uploading resource data to GPU memory and being freed on the host side. This has been fixed in recent versions of Filament, but other bugs on Windows prevent upgrading. Only workaround is to load a .glb file.
Only workaround is to load a .glb file.
## Android
In release mode, you must add the following to your `app/build.gradle`:
```
buildTypes {
release {
...
shrinkResources false
minifyEnabled false
}
}
...
dependencies {
....
implementation 'net.java.dev.jna:jna:5.10.0@aar'
}
```
# Thanks

View File

@@ -2,14 +2,14 @@ group 'app.polyvox.filament'
version '1.0-SNAPSHOT'
buildscript {
ext.kotlin_version = '1.6.20'
ext.kotlin_version = '1.7.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath 'com.android.tools.build:gradle:7.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

View File

@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip

View File

@@ -28,7 +28,7 @@ apply plugin: 'kotlin-android-extensions'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 33
compileSdkVersion 34
ndkVersion "25.2.9519653"
compileOptions {

View File

@@ -1,3 +1,9 @@
org.gradle.jvmargs=-Xmx1536M
org.gradle.jvmargs=-Xmx1536M \
--add-exports=java.base/sun.nio.ch=ALL-UNNAMED \
--add-opens=java.base/java.lang=ALL-UNNAMED \
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED \
--add-opens=java.base/java.io=ALL-UNNAMED \
--add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED
android.useAndroidX=true
android.enableJetifier=true

View File

@@ -1,6 +1,5 @@
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ab0cada556723be0d138d7b1cadb5e315a273524db0468e0c4255d8d2b0c1c2d
size 1222992
oid sha256:983af4eb524f24b17bf081ae40a7217b60415122ab02cbf02c0186fbf069d52a
size 1251408

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b728c8fe0ce9eb06290dc37034404a952df8ea360b014b76c82fe8b9d695e85a
size 113124
oid sha256:715793a3cf36ccf5608c347b961d0f2549e54edb4ba5227a5a008dc1d3445e83
size 116948

View File

@@ -2,7 +2,7 @@ import 'dart:async';
import 'package:flutter/material.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;
class CameraMatrixOverlay extends StatefulWidget {
@@ -96,7 +96,7 @@ class _CameraMatrixOverlayState extends State<CameraMatrixOverlay> {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text("Camera position : $_cameraPosition $_cameraRotation",
Text("Camera : $_cameraPosition $_cameraRotation",
style:
const TextStyle(color: Colors.white, fontSize: 10)),
// widget.showProjectionMatrices

View File

@@ -1,9 +1,5 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_filament/entities/entity_transform_controller.dart';
import 'package:flutter_filament/filament_controller.dart';
import 'package:flutter_filament/widgets/entity_controller_mouse_widget.dart';
import 'package:flutter_filament/widgets/filament_gesture_detector.dart';
import 'package:flutter_filament/widgets/filament_widget.dart';
import 'package:flutter_filament/flutter_filament.dart';
class ExampleViewport extends StatelessWidget {
final FilamentController? controller;

View File

@@ -1,22 +1,19 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_filament/entities/entity_transform_controller.dart';
import 'package:flutter_filament/filament_controller_ffi.dart';
import 'package:flutter_filament_example/camera_matrix_overlay.dart';
import 'package:flutter_filament_example/menus/controller_menu.dart';
import 'package:flutter_filament_example/example_viewport.dart';
import 'package:flutter_filament_example/picker_result_widget.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 foo = String.fromEnvironment('foo');
void main() async {
print(loadDefaultScene);
print(foo);
runApp(const MyApp());
}
@@ -31,7 +28,15 @@ class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
@override
Widget build(BuildContext context) {
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,
home: const Scaffold(body: ExampleWidget()));
}
@@ -69,14 +74,6 @@ class ExampleWidgetState extends State<ExampleWidget> {
static bool hasSkybox = false;
static bool coneHidden = false;
static final assets = <FilamentEntity>[];
static FilamentEntity? flightHelmet;
static FilamentEntity? buster;
static List<String>? animations;
static FilamentEntity? directionalLight;
static bool loop = false;
static final showProjectionMatrices = ValueNotifier<bool>(false);
@@ -90,19 +87,16 @@ class ExampleWidgetState extends State<ExampleWidget> {
setState(() {
_filamentController = FilamentControllerFFI();
});
await Future.delayed(const Duration(milliseconds: 100));
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await _filamentController!.createViewer();
_createEntityLoadListener();
await _filamentController!
.loadSkybox("assets/default_env/default_env_skybox.ktx");
await _filamentController!.setRendering(true);
assets.add(
await _filamentController!.loadGlb("assets/shapes/shapes.glb"));
ExampleWidgetState.animations =
await _filamentController!.getAnimationNames(assets.first);
await _filamentController!.loadGlb("assets/shapes/shapes.glb");
await _filamentController!
.setCameraManipulatorOptions(zoomSpeed: 1.0);
@@ -119,55 +113,9 @@ class ExampleWidgetState extends State<ExampleWidget> {
_listener.cancel();
}
void _createEntityLoadListener() {
_listener =
_filamentController!.onLoad.listen((FilamentEntity entity) async {
assets.add(entity);
animations = await _filamentController!.getAnimationNames(entity);
if (mounted) {
setState(() {});
}
print(_filamentController!.getNameForEntity(entity) ?? "NAME NOT FOUND");
});
}
EntityTransformController? _transformController;
FilamentEntity? _controlled;
final _sharedFocusNode = FocusNode();
Widget _assetEntry(FilamentEntity entity) {
return Row(children: [
Text("Asset ${entity}"),
IconButton(
tooltip: "Transform to unit cube",
onPressed: () async {
await _filamentController!.transformToUnitCube(entity);
},
icon: const Icon(Icons.settings_overscan_outlined)),
IconButton(
onPressed: () async {
_transformController?.dispose();
if (_controlled == entity) {
_controlled = null;
} else {
_controlled = entity;
_transformController?.dispose();
_transformController =
EntityTransformController(_filamentController!, entity);
}
setState(() {});
},
icon: Icon(Icons.control_camera,
color: _controlled == entity ? Colors.green : Colors.black)),
IconButton(
onPressed: () async {
await _filamentController!.removeEntity(entity);
assets.remove(entity);
setState(() {});
},
icon: const Icon(Icons.cancel_sharp)),
]);
}
final _sharedFocusNode = FocusNode();
@override
Widget build(BuildContext context) {
@@ -179,26 +127,14 @@ class ExampleWidgetState extends State<ExampleWidget> {
padding: _viewportMargin,
keyboardFocusNode: _sharedFocusNode),
),
EntityListWidget(controller: _filamentController),
Positioned(
bottom: 80,
left: 10,
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,
bottom: 0,
left: 0,
right: 10,
height: 60,
height: 30,
child: Container(
height: 60,
height: 30,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: Colors.white.withOpacity(0.25),
@@ -209,11 +145,17 @@ class ExampleWidgetState extends State<ExampleWidget> {
ControllerMenu(
sharedFocusNode: _sharedFocusNode,
controller: _filamentController,
onToggleViewport: () {
setState(() {
_viewportMargin = (_viewportMargin == EdgeInsets.zero)
? const EdgeInsets.all(30)
: EdgeInsets.zero;
});
},
onControllerDestroyed: () {},
onControllerCreated: (controller) {
setState(() {
_filamentController = controller;
_createEntityLoadListener();
});
}),
SceneMenu(
@@ -222,27 +164,28 @@ class ExampleWidgetState extends State<ExampleWidget> {
),
GestureDetector(
onTap: () async {
await _filamentController!
.loadGlb('assets/shapes/shapes.glb');
await _filamentController!.loadGlb(
'assets/shapes/shapes.glb',
numInstances: 10);
},
child: Container(
color: Colors.transparent,
child: const Text("shapes.glb"))),
SizedBox(width: 5),
const SizedBox(width: 5),
GestureDetector(
onTap: () async {
await _filamentController!.loadGlb('assets/1.glb');
},
child: Container(
color: Colors.transparent, child: const Text("1.glb"))),
SizedBox(width: 5),
const SizedBox(width: 5),
GestureDetector(
onTap: () async {
await _filamentController!.loadGlb('assets/2.glb');
},
child: Container(
color: Colors.transparent, child: const Text("2.glb"))),
SizedBox(width: 5),
const SizedBox(width: 5),
GestureDetector(
onTap: () async {
await _filamentController!.loadGlb('assets/3.glb');
@@ -250,21 +193,11 @@ class ExampleWidgetState extends State<ExampleWidget> {
child: Container(
color: Colors.transparent, child: const Text("3.glb"))),
Expanded(child: Container()),
TextButton(
child: const Text("Toggle viewport size"),
onPressed: () {
setState(() {
_viewportMargin = (_viewportMargin == EdgeInsets.zero)
? const EdgeInsets.all(30)
: EdgeInsets.zero;
});
},
)
]))),
_filamentController == null
? Container()
: Padding(
padding: const EdgeInsets.only(top: 40, left: 20, right: 20),
padding: const EdgeInsets.only(top: 10, left: 20, right: 20),
child: ValueListenableBuilder(
valueListenable: showProjectionMatrices,
builder: (ctx, value, child) => CameraMatrixOverlay(

View File

@@ -1,11 +1,7 @@
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_filament/animations/animation_data.dart';
import 'package:flutter_filament/filament_controller.dart';
import 'package:flutter_filament/flutter_filament.dart';
import 'package:flutter_filament_example/main.dart';
import 'package:permission_handler/permission_handler.dart';
@@ -30,8 +26,8 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
MenuItemButton(
closeOnActivate: false,
onPressed: () async {
var entity = await widget.controller
.getChildEntity(ExampleWidgetState.assets.last, "Cylinder");
var entity = await widget.controller.getChildEntity(
widget.controller.scene.listEntities().last, "Cylinder");
await showDialog(
context: context,
builder: (BuildContext context) {
@@ -43,20 +39,30 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
child: const Text('Find Cylinder entity by name')),
MenuItemButton(
onPressed: () async {
Timer.periodic(const Duration(milliseconds: 50), (_) async {
await widget.controller.setBoneTransform(
ExampleWidgetState.assets.last,
"Cylinder",
"Bone",
Matrix4.rotationX(pi / 2));
});
await widget.controller.addBoneAnimation(
widget.controller.scene.listEntities().last,
BoneAnimationData([
"Bone"
], [
"Cylinder"
], [
[v.Quaternion.axisAngle(v.Vector3(1, 1, 1), pi / 2)]
], [
[v.Vector3.zero()]
], 16));
},
child:
const Text('Set bone transform for Cylinder (pi/2 rotation X)')),
MenuItemButton(
onPressed: () async {
await widget.controller
.resetBones(widget.controller.scene.listEntities().last);
},
child: const Text('Reset bones for Cylinder')),
MenuItemButton(
onPressed: () async {
await widget.controller.addBoneAnimation(
ExampleWidgetState.assets.last,
widget.controller.scene.listEntities().last,
BoneAnimationData(
["Bone"],
["Cylinder"],
@@ -72,9 +78,11 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
},
child: const Text('Set bone transform animation for Cylinder')),
MenuItemButton(
closeOnActivate: false,
onPressed: () async {
var names = await widget.controller.getMorphTargetNames(
ExampleWidgetState.assets.last, "Cylinder");
widget.controller.scene.listEntities().last, "Cylinder");
print("NAMES : $names");
await showDialog(
context: context,
builder: (ctx) {
@@ -89,7 +97,7 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
MenuItemButton(
onPressed: () async {
widget.controller.setMorphTargetWeights(
ExampleWidgetState.assets.last,
widget.controller.scene.listEntities().last,
"Cylinder",
List.filled(4, 1.0));
},
@@ -97,24 +105,26 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
MenuItemButton(
onPressed: () async {
widget.controller.setMorphTargetWeights(
ExampleWidgetState.assets.last,
widget.controller.scene.listEntities().last,
"Cylinder",
List.filled(4, 0.0));
},
child: const Text("Set Cylinder morph weights to 0")),
MenuItemButton(
onPressed: () async {
widget.controller
.setPosition(ExampleWidgetState.assets.last, 1.0, 1.0, -1.0);
widget.controller.setPosition(
widget.controller.scene.listEntities().last, 1.0, 1.0, -1.0);
},
child: const Text('Set position to 1, 1, -1'),
),
MenuItemButton(
onPressed: () async {
if (ExampleWidgetState.coneHidden) {
widget.controller.reveal(ExampleWidgetState.assets.last, "Cone");
widget.controller
.reveal(widget.controller.scene.listEntities().last, "Cone");
} else {
widget.controller.hide(ExampleWidgetState.assets.last, "Cone");
widget.controller
.hide(widget.controller.scene.listEntities().last, "Cone");
}
ExampleWidgetState.coneHidden = !ExampleWidgetState.coneHidden;
@@ -124,7 +134,10 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
MenuItemButton(
onPressed: () async {
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")),
MenuItemButton(
@@ -134,56 +147,52 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
child: Text(
"Toggle animation looping ${ExampleWidgetState.loop ? "OFF" : "ON"}"))
];
if (ExampleWidgetState.animations != null) {
children.addAll(ExampleWidgetState.animations!.map((a) => MenuItemButton(
onPressed: () {
widget.controller.playAnimation(ExampleWidgetState.assets.last,
ExampleWidgetState.animations!.indexOf(a),
replaceActive: true,
crossfade: 0.5,
loop: ExampleWidgetState.loop);
},
child: Text(
"play animation ${ExampleWidgetState.animations!.indexOf(a)} (replace/fade)"))));
children.addAll(ExampleWidgetState.animations!.map((a) => MenuItemButton(
onPressed: () {
widget.controller.playAnimation(ExampleWidgetState.assets.last,
ExampleWidgetState.animations!.indexOf(a),
replaceActive: false, loop: ExampleWidgetState.loop);
},
child: Text(
"Play animation ${ExampleWidgetState.animations!.indexOf(a)} (noreplace)"))));
}
return SubmenuButton(menuChildren: children, child: const Text("Shapes"));
}
Widget _geometrySubmenu() {
return MenuItemButton(
onPressed: () async {
await widget.controller.createGeometry([
-1,
0,
-1,
-1,
0,
1,
1,
0,
1,
1,
0,
-1,
], [
0,
1,
2,
2,
3,
0
], "asset://assets/solidcolor.filamat");
},
child: const Text("Custom geometry"));
return SubmenuButton(
menuChildren: [
MenuItemButton(
onPressed: () async {
var verts = [
-1.0,
0.0,
-1.0,
-1.0,
0.0,
1.0,
1.0,
0.0,
1.0,
1.0,
0.0,
-1.0,
];
var indices = [0, 1, 2, 2, 3, 0];
var geom = await widget.controller.createGeometry(verts, indices,
materialPath: "asset://assets/solidcolor.filamat");
},
child: const Text("Quad")),
MenuItemButton(
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
@@ -194,55 +203,27 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
_geometrySubmenu(),
MenuItemButton(
onPressed: () async {
ExampleWidgetState.directionalLight = await widget.controller
await widget.controller
.addLight(1, 6500, 150000, 0, 1, 0, 0, -1, 0, true);
},
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(
onPressed: () async {
await widget.controller.clearLights();
},
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);
ExampleWidgetState.animations = await widget.controller
.getAnimationNames(ExampleWidgetState.assets.last);
} 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(
onPressed: () {
widget.controller.setBackgroundColor(const Color(0xFF73C9FA));
widget.controller.setBackgroundColor(const Color(0xAA73C9FA));
},
child: const Text("Set background color")),
MenuItemButton(
@@ -283,9 +264,6 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
MenuItemButton(
onPressed: () async {
await widget.controller.clearEntities();
ExampleWidgetState.flightHelmet = null;
ExampleWidgetState.buster = null;
ExampleWidgetState.assets.clear();
},
child: const Text('Clear assets')),
],

View File

@@ -2,8 +2,9 @@ import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:vector_math/vector_math_64.dart' as v;
import 'package:flutter_filament/flutter_filament.dart';
import 'package:flutter_filament/filament_controller.dart';
import 'package:flutter_filament_example/main.dart';
class CameraSubmenu extends StatefulWidget {
@@ -99,27 +100,15 @@ class _CameraSubmenuState extends State<CameraSubmenu> {
MenuItemButton(
onPressed: () async {
widget.controller.setCameraPosition(0.0, 0.0, 0.0);
widget.controller.setCameraRotation(0, 0.0, 1.0, 0.0);
widget.controller.setCameraRotation(
v.Quaternion.axisAngle(v.Vector3(0, 0.0, 1.0), 0.0));
},
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(
onPressed: () {
widget.controller.setCameraRotation(pi / 4, 0.0, 1.0, 0.0);
widget.controller.setCameraRotation(
v.Quaternion.axisAngle(v.Vector3(0, 1, 0), pi / 4));
},
child: const Text("Rotate camera 45 degrees around y axis"),
),

View File

@@ -2,11 +2,12 @@ import 'dart:io';
import 'package:flutter/material.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 {
final FilamentController? controller;
final void Function() onToggleViewport;
final void Function(FilamentController controller) onControllerCreated;
final void Function() onControllerDestroyed;
final FocusNode sharedFocusNode;
@@ -15,7 +16,8 @@ class ControllerMenu extends StatefulWidget {
{this.controller,
required this.onControllerCreated,
required this.onControllerDestroyed,
required this.sharedFocusNode});
required this.sharedFocusNode,
required this.onToggleViewport});
@override
State<StatefulWidget> createState() => _ControllerMenuState();
@@ -60,6 +62,7 @@ class _ControllerMenuState extends State<ControllerMenu> {
? null
: () async {
await _filamentController!.createViewer();
await _filamentController!
.setCameraManipulatorOptions(zoomSpeed: 1.0);
},
@@ -100,7 +103,13 @@ class _ControllerMenuState extends State<ControllerMenu> {
}
return MenuAnchor(
childFocusNode: widget.sharedFocusNode,
menuChildren: items,
menuChildren: items +
[
TextButton(
child: const Text("Toggle viewport size"),
onPressed: widget.onToggleViewport,
)
],
builder:
(BuildContext context, MenuController controller, Widget? child) {
return TextButton(

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.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';
class RenderingSubmenu extends StatefulWidget {
@@ -64,6 +64,13 @@ class _RenderingSubmenuState extends State<RenderingSubmenu> {
child: Text(
"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"),
);

View File

@@ -1,6 +1,6 @@
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/camera_submenu.dart';
import 'package:flutter_filament_example/menus/rendering_submenu.dart';

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_filament/filament_controller.dart';
import 'package:flutter_filament/flutter_filament.dart';
class PickerResultWidget extends StatelessWidget {
final FilamentController controller;
@@ -9,11 +9,8 @@ class PickerResultWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: controller.pickResult.map((FilamentEntity? entityId) {
if (entityId == null) {
return null;
}
return controller.getNameForEntity(entityId);
stream: controller.pickResult.map((result) {
return controller.getNameForEntity(result.entity);
}),
builder: (ctx, snapshot) => snapshot.data == null
? Container()

View File

@@ -35,17 +35,16 @@
#include "SceneManager.hpp"
#include "ThreadPool.hpp"
using namespace std;
using namespace filament;
using namespace filament::math;
using namespace gltfio;
using namespace camutils;
typedef int32_t EntityId;
namespace polyvox
namespace flutter_filament
{
typedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point_t;
using namespace std::chrono;
using namespace filament;
using namespace filament::math;
using namespace gltfio;
using namespace camutils;
enum ToneMapping
{
@@ -56,6 +55,8 @@ namespace polyvox
class FilamentViewer
{
typedef int32_t EntityId;
public:
FilamentViewer(const void *context, const ResourceLoaderWrapper *const resourceLoaderWrapper, void *const platform = nullptr, const char *uberArchivePath = nullptr);
~FilamentViewer();
@@ -83,6 +84,7 @@ namespace polyvox
bool setCamera(EntityId asset, const char *nodeName);
void setMainCamera();
EntityId getMainCamera();
void setCameraFov(double fovDegrees, double aspect);
void createSwapChain(const void *surface, uint32_t width, uint32_t height);
@@ -103,7 +105,7 @@ namespace polyvox
void setViewFrustumCulling(bool enabled);
void setCameraExposure(float aperture, float shutterSpeed, float sensitivity);
void setCameraPosition(float x, float y, float z);
void setCameraRotation(float rads, float x, float y, float z);
void setCameraRotation(float w, float x, float y, float z);
const math::mat4 getCameraModelMatrix();
const math::mat4 getCameraViewMatrix();
const math::mat4 getCameraProjectionMatrix();
@@ -123,7 +125,7 @@ namespace polyvox
void scrollBegin();
void scrollUpdate(float x, float y, float delta);
void scrollEnd();
void pick(uint32_t x, uint32_t y, EntityId *entityId);
void pick(uint32_t x, uint32_t y, void (*callback)(EntityId entityId, int x, int y));
EntityId addLight(LightManager::Type t, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows);
void removeLight(EntityId entityId);
@@ -134,8 +136,9 @@ namespace polyvox
void setRecordingOutputDirectory(const char* path);
void setAntiAliasing(bool msaaEnabled, bool fxaaEnabled, bool taaEnabled);
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()
{
@@ -158,11 +161,9 @@ namespace polyvox
SceneManager *_sceneManager = nullptr;
NameComponentManager *_ncm = nullptr;
std::mutex mtx; // mutex to ensure thread safety when removing assets
vector<utils::Entity> _lights;
std::vector<utils::Entity> _lights;
Texture *_skyboxTexture = nullptr;
Skybox *_skybox = nullptr;
Texture *_iblTexture = nullptr;
@@ -199,10 +200,10 @@ namespace polyvox
IndexBuffer *_imageIb = nullptr;
Material *_imageMaterial = nullptr;
TextureSampler _imageSampler;
void loadKtx2Texture(string path, ResourceBuffer data);
void loadKtxTexture(string path, ResourceBuffer data);
void loadPngTexture(string path, ResourceBuffer data);
void loadTextureFromPath(string path);
void loadKtx2Texture(std::string path, ResourceBuffer data);
void loadKtxTexture(std::string path, ResourceBuffer data);
void loadPngTexture(std::string path, ResourceBuffer data);
void loadTextureFromPath(std::string path);
void savePng(void* data, size_t size, int frameNumber);
time_point_t _recordingStartTime = std::chrono::high_resolution_clock::now();

View File

@@ -73,9 +73,14 @@ extern "C"
FLUTTER_PLUGIN_EXPORT EntityId add_light(const void *const viewer, uint8_t type, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows);
FLUTTER_PLUGIN_EXPORT void remove_light(const void *const viewer, EntityId entityId);
FLUTTER_PLUGIN_EXPORT void clear_lights(const void *const viewer);
FLUTTER_PLUGIN_EXPORT EntityId load_glb(void *sceneManager, const char *assetPath, bool unlit);
FLUTTER_PLUGIN_EXPORT EntityId load_glb(void *sceneManager, const char *assetPath, int numInstances);
FLUTTER_PLUGIN_EXPORT EntityId load_glb_from_buffer(void *sceneManager, const void* const data, size_t length);
FLUTTER_PLUGIN_EXPORT EntityId load_gltf(void *sceneManager, const char *assetPath, const char *relativePath);
FLUTTER_PLUGIN_EXPORT EntityId create_instance(void *sceneManager, EntityId id);
FLUTTER_PLUGIN_EXPORT int get_instance_count(void *sceneManager, EntityId entityId);
FLUTTER_PLUGIN_EXPORT void get_instances(void *sceneManager, EntityId entityId, EntityId *out);
FLUTTER_PLUGIN_EXPORT void set_main_camera(const void *const viewer);
FLUTTER_PLUGIN_EXPORT EntityId get_main_camera(const void *const viewer);
FLUTTER_PLUGIN_EXPORT bool set_camera(const void *const viewer, EntityId asset, const char *nodeName);
FLUTTER_PLUGIN_EXPORT void set_view_frustum_culling(const void *const viewer, bool enabled);
FLUTTER_PLUGIN_EXPORT void render(
@@ -159,7 +164,7 @@ extern "C"
FLUTTER_PLUGIN_EXPORT void set_camera_exposure(const void *const viewer, float aperture, float shutterSpeed, float sensitivity);
FLUTTER_PLUGIN_EXPORT void set_camera_position(const void *const viewer, float x, float y, float z);
FLUTTER_PLUGIN_EXPORT void get_camera_position(const void *const viewer);
FLUTTER_PLUGIN_EXPORT void set_camera_rotation(const void *const viewer, float rads, float x, float y, float z);
FLUTTER_PLUGIN_EXPORT void set_camera_rotation(const void *const viewer, float w, float x, float y, float z);
FLUTTER_PLUGIN_EXPORT void set_camera_model_matrix(const void *const viewer, const float *const matrix);
FLUTTER_PLUGIN_EXPORT const double *const get_camera_model_matrix(const void *const viewer);
FLUTTER_PLUGIN_EXPORT const double *const get_camera_view_matrix(const void *const viewer);
@@ -179,7 +184,7 @@ extern "C"
FLUTTER_PLUGIN_EXPORT int reveal_mesh(void *sceneManager, EntityId asset, const char *meshName);
FLUTTER_PLUGIN_EXPORT void set_post_processing(void *const viewer, bool enabled);
FLUTTER_PLUGIN_EXPORT void set_antialiasing(void *const viewer, bool msaa, bool fxaa, bool taa);
FLUTTER_PLUGIN_EXPORT void pick(void *const viewer, int x, int y, EntityId *entityId);
FLUTTER_PLUGIN_EXPORT void pick(void *const viewer, int x, int y, void (*callback)(EntityId entityId, int x, int y));
FLUTTER_PLUGIN_EXPORT const char *get_name_for_entity(void *const sceneManager, const EntityId entityId);
FLUTTER_PLUGIN_EXPORT EntityId find_child_entity_by_name(void *const sceneManager, const EntityId parent, const char* name);
FLUTTER_PLUGIN_EXPORT int get_entity_count(void *const sceneManager, const EntityId target, bool renderableOnly);
@@ -189,10 +194,14 @@ extern "C"
FLUTTER_PLUGIN_EXPORT void ios_dummy();
FLUTTER_PLUGIN_EXPORT void flutter_filament_free(void *ptr);
FLUTTER_PLUGIN_EXPORT void add_collision_component(void *const sceneManager, EntityId entityId, void (*callback)(const EntityId entityId1, const EntityId entityId2), bool affectsCollidingTransform);
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 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 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
}

View File

@@ -4,96 +4,118 @@
#include "FlutterFilamentApi.h"
#ifdef __cplusplus
extern "C" {
extern "C"
{
#endif
///
/// This header replicates most of the methods in FlutterFilamentApi.h, and is only intended to be used to generate client FFI bindings.
/// The intention is that calling one of these methods will call its respective method in FlutterFilamentApi.h, but wrapped in some kind of thread runner to ensure thread safety.
///
///
/// This header replicates most of the methods in FlutterFilamentApi.h, and is only intended to be used to generate client FFI bindings.
/// The intention is that calling one of these methods will call its respective method in FlutterFilamentApi.h, but wrapped in some kind of thread runner to ensure thread safety.
///
typedef int32_t EntityId;
typedef void (*FilamentRenderCallback)(void* const owner);
typedef int32_t EntityId;
typedef void (*FilamentRenderCallback)(void *const owner);
FLUTTER_PLUGIN_EXPORT void* const create_filament_viewer_ffi(void* const context, void* const platform, const char* uberArchivePath, const ResourceLoaderWrapper* const loader, void (*renderCallback)(void* const renderCallbackOwner), void* const renderCallbackOwner);
FLUTTER_PLUGIN_EXPORT void create_swap_chain_ffi(void* const viewer, void* const surface, uint32_t width, uint32_t height);
FLUTTER_PLUGIN_EXPORT void destroy_swap_chain_ffi(void* const viewer);
FLUTTER_PLUGIN_EXPORT void create_render_target_ffi(void* const viewer, intptr_t nativeTextureId, uint32_t width, uint32_t height);
FLUTTER_PLUGIN_EXPORT void destroy_filament_viewer_ffi(void* const viewer);
FLUTTER_PLUGIN_EXPORT void render_ffi(void* const viewer);
FLUTTER_PLUGIN_EXPORT FilamentRenderCallback make_render_callback_fn_pointer(FilamentRenderCallback);
FLUTTER_PLUGIN_EXPORT void set_rendering_ffi(void* const viewer, bool rendering);
FLUTTER_PLUGIN_EXPORT void set_frame_interval_ffi(float frameInterval);
FLUTTER_PLUGIN_EXPORT void update_viewport_and_camera_projection_ffi(void* const viewer, const uint32_t width, const uint32_t height, const float scaleFactor);
FLUTTER_PLUGIN_EXPORT void set_background_color_ffi(void* const viewer, const float r, const float g, const float b, const float a);
FLUTTER_PLUGIN_EXPORT void clear_background_image_ffi(void* const viewer);
FLUTTER_PLUGIN_EXPORT void set_background_image_ffi(void* const viewer, const char *path, bool fillHeight);
FLUTTER_PLUGIN_EXPORT void set_background_image_position_ffi(void* const viewer, float x, float y, bool clamp);
FLUTTER_PLUGIN_EXPORT void set_tone_mapping_ffi(void* const viewer, int toneMapping);
FLUTTER_PLUGIN_EXPORT void set_bloom_ffi(void* const viewer, float strength);
FLUTTER_PLUGIN_EXPORT void load_skybox_ffi(void* const viewer, const char *skyboxPath);
FLUTTER_PLUGIN_EXPORT void load_ibl_ffi(void* const viewer, const char *iblPath, float intensity);
FLUTTER_PLUGIN_EXPORT void remove_skybox_ffi(void* const viewer);
FLUTTER_PLUGIN_EXPORT void remove_ibl_ffi(void* const viewer);
FLUTTER_PLUGIN_EXPORT EntityId add_light_ffi(void* const viewer, uint8_t type, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows);
FLUTTER_PLUGIN_EXPORT void remove_light_ffi(void* const viewer, EntityId entityId);
FLUTTER_PLUGIN_EXPORT void clear_lights_ffi(void* const viewer);
FLUTTER_PLUGIN_EXPORT EntityId load_glb_ffi(void* const assetManager, const char *assetPath, bool unlit);
FLUTTER_PLUGIN_EXPORT EntityId load_gltf_ffi(void* const assetManager, const char *assetPath, const char *relativePath);
FLUTTER_PLUGIN_EXPORT void remove_entity_ffi(void* const viewer, EntityId asset);
FLUTTER_PLUGIN_EXPORT void clear_entities_ffi(void* const viewer);
FLUTTER_PLUGIN_EXPORT bool set_camera_ffi(void* const viewer, EntityId asset, const char *nodeName);
FLUTTER_PLUGIN_EXPORT void apply_weights_ffi(
void* const assetManager,
EntityId asset,
const char *const entityName,
float *const weights,
int count
);
FLUTTER_PLUGIN_EXPORT void create_filament_viewer_ffi(
void *const context,
void *const platform,
const char *uberArchivePath,
const ResourceLoaderWrapper *const loader,
void (*renderCallback)(void *const renderCallbackOwner),
void *const renderCallbackOwner,
void (*callback)(void* const viewer)
);
FLUTTER_PLUGIN_EXPORT void create_swap_chain_ffi(void *const viewer, void *const surface, uint32_t width, uint32_t height, void (*onComplete)());
FLUTTER_PLUGIN_EXPORT void destroy_swap_chain_ffi(void *const viewer, void (*onComplete)());
FLUTTER_PLUGIN_EXPORT void create_render_target_ffi(void *const viewer, intptr_t nativeTextureId, uint32_t width, uint32_t height, void (*onComplete)());
FLUTTER_PLUGIN_EXPORT void destroy_filament_viewer_ffi(void *const viewer);
FLUTTER_PLUGIN_EXPORT void render_ffi(void *const viewer);
FLUTTER_PLUGIN_EXPORT FilamentRenderCallback make_render_callback_fn_pointer(FilamentRenderCallback);
FLUTTER_PLUGIN_EXPORT void set_rendering_ffi(void *const viewer, bool rendering);
FLUTTER_PLUGIN_EXPORT void set_frame_interval_ffi(float frameInterval);
FLUTTER_PLUGIN_EXPORT void update_viewport_and_camera_projection_ffi(void *const viewer, const uint32_t width, const uint32_t height, const float scaleFactor, void (*onComplete)());
FLUTTER_PLUGIN_EXPORT void set_background_color_ffi(void *const viewer, const float r, const float g, const float b, const float a);
FLUTTER_PLUGIN_EXPORT void clear_background_image_ffi(void *const viewer);
FLUTTER_PLUGIN_EXPORT void set_background_image_ffi(void *const viewer, const char *path, bool fillHeight);
FLUTTER_PLUGIN_EXPORT void set_background_image_position_ffi(void *const viewer, float x, float y, bool clamp);
FLUTTER_PLUGIN_EXPORT void set_tone_mapping_ffi(void *const viewer, int toneMapping);
FLUTTER_PLUGIN_EXPORT void set_bloom_ffi(void *const viewer, float strength);
FLUTTER_PLUGIN_EXPORT void load_skybox_ffi(void *const viewer, const char *skyboxPath);
FLUTTER_PLUGIN_EXPORT void load_ibl_ffi(void *const viewer, const char *iblPath, float intensity);
FLUTTER_PLUGIN_EXPORT void remove_skybox_ffi(void *const viewer);
FLUTTER_PLUGIN_EXPORT void remove_ibl_ffi(void *const viewer);
FLUTTER_PLUGIN_EXPORT void add_light_ffi(
void *const viewer,
uint8_t type,
float colour,
float intensity,
float posX,
float posY,
float posZ,
float dirX,
float dirY,
float dirZ,
bool shadows,
void (*callback)(EntityId));
FLUTTER_PLUGIN_EXPORT void remove_light_ffi(void *const viewer, EntityId entityId);
FLUTTER_PLUGIN_EXPORT void clear_lights_ffi(void *const viewer);
FLUTTER_PLUGIN_EXPORT void load_glb_ffi(void *const sceneManager, const char *assetPath, int numInstances, void (*callback)(EntityId));
FLUTTER_PLUGIN_EXPORT void load_glb_from_buffer_ffi(void *const sceneManager, const void *const data, size_t length, int numInstances, void (*callback)(EntityId));
FLUTTER_PLUGIN_EXPORT void load_gltf_ffi(void *const sceneManager, const char *assetPath, const char *relativePath, void (*callback)(EntityId));
FLUTTER_PLUGIN_EXPORT void create_instance_ffi(void *const sceneManager, EntityId entityId, void (*callback)(EntityId));
FLUTTER_PLUGIN_EXPORT void remove_entity_ffi(void *const viewer, EntityId asset, void (*callback)());
FLUTTER_PLUGIN_EXPORT void clear_entities_ffi(void *const viewer, void (*callback)());
FLUTTER_PLUGIN_EXPORT void set_camera_ffi(void *const viewer, EntityId asset, const char *nodeName, void (*callback)(bool));
FLUTTER_PLUGIN_EXPORT void apply_weights_ffi(
void *const sceneManager,
EntityId asset,
const char *const entityName,
float *const weights,
int count);
FLUTTER_PLUGIN_EXPORT void play_animation_ffi(void* const assetManager, EntityId asset, int index, bool loop, bool reverse, bool replaceActive, float crossfade);
FLUTTER_PLUGIN_EXPORT void set_animation_frame_ffi(void* const assetManager, EntityId asset, int animationIndex, int animationFrame);
FLUTTER_PLUGIN_EXPORT void stop_animation_ffi(void* const assetManager, EntityId asset, int index);
FLUTTER_PLUGIN_EXPORT int get_animation_count_ffi(void* const assetManager, EntityId asset);
FLUTTER_PLUGIN_EXPORT void get_animation_name_ffi(void* const assetManager, EntityId asset, char *const outPtr, int index);
FLUTTER_PLUGIN_EXPORT void get_morph_target_name_ffi(void* const assetManager, EntityId asset, const char *meshName, char *const outPtr, int index);
FLUTTER_PLUGIN_EXPORT int get_morph_target_name_count_ffi(void* const assetManager, EntityId asset, const char *meshName);
FLUTTER_PLUGIN_EXPORT void set_morph_target_weights_ffi(void* const assetManager,
EntityId asset,
const char *const entityName,
const float *const morphData,
int numWeights
);
FLUTTER_PLUGIN_EXPORT bool set_morph_animation_ffi(
void *assetManager,
EntityId asset,
const char *const entityName,
const float *const morphData,
const int *const morphIndices,
int numMorphTargets,
int numFrames,
float frameLengthInMs);
FLUTTER_PLUGIN_EXPORT bool set_bone_transform_ffi(
void *assetManager,
EntityId asset,
const char *entityName,
const float *const transform,
const char *boneName);
FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi(
void *assetManager,
EntityId asset,
const float *const frameData,
int numFrames,
const char *const boneName,
const char **const meshNames,
int numMeshTargets,
float frameLengthInMs,
bool isModelSpace);
FLUTTER_PLUGIN_EXPORT void set_post_processing_ffi(void* const viewer, bool enabled);
FLUTTER_PLUGIN_EXPORT void pick_ffi(void* const viewer, int x, int y, EntityId* entityId);
FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose_ffi(void* const assetManager, EntityId entityId);
FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi();
FLUTTER_PLUGIN_EXPORT EntityId create_geometry_ffi(void* const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath);
FLUTTER_PLUGIN_EXPORT void play_animation_ffi(void *const sceneManager, EntityId asset, int index, bool loop, bool reverse, bool replaceActive, float crossfade);
FLUTTER_PLUGIN_EXPORT void set_animation_frame_ffi(void *const sceneManager, EntityId asset, int animationIndex, int animationFrame);
FLUTTER_PLUGIN_EXPORT void stop_animation_ffi(void *const sceneManager, EntityId asset, int index);
FLUTTER_PLUGIN_EXPORT void get_animation_count_ffi(void *const sceneManager, EntityId asset, void (*callback)(int));
FLUTTER_PLUGIN_EXPORT void get_animation_name_ffi(void *const sceneManager, EntityId asset, char *const outPtr, int index, void (*callback)());
FLUTTER_PLUGIN_EXPORT void get_morph_target_name_ffi(void *const sceneManager, EntityId asset, const char *meshName, char *const outPtr, int index, void (*callback)());
FLUTTER_PLUGIN_EXPORT void get_morph_target_name_count_ffi(void *const sceneManager, EntityId asset, const char *meshName, void (*callback)(int32_t));
FLUTTER_PLUGIN_EXPORT void set_morph_target_weights_ffi(void *const sceneManager,
EntityId asset,
const char *const entityName,
const float *const morphData,
int numWeights);
FLUTTER_PLUGIN_EXPORT void set_morph_animation_ffi(
void *sceneManager,
EntityId asset,
const char *const entityName,
const float *const morphData,
const int *const morphIndices,
int numMorphTargets,
int numFrames,
float frameLengthInMs,
void (*callback)(bool));
FLUTTER_PLUGIN_EXPORT void set_bone_transform_ffi(
void *sceneManager,
EntityId asset,
const char *entityName,
const float *const transform,
const char *boneName,
void (*callback)(bool));
FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi(
void *sceneManager,
EntityId asset,
const float *const frameData,
int numFrames,
const char *const boneName,
const char **const meshNames,
int numMeshTargets,
float frameLengthInMs,
bool isModelSpace);
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 ios_dummy_ffi();
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
}

View File

@@ -1,93 +0,0 @@
#pragma once
#include "Log.hpp"
#include <filament/Engine.h>
#include <filament/RenderableManager.h>
#include <filament/Renderer.h>
#include <filament/Scene.h>
#include <filament/Texture.h>
#include <filament/TransformManager.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <math/mat3.h>
#include <math/norm.h>
#include <gltfio/Animator.h>
#include <gltfio/AssetLoader.h>
#include <gltfio/ResourceLoader.h>
#include <utils/NameComponentManager.h>
extern "C" {
#include "FlutterFilamentApi.h"
}
template class std::vector<float>;
namespace polyvox {
using namespace filament;
using namespace filament::gltfio;
using namespace utils;
using namespace std;
typedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point_t;
enum AnimationType {
MORPH, BONE, GLTF
};
struct AnimationStatus {
time_point_t start = time_point_t::max();
bool loop = false;
bool reverse = false;
float durationInSecs = 0;
};
struct GltfAnimation : AnimationStatus {
int index = -1;
};
//
// Use this to construct a dynamic (i.e. non-glTF embedded) morph target animation.
//
struct MorphAnimation : AnimationStatus {
utils::Entity meshTarget;
int numFrames = -1;
float frameLengthInMs = 0;
vector<float> frameData;
vector<int> morphIndices;
int lengthInFrames;
};
//
// Use this to construct a dynamic (i.e. non-glTF embedded) bone/joint animation.
//
struct BoneAnimation : AnimationStatus {
size_t boneIndex;
vector<utils::Entity> meshTargets;
size_t skinIndex = 0;
int lengthInFrames;
float frameLengthInMs = 0;
vector<math::mat4f> frameData;
};
struct SceneAsset {
FilamentAsset* asset = nullptr;
vector<math::mat4f> initialJointTransforms;
vector<GltfAnimation> gltfAnimations;
vector<MorphAnimation> morphAnimations;
vector<BoneAnimation> boneAnimations;
// the index of the last active glTF animation,
// used to cross-fade
int fadeGltfAnimationIndex = -1;
float fadeDuration = 0.0f;
float fadeOutAnimationStart = 0.0f;
// a slot to preload textures
filament::Texture* texture = nullptr;
SceneAsset(
FilamentAsset* asset
) : asset(asset) {}
};
}

View File

@@ -1,41 +1,68 @@
#pragma once
#include <mutex>
#include <vector>
#include <memory>
#include <map>
#include <filament/Scene.h>
#include <gltfio/AssetLoader.h>
#include <gltfio/FilamentAsset.h>
#include <gltfio/FilamentInstance.h>
#include <gltfio/ResourceLoader.h>
#include "SceneAsset.hpp"
#include <filament/IndexBuffer.h>
#include <filament/InstanceBuffer.h>
#include "material/gizmo.h"
#include "utils/NameComponentManager.h"
#include "ResourceBuffer.hpp"
#include "components/StandardComponents.h"
#include "components/CollisionComponentManager.hpp"
#include "components/AnimationComponentManager.hpp"
typedef int32_t EntityId;
#include "tsl/robin_map.h"
namespace polyvox
namespace flutter_filament
{
typedef int32_t EntityId;
using namespace filament;
using namespace filament::gltfio;
using namespace utils;
using std::vector;
using std::unique_ptr;
using std::string;
class SceneManager
{
public:
SceneManager(const ResourceLoaderWrapper *const loader,
NameComponentManager *ncm,
Engine *engine,
Scene *scene,
const char *uberArchivePath);
~SceneManager();
EntityId loadGltf(const char *uri, const char *relativeResourcePath);
EntityId loadGlb(const char *uri, bool unlit);
FilamentAsset *getAssetByEntityId(EntityId entityId);
////
/// @brief Load the GLB from the specified path, optionally creating multiple instances.
/// @param uri the path to the asset. Should be either asset:// (representing a Flutter asset), or file:// (representing a filesystem file).
/// @param numInstances the number of instances to create.
/// @return an Entity representing the FilamentAsset associated with the loaded FilamentAsset.
///
EntityId loadGlb(const char *uri, int numInstances);
EntityId loadGlbFromBuffer(const uint8_t* data, size_t length, int numInstances=1);
EntityId createInstance(EntityId entityId);
void remove(EntityId entity);
void destroyAll();
unique_ptr<vector<string>> getAnimationNames(EntityId entity);
float getAnimationDuration(EntityId entity, int animationIndex);
unique_ptr<vector<string>> getMorphTargetNames(EntityId entity, const char *meshName);
unique_ptr<vector<string>> getMorphTargetNames(EntityId entity, const char *name);
void transformToUnitCube(EntityId e);
inline void updateTransform(EntityId e);
void setScale(EntityId e, float scale);
@@ -45,8 +72,8 @@ namespace polyvox
void queueRotationUpdate(EntityId e, float rads, float x, float y, float z, float w, bool relative);
const utils::Entity *getCameraEntities(EntityId e);
size_t getCameraEntityCount(EntityId e);
const utils::Entity *getLightEntities(EntityId e) const noexcept;
size_t getLightEntityCount(EntityId e) const noexcept;
const utils::Entity *getLightEntities(EntityId e) noexcept;
size_t getLightEntityCount(EntityId e) noexcept;
void updateAnimations();
void updateTransforms();
void testCollisions(EntityId entity);
@@ -80,7 +107,7 @@ namespace polyvox
/// @param meshName an array of mesh names under [entity] that should be animated
/// @param numMeshTargets the number of meshes under [meshName]
/// @param frameLengthInMs the length of each frame in ms
/// @return
/// @return true if the bone animation was successfully enqueued
bool addBoneAnimation(
EntityId entity,
const float *const frameData,
@@ -107,30 +134,67 @@ namespace polyvox
void addCollisionComponent(EntityId entity, void (*onCollisionCallback)(const EntityId entityId1, const EntityId entityId2), bool affectsCollidingTransform);
void removeCollisionComponent(EntityId entityId);
void setParent(EntityId child, EntityId parent);
void addAnimationComponent(EntityId entity);
/// @brief returns the number of instances of the FilamentAsset represented by the given entity.
/// @param entityId
/// @return the number of instances
int getInstanceCount(EntityId entityId);
/// @brief returns an array containing all instances of the FilamentAsset represented by the given entity.
/// @param entityId
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;
private:
AssetLoader *_assetLoader = nullptr;
gltfio::AssetLoader *_assetLoader = nullptr;
const ResourceLoaderWrapper *const _resourceLoaderWrapper;
NameComponentManager *_ncm = nullptr;
Engine *_engine;
Scene *_scene;
MaterialProvider *_ubershaderProvider = nullptr;
gltfio::MaterialProvider *_ubershaderProvider = nullptr;
gltfio::ResourceLoader *_gltfResourceLoader = nullptr;
gltfio::TextureProvider *_stbDecoder = nullptr;
gltfio::TextureProvider *_ktxDecoder = nullptr;
std::mutex _mutex;
Material* _gizmoMaterial;
vector<SceneAsset> _assets;
tsl::robin_map<EntityId, int> _entityIdLookup;
utils::NameComponentManager* _ncm;
tsl::robin_map<
EntityId,
gltfio::FilamentInstance*> _instances;
tsl::robin_map<EntityId, gltfio::FilamentAsset*> _assets;
tsl::robin_map<EntityId, std::tuple<math::float3,bool,math::quatf,bool,float>> _transformUpdates;
std::vector<EntityId> _nonTransformableCollidableEntities;
AnimationComponentManager* _animationComponentManager = nullptr;
CollisionComponentManager* _collisionComponentManager = nullptr;
gltfio::FilamentInstance* getInstanceByEntityId(EntityId entityId);
gltfio::FilamentAsset* getAssetByEntityId(EntityId entityId);
utils::Entity findEntityByName(
SceneAsset asset,
const gltfio::FilamentInstance* instance,
const char *entityName);
EntityId addGizmo();
utils::Entity _gizmoX;
utils::Entity _gizmoY;
utils::Entity _gizmoZ;
};
}

View File

@@ -5,9 +5,8 @@
#include <cassert>
#include <cstring>
namespace polyvox {
namespace flutter_filament {
using namespace std;
//
// A generic adapter to expose any contiguous section of memory as a std::streambuf.
@@ -20,15 +19,15 @@ namespace polyvox {
~StreamBufferAdapter() {
}
streamsize size();
std::streamsize size();
private:
int_type uflow() override;
int_type underflow() override;
int_type pbackfail(int_type ch) override;
streampos seekoff(streamoff off, ios_base::seekdir way, ios_base::openmode which) override;
streampos seekpos(streampos sp, ios_base::openmode which) override;
streamsize showmanyc() override;
std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which) override;
std::streampos seekpos(std::streampos sp, std::ios_base::openmode which) override;
std::streamsize showmanyc() override;
};

View File

@@ -0,0 +1,246 @@
#pragma once
#include "Log.hpp"
#include <chrono>
#include <filament/Engine.h>
#include <filament/RenderableManager.h>
#include <filament/Renderer.h>
#include <filament/Scene.h>
#include <filament/Texture.h>
#include <filament/TransformManager.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <math/mat3.h>
#include <math/norm.h>
#include <gltfio/Animator.h>
#include <gltfio/AssetLoader.h>
#include <gltfio/ResourceLoader.h>
#include <utils/NameComponentManager.h>
template class std::vector<float>;
namespace flutter_filament {
using namespace filament;
using namespace filament::gltfio;
using namespace utils;
using namespace std::chrono;
typedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point_t;
enum AnimationType {
MORPH, BONE, GLTF
};
struct AnimationStatus {
time_point_t start = time_point_t::max();
bool loop = false;
bool reverse = false;
float durationInSecs = 0;
};
struct GltfAnimation : AnimationStatus {
int index = -1;
};
//
// Use this to construct a dynamic (i.e. non-glTF embedded) morph target animation.
//
struct MorphAnimation : AnimationStatus {
utils::Entity meshTarget;
int numFrames = -1;
float frameLengthInMs = 0;
std::vector<float> frameData;
std::vector<int> morphIndices;
int lengthInFrames;
};
//
// Use this to construct a dynamic (i.e. non-glTF embedded) bone/joint animation.
//
struct BoneAnimation : AnimationStatus {
size_t boneIndex;
std::vector<utils::Entity> meshTargets;
size_t skinIndex = 0;
int lengthInFrames;
float frameLengthInMs = 0;
std::vector<math::mat4f> frameData;
};
struct AnimationComponent {
FilamentInstance* instance;
std::vector<math::mat4f> initialJointTransforms;
std::vector<GltfAnimation> gltfAnimations;
std::vector<MorphAnimation> morphAnimations;
std::vector<BoneAnimation> boneAnimations;
// the index of the last active glTF animation,
// used to cross-fade
int fadeGltfAnimationIndex = -1;
float fadeDuration = 0.0f;
float fadeOutAnimationStart = 0.0f;
};
class AnimationComponentManager : public utils::SingleInstanceComponentManager<AnimationComponent> {
filament::TransformManager& _transformManager;
filament::RenderableManager& _renderableManager;
public:
AnimationComponentManager(
filament::TransformManager& transformManager,
filament::RenderableManager& renderableManager) :
_transformManager(transformManager),
_renderableManager(renderableManager) {};
void addAnimationComponent(FilamentInstance* instance) {
const auto joints = instance->getJointsAt(0);
AnimationComponent animationComponent;
animationComponent.instance = instance;
for(int i = 0; i < instance->getJointCountAt(0); i++) {
const auto joint = joints[i];
const auto& jointTransformInstance = _transformManager.getInstance(joint);
const auto& jointTransform = _transformManager.getTransform(jointTransformInstance);
animationComponent.initialJointTransforms.push_back(jointTransform);
}
auto componentInstance = addComponent(instance->getRoot());
this->elementAt<0>(componentInstance) = animationComponent;
}
void update()
{
auto now = high_resolution_clock::now();
// Log("animation component count : %d", )
for(auto it = begin(); it < end(); it++) {
const auto& entity = getEntity(it);
auto componentInstance = getInstance(entity);
auto& animationComponent = elementAt<0>(componentInstance);
auto animator = animationComponent.instance->getAnimator();
auto& gltfAnimations = animationComponent.gltfAnimations;
auto& morphAnimations = animationComponent.morphAnimations;
auto& boneAnimations = animationComponent.boneAnimations;
for (int i = ((int)gltfAnimations.size()) - 1; i >= 0; i--) {
auto animationStatus = animationComponent.gltfAnimations[i];
auto elapsedInSecs = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - animationStatus.start).count()) / 1000.0f;
if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs)
{
animator->applyAnimation(animationStatus.index, animationStatus.durationInSecs - 0.001);
animator->updateBoneMatrices();
gltfAnimations.erase(gltfAnimations.begin() + i);
animationComponent.fadeGltfAnimationIndex = -1;
continue;
}
animator->applyAnimation(animationStatus.index, elapsedInSecs);
if (animationComponent.fadeGltfAnimationIndex != -1 && elapsedInSecs < animationComponent.fadeDuration)
{
// cross-fade
auto fadeFromTime = animationComponent.fadeOutAnimationStart + elapsedInSecs;
auto alpha = elapsedInSecs / animationComponent.fadeDuration;
animator->applyCrossFade(animationComponent.fadeGltfAnimationIndex, fadeFromTime, alpha);
}
}
animator->updateBoneMatrices();
for (int i = (int)morphAnimations.size() - 1; i >= 0; i--) {
auto animationStatus = morphAnimations[i];
auto elapsedInSecs = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - animationStatus.start).count()) / 1000.0f;
if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs)
{
morphAnimations.erase(morphAnimations.begin() + i);
continue;
}
int frameNumber = static_cast<int>(elapsedInSecs * 1000.0f / animationStatus.frameLengthInMs) % animationStatus.lengthInFrames;
// offset from the end if reverse
if (animationStatus.reverse)
{
frameNumber = animationStatus.lengthInFrames - frameNumber;
}
auto baseOffset = frameNumber * animationStatus.morphIndices.size();
for (int i = 0; i < animationStatus.morphIndices.size(); i++)
{
auto morphIndex = animationStatus.morphIndices[i];
// set the weights appropriately
_renderableManager.setMorphWeights(
_renderableManager.getInstance(animationStatus.meshTarget),
animationStatus.frameData.data() + baseOffset + i,
1,
morphIndex);
}
}
for (int i = (int)boneAnimations.size() - 1; i >= 0; i--) {
auto animationStatus = boneAnimations[i];
auto elapsedInSecs = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - animationStatus.start).count()) / 1000.0f;
if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs)
{
boneAnimations.erase(boneAnimations.begin() + i);
continue;
}
float elapsedFrames = elapsedInSecs * 1000.0f / animationStatus.frameLengthInMs;
int currFrame = static_cast<int>(elapsedFrames) % animationStatus.lengthInFrames;
float delta = elapsedFrames - currFrame;
int nextFrame = currFrame;
auto restLocalTransform = animationComponent.initialJointTransforms[animationStatus.boneIndex];
// offset from the end if reverse
if (animationStatus.reverse)
{
currFrame = animationStatus.lengthInFrames - currFrame;
if(currFrame > 0) {
nextFrame = currFrame - 1;
} else {
nextFrame = 0;
}
} else {
if(currFrame < animationStatus.lengthInFrames - 1) {
nextFrame = currFrame + 1;
} else {
nextFrame = currFrame;
}
}
// simple linear interpolation
math::mat4f curr = (1 - delta) * (restLocalTransform * animationStatus.frameData[currFrame]);
math::mat4f next = delta * (restLocalTransform * animationStatus.frameData[nextFrame]);
math::mat4f localTransform = curr + next;
const Entity joint = animationComponent.instance->getJointsAt(animationStatus.skinIndex)[animationStatus.boneIndex];
auto jointTransform = _transformManager.getInstance(joint);
_transformManager.setTransform(jointTransform, localTransform);
animator->updateBoneMatrices();
if (animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs)
{
animationStatus.start = now;
}
}
}
}
};
}

View File

@@ -1,5 +1,4 @@
#ifndef _STANDARD_COMPONENTS_H
#define _STANDARD_COMPONENTS_H
#pragma once
#include "utils/Entity.h"
#include "utils/EntityInstance.h"
@@ -9,7 +8,7 @@
#include "gltfio/FilamentInstance.h"
#include "Log.hpp"
namespace polyvox
namespace flutter_filament
{
typedef void(*CollisionCallback)(int32_t entityId1, int32_t entityId2) ;
@@ -19,11 +18,15 @@ class CollisionComponentManager : public utils::SingleInstanceComponentManager<f
public:
CollisionComponentManager(const filament::TransformManager& transformManager) : _transformManager(transformManager) {}
std::vector<filament::math::float3> collides(EntityId transformingEntityId, filament::Aabb sourceBox) {
std::vector<filament::math::float3> collides(utils::Entity transformingEntity, filament::Aabb sourceBox) {
auto sourceCorners = sourceBox.getCorners();
std::vector<filament::math::float3> collisionAxes;
for(auto it = begin(); it < end(); it++) {
auto entity = getEntity(it);
if(entity == transformingEntity) {
continue;
}
auto targetXformInstance = _transformManager.getInstance(entity);
auto targetXform = _transformManager.getWorldTransform(targetXformInstance);
auto targetBox = elementAt<0>(it).transform(targetXform);
@@ -40,10 +43,10 @@ class CollisionComponentManager : public utils::SingleInstanceComponentManager<f
auto max = targetBox.max;
// if the vertex has insersected with the target/source AABB
if(targetBox.contains(sourceCorners.vertices[i]) < 0) {
if(targetBox.contains(sourceCorners.vertices[i]) <= 0) {
collided = true;
// Log("targetBox %f %f %f contains source vertex %f %f %f", targetBox.extent().x, targetBox.extent().y, targetBox.extent().z, sourceCorners.vertices[i].x, sourceCorners.vertices[i].y, sourceCorners.vertices[i].z);
} else if(sourceBox.contains(targetCorners.vertices[i]) < 0) {
} else if(sourceBox.contains(targetCorners.vertices[i]) <= 0) {
// Log("sourceBox %f %f %f contains target vertex %f %f %f", sourceBox.extent().x, sourceBox.extent().y, sourceBox.extent().z, targetCorners.vertices[i].x, targetCorners.vertices[i].y, targetCorners.vertices[i].z);
collided = true;
intersecting = targetCorners.vertices[i];
@@ -83,7 +86,7 @@ class CollisionComponentManager : public utils::SingleInstanceComponentManager<f
if(collided) {
auto callback = elementAt<1>(it);
if(callback) {
callback(utils::Entity::smuggle(entity), transformingEntityId);
callback(utils::Entity::smuggle(entity), utils::Entity::smuggle(transformingEntity));
}
}
}
@@ -92,7 +95,6 @@ class CollisionComponentManager : public utils::SingleInstanceComponentManager<f
}
};
}
#endif

View File

@@ -1,91 +0,0 @@
#ifndef FILE_MATERIAL_PROVIDER
#define FILE_MATERIAL_PROVIDER
#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>
namespace polyvox {
class FileMaterialProvider : public MaterialProvider {
Material* _m;
const Material* _ms[1];
Texture* mDummyTexture = nullptr;
public:
FileMaterialProvider(Engine* engine, const void* const data, const size_t size) {
_m = Material::Builder()
.package(data, size)
.build(*engine);
_ms[0] = _m;
unsigned char texels[4] = {};
mDummyTexture = Texture::Builder()
.width(1).height(1)
.format(Texture::InternalFormat::RGBA8)
.build(*engine);
Texture::PixelBufferDescriptor pbd(texels, sizeof(texels), Texture::Format::RGBA,
Texture::Type::UBYTE);
mDummyTexture->setImage(*engine, 0, std::move(pbd));
}
filament::MaterialInstance* createMaterialInstance(MaterialKey* config, 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();
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) {
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 Material* getMaterial(MaterialKey* config, 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

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View File

@@ -82,6 +82,7 @@
#include <fstream>
#include <filesystem>
#include <mutex>
#include <iomanip>
#include "Log.hpp"
@@ -91,11 +92,6 @@
#include "TimeIt.hpp"
#include "ThreadPool.hpp"
using namespace filament;
using namespace filament::math;
using namespace gltfio;
using namespace utils;
using namespace image;
namespace filament
{
@@ -103,9 +99,18 @@ namespace filament
class LightManager;
} // namespace filament
namespace polyvox
namespace flutter_filament
{
using namespace filament;
using namespace filament::math;
using namespace gltfio;
using namespace utils;
using namespace image;
using namespace std::chrono;
using std::string;
// const float kAperture = 1.0f;
// const float kShutterSpeed = 1.0f;
// const float kSensitivity = 50.0f;
@@ -135,6 +140,8 @@ namespace polyvox
_engine = Engine::create(Engine::Backend::OPENGL, (backend::Platform *)platform, (void *)sharedContext, nullptr);
#endif
_engine->setAutomaticInstancingEnabled(true);
_renderer = _engine->createRenderer();
_frameInterval = 1000.0f / 60.0f;
@@ -143,38 +150,26 @@ namespace polyvox
_scene = _engine->createScene();
Log("Scene created");
utils::Entity camera = EntityManager::get().create();
_mainCamera = _engine->createCamera(camera);
Log("Main camera created");
_view = _engine->createView();
Log("View created");
setToneMapping(ToneMapping::ACES);
Log("Set tone mapping");
#ifdef __EMSCRIPTEN__
Log("Bloom is disabled on WebGL builds as it causes instability with certain drivers");
decltype(_view->getBloomOptions()) opts;
opts.enabled = false;
_view->setBloomOptions(opts);
// there's a glitch on certain iGPUs where nothing will render when postprocessing is enabled and bloom is disabled
// set bloom to a small value here
setBloom(0.01);
_view->setAmbientOcclusionOptions({.enabled = false});
_view->setDynamicResolutionOptions({.enabled = false});
_view->setDithering(filament::Dithering::NONE);
_view->setAntiAliasing(filament::AntiAliasing::NONE);
setAntiAliasing(false, false, false);
_view->setShadowingEnabled(false);
_view->setScreenSpaceRefractionEnabled(false);
#else
setBloom(0.6f);
Log("Set bloom");
#endif
setPostProcessing(false);
_view->setScene(_scene);
_view->setCamera(_mainCamera);
@@ -189,26 +184,11 @@ namespace polyvox
const float sens = _mainCamera->getSensitivity();
Log("Camera aperture %f shutter %f sensitivity %f", aperture, shutterSpeed, sens);
View::DynamicResolutionOptions options;
options.enabled = false;
// options.homogeneousScaling = homogeneousScaling;
// options.minScale = filament::math::float2{ minScale };
// options.maxScale = filament::math::float2{ maxScale };
// options.sharpness = sharpness;
// options.quality = View::QualityLevel::ULTRA;
_view->setDynamicResolutionOptions(options);
setAntiAliasing(false, true, false);
EntityManager &em = EntityManager::get();
_ncm = new NameComponentManager(em);
_sceneManager = new SceneManager(
_resourceLoaderWrapper,
_ncm,
_engine,
_scene,
uberArchivePath);
@@ -268,6 +248,7 @@ namespace polyvox
.build(*_engine, imageEntity);
_imageEntity = &imageEntity;
_scene->addEntity(imageEntity);
Log("Added imageEntity %d", imageEntity);
}
void FilamentViewer::setAntiAliasing(bool msaa, bool fxaa, bool taa) {
@@ -288,14 +269,15 @@ namespace polyvox
void FilamentViewer::setBloom(float strength)
{
decltype(_view->getBloomOptions()) opts;
#ifdef __EMSCRIPTEN__
opts.enabled = false;
Log("Bloom is disabled on WebGL builds as it causes instability with certain drivers. setBloom will be ignored");
#else
decltype(_view->getBloomOptions()) opts;
opts.enabled = true;
opts.strength = strength;
_view->setBloomOptions(opts);
#endif
_view->setBloomOptions(opts);
}
void FilamentViewer::setToneMapping(ToneMapping toneMapping)
@@ -331,7 +313,7 @@ namespace polyvox
{
_frameInterval = frameInterval;
Renderer::FrameRateOptions fro;
fro.interval = frameInterval;
fro.interval = 1; //frameInterval;
_renderer->setFrameRateOptions(fro);
Log("Set framerate interval to %f", frameInterval);
}
@@ -339,7 +321,10 @@ namespace polyvox
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();
LightManager::Builder(t)
auto& transformManager = _engine->getTransformManager();
transformManager.create(light);
auto parent = transformManager.getInstance(light);
auto builder = LightManager::Builder(t)
.color(Color::cct(colour))
.intensity(intensity)
.position(math::float3(posX, posY, posZ))
@@ -348,6 +333,7 @@ namespace polyvox
.build(*_engine, light);
_scene->addEntity(light);
_lights.push_back(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);
return entityId;
@@ -377,7 +363,7 @@ namespace polyvox
_lights.clear();
}
static bool endsWith(string path, string ending)
static bool endsWith(std::string path, std::string ending)
{
return path.compare(path.length() - ending.length(), ending.length(), ending) == 0;
}
@@ -435,7 +421,7 @@ namespace polyvox
void FilamentViewer::loadPngTexture(string path, ResourceBuffer rb)
{
polyvox::StreamBufferAdapter sb((char *)rb.data, (char *)rb.data + rb.size);
flutter_filament::StreamBufferAdapter sb((char *)rb.data, (char *)rb.data + rb.size);
std::istream inputStream(&sb);
@@ -480,7 +466,7 @@ namespace polyvox
void FilamentViewer::loadTextureFromPath(string path)
{
string ktxExt(".ktx");
std::string ktxExt(".ktx");
string ktx2Ext(".ktx2");
string pngExt(".png");
@@ -802,10 +788,22 @@ namespace polyvox
cam.setFocusDistance(_cameraFocusDistance);
}
///
///
///
void FilamentViewer::setMainCamera() {
_view->setCamera(_mainCamera);
}
///
///
///
EntityId FilamentViewer::getMainCamera() {
return Entity::smuggle(_mainCamera->getEntity());
}
///
/// Sets the active camera to the GLTF camera node specified by [name] (or if null, the first camera found under that node).
/// N.B. Blender will generally export a three-node hierarchy -
@@ -832,18 +830,16 @@ namespace polyvox
utils::Entity target;
if (!cameraName)
{
auto inst = _ncm->getInstance(cameras[0]);
const char *name = _ncm->getName(inst);
{
target = cameras[0];
const char *name = asset->getName(target);
Log("No camera specified, using first camera node found (%s)", name);
}
else
{
for (int j = 0; j < count; j++)
{
auto inst = _ncm->getInstance(cameras[j]);
const char *name = _ncm->getName(inst);
const char *name = asset->getName(cameras[j]);
if (strcmp(name, cameraName) == 0)
{
target = cameras[j];
@@ -1031,8 +1027,7 @@ namespace polyvox
if (secsSinceLastFpsCheck >= 1)
{
auto fps = _frameCount / secsSinceLastFpsCheck;
Log("%ffps (_frameCount %d, secs since last check %f)", fps, _frameCount, secsSinceLastFpsCheck);
// Log("1 sec average for asset animation update %f", _elapsed / _frameCount);
Log("%ffps (%d skipped)", fps, _skippedFrames);
_frameCount = 0;
_skippedFrames = 0;
_fpsCounterStartTime = now;
@@ -1138,7 +1133,7 @@ namespace polyvox
std::string filename = stringStream.str();
ofstream wf(filename, ios::out | ios::binary);
std::ofstream wf(filename, std::ios::out | std::ios::binary);
LinearImage image(toLinearWithAlpha<uint8_t>(vp.width, vp.height, vp.width * 4,
static_cast<uint8_t *>(buf)));
@@ -1228,7 +1223,7 @@ namespace polyvox
Camera &cam = _view->getCamera();
_cameraPosition = math::mat4f::translation(math::float3(x, y, z));
cam.setModelMatrix(_cameraPosition * _cameraRotation);
cam.setModelMatrix(_cameraRotation * _cameraPosition);
}
void FilamentViewer::moveCameraToAsset(EntityId entityId)
@@ -1249,11 +1244,11 @@ namespace polyvox
Log("Moved camera to %f %f %f, lookAt %f %f %f, near %f far %f", eye[0], eye[1], eye[2], lookAt[0], lookAt[1], lookAt[2], cam.getNear(), cam.getCullingFar());
}
void FilamentViewer::setCameraRotation(float rads, float x, float y, float z)
void FilamentViewer::setCameraRotation(float w, float x, float y, float z)
{
Camera &cam = _view->getCamera();
_cameraRotation = math::mat4f::rotation(rads, math::float3(x, y, z));
cam.setModelMatrix(_cameraPosition * _cameraRotation);
_cameraRotation = math::mat4f(math::quatf(w, x, y, z));
cam.setModelMatrix(_cameraRotation * _cameraPosition);
}
void FilamentViewer::setCameraModelMatrix(const float *const matrix)
@@ -1452,13 +1447,15 @@ namespace polyvox
_manipulator = nullptr;
}
void FilamentViewer::pick(uint32_t x, uint32_t y, EntityId *entityId)
void FilamentViewer::pick(uint32_t x, uint32_t y, void (*callback)(EntityId entityId, int x, int y))
{
_view->pick(x, y, [=](filament::View::PickingQueryResult const &result)
{ *entityId = Entity::smuggle(result.renderable); });
{
callback(Entity::smuggle(result.renderable), x, y);
});
}
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));
@@ -1507,7 +1504,7 @@ namespace polyvox
RenderableManager::Builder builder = RenderableManager::Builder(1);
builder
.boundingBox({{minX, minY, minZ}, {maxX, maxY, maxZ}})
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES,
.geometry(0, primitiveType,
vb, ib, 0, numIndices)
.culling(false)
.receiveShadows(false)
@@ -1518,9 +1515,10 @@ namespace polyvox
auto result = builder.build(*_engine, 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);
}
} // namespace polyvox
} // namespace flutter_filament

View File

@@ -8,7 +8,7 @@
#include <thread>
#include <functional>
using namespace polyvox;
using namespace flutter_filament;
extern "C"
{
@@ -114,9 +114,26 @@ extern "C"
((FilamentViewer *)viewer)->clearLights();
}
FLUTTER_PLUGIN_EXPORT EntityId load_glb(void *sceneManager, const char *assetPath, bool unlit)
FLUTTER_PLUGIN_EXPORT EntityId load_glb(void *sceneManager, const char *assetPath, int numInstances)
{
return ((SceneManager *)sceneManager)->loadGlb(assetPath, unlit);
return ((SceneManager *)sceneManager)->loadGlb(assetPath, numInstances);
}
FLUTTER_PLUGIN_EXPORT EntityId load_glb_from_buffer(void *sceneManager, const void* const data, size_t length)
{
return ((SceneManager *)sceneManager)->loadGlbFromBuffer((const uint8_t*)data, length);
}
FLUTTER_PLUGIN_EXPORT EntityId create_instance(void *sceneManager, EntityId entityId) {
return ((SceneManager *)sceneManager)->createInstance(entityId);
}
FLUTTER_PLUGIN_EXPORT int get_instance_count(void *sceneManager, EntityId entityId) {
return ((SceneManager*)sceneManager)->getInstanceCount(entityId);
}
FLUTTER_PLUGIN_EXPORT void get_instances(void *sceneManager, EntityId entityId, EntityId *out) {
return ((SceneManager*)sceneManager)->getInstances(entityId, out);
}
FLUTTER_PLUGIN_EXPORT EntityId load_gltf(void *sceneManager, const char *assetPath, const char *relativePath)
@@ -129,6 +146,12 @@ extern "C"
return ((FilamentViewer *)viewer)->setMainCamera();
}
FLUTTER_PLUGIN_EXPORT EntityId get_main_camera(const void *const viewer)
{
return ((FilamentViewer *)viewer)->getMainCamera();
}
FLUTTER_PLUGIN_EXPORT bool set_camera(const void *const viewer, EntityId asset, const char *nodeName)
{
return ((FilamentViewer *)viewer)->setCamera(asset, nodeName);
@@ -236,9 +259,9 @@ extern "C"
((FilamentViewer *)viewer)->setCameraPosition(x, y, z);
}
FLUTTER_PLUGIN_EXPORT void set_camera_rotation(const void *const viewer, float rads, float x, float y, float z)
FLUTTER_PLUGIN_EXPORT void set_camera_rotation(const void *const viewer, float w, float x, float y, float z)
{
((FilamentViewer *)viewer)->setCameraRotation(rads, x, y, z);
((FilamentViewer *)viewer)->setCameraRotation(w, x, y, z);
}
FLUTTER_PLUGIN_EXPORT void set_camera_model_matrix(const void *const viewer, const float *const matrix)
@@ -448,20 +471,20 @@ extern "C"
int index)
{
auto names = ((SceneManager *)sceneManager)->getAnimationNames(asset);
string name = names->at(index);
std::string name = names->at(index);
strcpy(outPtr, name.c_str());
}
FLUTTER_PLUGIN_EXPORT int get_morph_target_name_count(void *sceneManager, EntityId asset, const char *meshName)
{
unique_ptr<vector<string>> names = ((SceneManager *)sceneManager)->getMorphTargetNames(asset, meshName);
std::unique_ptr<std::vector<std::string>> names = ((SceneManager *)sceneManager)->getMorphTargetNames(asset, meshName);
return (int)names->size();
}
FLUTTER_PLUGIN_EXPORT void get_morph_target_name(void *sceneManager, EntityId asset, const char *meshName, char *const outPtr, int index)
{
unique_ptr<vector<string>> names = ((SceneManager *)sceneManager)->getMorphTargetNames(asset, meshName);
string name = names->at(index);
std::unique_ptr<std::vector<std::string>> names = ((SceneManager *)sceneManager)->getMorphTargetNames(asset, meshName);
std::string name = names->at(index);
strcpy(outPtr, name.c_str());
}
@@ -525,9 +548,9 @@ extern "C"
return ((SceneManager *)sceneManager)->reveal(asset, meshName);
}
FLUTTER_PLUGIN_EXPORT void pick(void *const viewer, int x, int y, EntityId *entityId)
FLUTTER_PLUGIN_EXPORT void pick(void *const viewer, int x, int y, void (*callback)(EntityId entityId, int x, int y))
{
((FilamentViewer *)viewer)->pick(static_cast<uint32_t>(x), static_cast<uint32_t>(y), static_cast<int32_t *>(entityId));
((FilamentViewer *)viewer)->pick(static_cast<uint32_t>(x), static_cast<uint32_t>(y), callback);
}
FLUTTER_PLUGIN_EXPORT const char *get_name_for_entity(void *const sceneManager, const EntityId entityId)
@@ -566,13 +589,28 @@ extern "C"
((SceneManager*)sceneManager)->addCollisionComponent(entityId, onCollisionCallback, affectsCollidingTransform);
}
FLUTTER_PLUGIN_EXPORT EntityId create_geometry(void *const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath) {
return ((FilamentViewer*)viewer)->createGeometry(vertices, (size_t)numVertices, indices, numIndices, materialPath);
FLUTTER_PLUGIN_EXPORT void remove_collision_component(void *const sceneManager, EntityId entityId) {
((SceneManager*)sceneManager)->removeCollisionComponent(entityId);
}
FLUTTER_PLUGIN_EXPORT void add_animation_component(void *const sceneManager, EntityId entityId) {
((SceneManager*)sceneManager)->addAnimationComponent(entityId);
}
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,
(filament::RenderableManager::PrimitiveType)primitiveType,
materialPath
);
}
FLUTTER_PLUGIN_EXPORT EntityId find_child_entity_by_name(void *const sceneManager, const EntityId parent, const char* name) {
auto entity = ((SceneManager*)sceneManager)->findChildEntityByName(parent, name);
return Entity::smuggle(entity);
return utils::Entity::smuggle(entity);
}
FLUTTER_PLUGIN_EXPORT void set_parent(void *const sceneManager, EntityId child, EntityId parent) {
@@ -583,4 +621,12 @@ extern "C"
((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);
}
}

View File

@@ -31,7 +31,7 @@ extern "C"
#include <pthread.h>
#endif
using namespace polyvox;
using namespace flutter_filament;
using namespace std::chrono_literals;
class RenderLoop {
@@ -41,36 +41,37 @@ public:
auto last = std::chrono::high_resolution_clock::now();
while (!_stop) {
if (_rendering) {
// auto frameStart = std::chrono::high_resolution_clock::now();
doRender();
// auto frameEnd = std::chrono::high_resolution_clock::now();
}
last = 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());
while(elapsed < 3 * _frameIntervalInMilliseconds / 4) {
std::function<void()> task;
std::unique_lock<std::mutex> lock(_access);
if (_tasks.empty()) {
_cond.wait_for(lock, std::chrono::duration<float, std::milli>(1));
now = std::chrono::high_resolution_clock::now();
elapsed = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - last).count());
continue;
}
std::function<void()> task;
std::unique_lock<std::mutex> lock(_access);
if(_tasks.empty()) {
_cond.wait_for(lock, std::chrono::duration<float, std::milli>(1));
}
while(!_tasks.empty()) {
task = std::move(_tasks.front());
_tasks.pop_front();
task();
now = std::chrono::high_resolution_clock::now();
elapsed = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - last).count());
}
if (_rendering) {
auto frameStart = std::chrono::high_resolution_clock::now();
doRender();
auto frameEnd = std::chrono::high_resolution_clock::now();
// Log("Took %f milliseconds for render", float(std::chrono::duration_cast<std::chrono::milliseconds>(frameEnd - frameStart).count()));
now = std::chrono::high_resolution_clock::now();
elapsed = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - last).count());
if(elapsed < _frameIntervalInMilliseconds) {
auto sleepFor = std::chrono::microseconds(int(_frameIntervalInMilliseconds - elapsed) * 1000);
std::this_thread::sleep_for(sleepFor);
}
last = now;
}
});
}
@@ -79,10 +80,12 @@ public:
_t->join();
}
void *const createViewer(void *const context, void *const platform,
void createViewer(void *const context, void *const platform,
const char *uberArchivePath,
const ResourceLoaderWrapper *const loader,
void (*renderCallback)(void *), void *const owner) {
void (*renderCallback)(void *),
void *const owner,
void (*callback)(void* const) ) {
_renderCallback = renderCallback;
_renderCallbackOwner = owner;
std::packaged_task<FilamentViewer *()> lambda([=]() mutable {
@@ -98,34 +101,30 @@ public:
#else
_viewer = new FilamentViewer(context, loader, platform, uberArchivePath);
#endif
callback(_viewer);
return _viewer;
});
auto fut = add_task(lambda);
fut.wait();
_viewer = fut.get();
return (void *const)_viewer;
}
void destroyViewer() {
std::packaged_task<void()> lambda([&]() mutable {
std::packaged_task<void()> lambda([=]() mutable {
_rendering = false;
destroy_filament_viewer(_viewer);
_viewer = nullptr;
});
auto fut = add_task(lambda);
fut.wait();
}
void setRendering(bool rendering) {
std::packaged_task<void()> lambda(
[&]() mutable { this->_rendering = rendering; });
[=]() mutable { this->_rendering = rendering; });
auto fut = add_task(lambda);
fut.wait();
}
void doRender() {
auto now = std::chrono::high_resolution_clock::now();
auto nanos = std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch()).count();
// auto now = std::chrono::high_resolution_clock::now();
// auto nanos = std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch()).count();
render(_viewer, 0, nullptr, nullptr, nullptr);
_lastRenderTime = std::chrono::high_resolution_clock::now();
if(_renderCallback) {
@@ -139,6 +138,7 @@ public:
void setFrameIntervalInMilliseconds(float frameIntervalInMilliseconds) {
_frameIntervalInMilliseconds = frameIntervalInMilliseconds;
Log("Set _frameIntervalInMilliseconds to %f", _frameIntervalInMilliseconds);
}
template <class Rt>
@@ -169,16 +169,17 @@ extern "C" {
static RenderLoop *_rl;
FLUTTER_PLUGIN_EXPORT void *const create_filament_viewer_ffi(
FLUTTER_PLUGIN_EXPORT void create_filament_viewer_ffi(
void *const context, void *const platform, const char *uberArchivePath,
const ResourceLoaderWrapper *const loader,
void (*renderCallback)(void *const renderCallbackOwner),
void *const renderCallbackOwner) {
void *const renderCallbackOwner,
void (*callback)(void* const) ) {
if (!_rl) {
_rl = new RenderLoop();
}
return _rl->createViewer(context, platform, uberArchivePath, loader,
renderCallback, renderCallbackOwner);
_rl->createViewer(context, platform, uberArchivePath, loader,
renderCallback, renderCallbackOwner, callback);
}
FLUTTER_PLUGIN_EXPORT void destroy_filament_viewer_ffi(void *const viewer) {
@@ -188,44 +189,49 @@ FLUTTER_PLUGIN_EXPORT void destroy_filament_viewer_ffi(void *const viewer) {
FLUTTER_PLUGIN_EXPORT void create_swap_chain_ffi(void *const viewer,
void *const surface,
uint32_t width,
uint32_t height) {
Log("Creating swapchain %dx%d", width, height);
uint32_t height,
void (*onComplete)()) {
Log("Creating swapchain %dx%d with viewer %d", width, height, viewer);
std::packaged_task<void()> lambda(
[&]() mutable { create_swap_chain(viewer, surface, width, height); });
[=]() mutable {
create_swap_chain(viewer, surface, width, height);
onComplete();
});
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void destroy_swap_chain_ffi(void *const viewer) {
FLUTTER_PLUGIN_EXPORT void destroy_swap_chain_ffi(void *const viewer, void (*onComplete)()) {
Log("Destroying swapchain");
std::packaged_task<void()> lambda(
[&]() mutable {
[=]() mutable {
destroy_swap_chain(viewer);
onComplete();
});
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void create_render_target_ffi(void *const viewer,
intptr_t nativeTextureId,
uint32_t width,
uint32_t height) {
std::packaged_task<void()> lambda([&]() mutable {
uint32_t height,
void (*onComplete)()) {
std::packaged_task<void()> lambda([=]() mutable {
create_render_target(viewer, nativeTextureId, width, height);
onComplete();
});
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void update_viewport_and_camera_projection_ffi(
void *const viewer, const uint32_t width, const uint32_t height,
const float scaleFactor) {
const float scaleFactor,
void (*onComplete)()) {
Log("Update viewport %dx%d", width, height);
std::packaged_task<void()> lambda([&]() mutable {
std::packaged_task<void()> lambda([=]() mutable {
update_viewport_and_camera_projection(viewer, width, height, scaleFactor);
onComplete();
});
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void set_rendering_ffi(void *const viewer,
@@ -248,294 +254,305 @@ set_frame_interval_ffi(float frameIntervalInMilliseconds) {
}
FLUTTER_PLUGIN_EXPORT void render_ffi(void *const viewer) {
std::packaged_task<void()> lambda([&]() mutable { _rl->doRender(); });
std::packaged_task<void()> lambda([=]() mutable { _rl->doRender(); });
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void
set_background_color_ffi(void *const viewer, const float r, const float g,
const float b, const float a) {
std::packaged_task<void()> lambda(
[&]() mutable { set_background_color(viewer, r, g, b, a); });
[=]() mutable { set_background_color(viewer, r, g, b, a); });
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT EntityId load_gltf_ffi(void *const assetManager,
FLUTTER_PLUGIN_EXPORT void load_gltf_ffi(void *const sceneManager,
const char *path,
const char *relativeResourcePath) {
std::packaged_task<EntityId()> lambda([&]() mutable {
return load_gltf(assetManager, path, relativeResourcePath);
const char *relativeResourcePath,
void (*callback)(EntityId)) {
std::packaged_task<EntityId()> lambda([=]() mutable {
auto entity = load_gltf(sceneManager, path, relativeResourcePath);
callback(entity);
return entity;
});
auto fut = _rl->add_task(lambda);
fut.wait();
return fut.get();
}
FLUTTER_PLUGIN_EXPORT EntityId load_glb_ffi(void *const assetManager,
const char *path, bool unlit) {
FLUTTER_PLUGIN_EXPORT void load_glb_ffi(void *const sceneManager,
const char *path, int numInstances, void (*callback)(EntityId)) {
std::packaged_task<EntityId()> lambda(
[&]() mutable { return load_glb(assetManager, path, unlit); });
[=]() mutable {
auto entity = load_glb(sceneManager, path, numInstances);
callback(entity);
return entity;
});
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void load_glb_from_buffer_ffi(void *const sceneManager,
const void *const data, size_t length, int numInstances, void (*callback)(EntityId)) {
std::packaged_task<EntityId()> lambda(
[=]() mutable {
auto entity = load_glb_from_buffer(sceneManager, data, length);
callback(entity);
return entity;
});
auto fut = _rl->add_task(lambda);
fut.wait();
return fut.get();
}
FLUTTER_PLUGIN_EXPORT void clear_background_image_ffi(void *const viewer) {
std::packaged_task<void()> lambda([&] { clear_background_image(viewer); });
std::packaged_task<void()> lambda([=] { clear_background_image(viewer); });
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void set_background_image_ffi(void *const viewer,
const char *path,
bool fillHeight) {
std::packaged_task<void()> lambda(
[&] { set_background_image(viewer, path, fillHeight); });
[=] { set_background_image(viewer, path, fillHeight); });
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void set_background_image_position_ffi(void *const viewer,
float x, float y,
bool clamp) {
std::packaged_task<void()> lambda(
[&] { set_background_image_position(viewer, x, y, clamp); });
[=] { set_background_image_position(viewer, x, y, clamp); });
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void set_tone_mapping_ffi(void *const viewer,
int toneMapping) {
std::packaged_task<void()> lambda(
[&] { set_tone_mapping(viewer, toneMapping); });
[=] { set_tone_mapping(viewer, toneMapping); });
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void set_bloom_ffi(void *const viewer, float strength) {
std::packaged_task<void()> lambda([&] { set_bloom(viewer, strength); });
std::packaged_task<void()> lambda([=] { set_bloom(viewer, strength); });
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void load_skybox_ffi(void *const viewer,
const char *skyboxPath) {
std::packaged_task<void()> lambda([&] { load_skybox(viewer, skyboxPath); });
std::packaged_task<void()> lambda([=] { load_skybox(viewer, skyboxPath); });
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void load_ibl_ffi(void *const viewer, const char *iblPath,
float intensity) {
std::packaged_task<void()> lambda(
[&] { load_ibl(viewer, iblPath, intensity); });
[=] { load_ibl(viewer, iblPath, intensity); });
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void remove_skybox_ffi(void *const viewer) {
std::packaged_task<void()> lambda([&] { remove_skybox(viewer); });
std::packaged_task<void()> lambda([=] { remove_skybox(viewer); });
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void remove_ibl_ffi(void *const viewer) {
std::packaged_task<void()> lambda([&] { remove_ibl(viewer); });
std::packaged_task<void()> lambda([=] { remove_ibl(viewer); });
auto fut = _rl->add_task(lambda);
fut.wait();
}
EntityId add_light_ffi(void *const viewer, uint8_t type, float colour,
void add_light_ffi(void *const viewer, uint8_t type, float colour,
float intensity, float posX, float posY, float posZ,
float dirX, float dirY, float dirZ, bool shadows) {
std::packaged_task<EntityId()> lambda([&] {
return add_light(viewer, type, colour, intensity, posX, posY, posZ, dirX,
float dirX, float dirY, float dirZ, bool shadows, void (*callback)(EntityId)) {
std::packaged_task<EntityId()> lambda([=] {
auto entity = add_light(viewer, type, colour, intensity, posX, posY, posZ, dirX,
dirY, dirZ, shadows);
callback(entity);
return entity;
});
auto fut = _rl->add_task(lambda);
fut.wait();
return fut.get();
}
FLUTTER_PLUGIN_EXPORT void remove_light_ffi(void *const viewer,
EntityId entityId) {
std::packaged_task<void()> lambda([&] { remove_light(viewer, entityId); });
std::packaged_task<void()> lambda([=] { remove_light(viewer, entityId); });
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void clear_lights_ffi(void *const viewer) {
std::packaged_task<void()> lambda([&] { clear_lights(viewer); });
std::packaged_task<void()> lambda([=] { clear_lights(viewer); });
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void remove_entity_ffi(void *const viewer,
EntityId asset) {
std::packaged_task<void()> lambda([&] { remove_entity(viewer, asset); });
EntityId asset, void (*callback)()) {
std::packaged_task<void()> lambda([=] {
remove_entity(viewer, asset);
callback();
});
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void clear_entities_ffi(void *const viewer) {
std::packaged_task<void()> lambda([&] { clear_entities(viewer); });
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT bool set_camera_ffi(void *const viewer, EntityId asset,
const char *nodeName) {
std::packaged_task<bool()> lambda(
[&] { return set_camera(viewer, asset, nodeName); });
FLUTTER_PLUGIN_EXPORT void clear_entities_ffi(void *const viewer, void (*callback)()) {
std::packaged_task<void()> lambda([=] {
clear_entities(viewer);
callback();
});
auto fut = _rl->add_task(lambda);
}
FLUTTER_PLUGIN_EXPORT void set_camera_ffi(void *const viewer, EntityId asset,
const char *nodeName, void (*callback)(bool)) {
std::packaged_task<bool()> lambda(
[=] {
auto success = set_camera(viewer, asset, nodeName);
callback(success);
return success;
});
auto fut = _rl->add_task(lambda);
fut.wait();
return fut.get();
}
FLUTTER_PLUGIN_EXPORT void
get_morph_target_name_ffi(void *assetManager, EntityId asset,
const char *meshName, char *const outPtr, int index) {
std::packaged_task<void()> lambda([&] {
get_morph_target_name(assetManager, asset, meshName, outPtr, index);
get_morph_target_name_ffi(void *sceneManager, EntityId asset,
const char *meshName, char *const outPtr, int index, void (*callback)()) {
std::packaged_task<void()> lambda([=] {
get_morph_target_name(sceneManager, asset, meshName, outPtr, index);
callback();
});
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT int
get_morph_target_name_count_ffi(void *assetManager, EntityId asset,
const char *meshName) {
std::packaged_task<int()> lambda([&] {
return get_morph_target_name_count(assetManager, asset, meshName);
FLUTTER_PLUGIN_EXPORT void
get_morph_target_name_count_ffi(void *sceneManager, EntityId asset,
const char *meshName, void (*callback)(int)) {
std::packaged_task<int()> lambda([=] {
auto count = get_morph_target_name_count(sceneManager, asset, meshName);
callback(count);
return count;
});
auto fut = _rl->add_task(lambda);
fut.wait();
return fut.get();
}
FLUTTER_PLUGIN_EXPORT void play_animation_ffi(void *const assetManager,
FLUTTER_PLUGIN_EXPORT void play_animation_ffi(void *const sceneManager,
EntityId asset, int index,
bool loop, bool reverse,
bool replaceActive,
float crossfade) {
std::packaged_task<void()> lambda([&] {
play_animation(assetManager, asset, index, loop, reverse, replaceActive,
std::packaged_task<void()> lambda([=] {
play_animation(sceneManager, asset, index, loop, reverse, replaceActive,
crossfade);
});
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void set_animation_frame_ffi(void *const assetManager,
FLUTTER_PLUGIN_EXPORT void set_animation_frame_ffi(void *const sceneManager,
EntityId asset,
int animationIndex,
int animationFrame) {
std::packaged_task<void()> lambda([&] {
set_animation_frame(assetManager, asset, animationIndex, animationFrame);
std::packaged_task<void()> lambda([=] {
set_animation_frame(sceneManager, asset, animationIndex, animationFrame);
});
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void stop_animation_ffi(void *const assetManager,
FLUTTER_PLUGIN_EXPORT void stop_animation_ffi(void *const sceneManager,
EntityId asset, int index) {
std::packaged_task<void()> lambda(
[&] { stop_animation(assetManager, asset, index); });
[=] { stop_animation(sceneManager, asset, index); });
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT int get_animation_count_ffi(void *const assetManager,
EntityId asset) {
FLUTTER_PLUGIN_EXPORT void get_animation_count_ffi(void *const sceneManager,
EntityId asset,
void (*callback)(int)) {
std::packaged_task<int()> lambda(
[&] { return get_animation_count(assetManager, asset); });
[=] {
auto count = get_animation_count(sceneManager, asset);
callback(count);
return count;
});
auto fut = _rl->add_task(lambda);
fut.wait();
return fut.get();
}
FLUTTER_PLUGIN_EXPORT void get_animation_name_ffi(void *const assetManager,
FLUTTER_PLUGIN_EXPORT void get_animation_name_ffi(void *const sceneManager,
EntityId asset,
char *const outPtr,
int index) {
int index,
void (*callback)()) {
std::packaged_task<void()> lambda(
[&] { get_animation_name(assetManager, asset, outPtr, index); });
[=] {
get_animation_name(sceneManager, asset, outPtr, index);
callback();
});
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void set_post_processing_ffi(void *const viewer,
bool enabled) {
std::packaged_task<void()> lambda(
[&] { set_post_processing(viewer, enabled); });
[=] { set_post_processing(viewer, enabled); });
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void pick_ffi(void *const viewer, int x, int y,
EntityId *entityId) {
std::packaged_task<void()> lambda([&] { pick(viewer, x, y, entityId); });
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT const char *
get_name_for_entity_ffi(void *const assetManager, const EntityId entityId) {
FLUTTER_PLUGIN_EXPORT void
get_name_for_entity_ffi(void *const sceneManager, const EntityId entityId, void (*callback)(const char*)) {
std::packaged_task<const char *()> lambda(
[&] { return get_name_for_entity(assetManager, entityId); });
[=] {
auto name = get_name_for_entity(sceneManager, entityId);
callback(name);
return name;
});
auto fut = _rl->add_task(lambda);
fut.wait();
return fut.get();
}
void set_morph_target_weights_ffi(void *const assetManager,
void set_morph_target_weights_ffi(void *const sceneManager,
EntityId asset,
const char *const entityName,
const float *const morphData,
int numWeights) {
std::packaged_task<void()> lambda(
[&] { return set_morph_target_weights(assetManager, asset, entityName, morphData, numWeights); });
[=] {
set_morph_target_weights(sceneManager, asset, entityName, morphData, numWeights);
});
auto fut = _rl->add_task(lambda);
fut.wait();
}
bool set_morph_animation_ffi(
void *assetManager,
void set_morph_animation_ffi(
void *sceneManager,
EntityId asset,
const char *const entityName,
const float *const morphData,
const int *const morphIndices,
int numMorphTargets,
int numFrames,
float frameLengthInMs) {
float frameLengthInMs,
void (*callback)(bool)) {
std::packaged_task<bool()> lambda(
[&] {
return set_morph_animation(assetManager, asset, entityName, morphData, morphIndices, numMorphTargets, numFrames, frameLengthInMs);
[=] {
auto success = set_morph_animation(sceneManager, asset, entityName, morphData, morphIndices, numMorphTargets, numFrames, frameLengthInMs);
callback(success);
return success;
});
auto fut = _rl->add_task(lambda);
fut.wait();
return fut.get();
}
FLUTTER_PLUGIN_EXPORT bool set_bone_transform_ffi(
void *assetManager,
FLUTTER_PLUGIN_EXPORT void set_bone_transform_ffi(
void *sceneManager,
EntityId asset,
const char *entityName,
const float *const transform,
const char *boneName) {
const char *boneName,
void (*callback)(bool)) {
std::packaged_task<bool()> lambda(
[&] { return set_bone_transform(assetManager, asset, entityName, transform, boneName); });
[=] {
auto success = set_bone_transform(sceneManager, asset, entityName, transform, boneName);
callback(success);
return success;
});
auto fut = _rl->add_task(lambda);
fut.wait();
return fut.get();
}
FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose_ffi(void* const assetManager, EntityId entityId) {
FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose_ffi(void* const sceneManager, EntityId entityId) {
std::packaged_task<void()> lambda(
[&] { return reset_to_rest_pose(assetManager, entityId); });
[=] { return reset_to_rest_pose(sceneManager, entityId); });
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi(
void *assetManager,
void *sceneManager,
EntityId asset,
const float *const frameData,
int numFrames,
@@ -547,22 +564,21 @@ FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi(
std::packaged_task<void()> lambda(
[=] {
add_bone_animation(assetManager, asset, frameData, numFrames, boneName, meshNames, numMeshTargets, frameLengthInMs, isModelSpace);
add_bone_animation(sceneManager, asset, frameData, numFrames, boneName, meshNames, numMeshTargets, frameLengthInMs, isModelSpace);
});
auto fut = _rl->add_task(lambda);
fut.wait();
}
FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi() { Log("Dummy called"); }
FLUTTER_PLUGIN_EXPORT EntityId create_geometry_ffi(void* const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath) {
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(
[=] {
return create_geometry(viewer, vertices, numVertices, indices, numIndices, materialPath);
auto entity = create_geometry(viewer, vertices, numVertices, indices, numIndices, primitiveType, materialPath);
callback(entity);
return entity;
});
auto fut = _rl->add_task(lambda);
fut.wait();
return fut.get();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,9 +3,7 @@
#include <cassert>
#include <cstring>
using namespace std;
namespace polyvox {
namespace flutter_filament {
class StreamBufferAdapter : public std::streambuf
{
@@ -14,14 +12,14 @@ class StreamBufferAdapter : public std::streambuf
~StreamBufferAdapter() {
}
streamsize size();
std::streamsize size();
private:
int_type uflow() override;
int_type underflow() override;
int_type pbackfail(int_type ch) override;
streampos seekoff(streamoff off, ios_base::seekdir way, ios_base::openmode which) override;
streampos seekpos(streampos sp, ios_base::openmode which) override;
std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which) override;
std::streampos seekpos(std::streampos sp, std::ios_base::openmode which) override;
std::streamsize showmanyc() override;
};
@@ -31,11 +29,11 @@ StreamBufferAdapter::StreamBufferAdapter(const char *begin, const char *end)
setg((char*)begin, (char*)begin, (char*)end);
}
streamsize StreamBufferAdapter::size() {
std::streamsize StreamBufferAdapter::size() {
return egptr() - eback();
}
streambuf::int_type StreamBufferAdapter::underflow()
std::streambuf::int_type StreamBufferAdapter::underflow()
{
if (gptr() == egptr()) {
return traits_type::eof();
@@ -43,7 +41,7 @@ streambuf::int_type StreamBufferAdapter::underflow()
return *(gptr());
}
streambuf::int_type StreamBufferAdapter::uflow()
std::streambuf::int_type StreamBufferAdapter::uflow()
{
if (gptr() == egptr()) {
return traits_type::eof();
@@ -53,7 +51,7 @@ streambuf::int_type StreamBufferAdapter::uflow()
return *(gptr());
}
streambuf::int_type StreamBufferAdapter::pbackfail(int_type ch)
std::streambuf::int_type StreamBufferAdapter::pbackfail(int_type ch)
{
if (gptr() == eback() || (ch != traits_type::eof() && ch != gptr()[-1]))
return traits_type::eof();
@@ -61,23 +59,23 @@ streambuf::int_type StreamBufferAdapter::pbackfail(int_type ch)
return *(gptr());
}
streamsize StreamBufferAdapter::showmanyc()
std::streamsize StreamBufferAdapter::showmanyc()
{
return egptr() - gptr();
}
streampos StreamBufferAdapter::seekoff(streamoff off, ios_base::seekdir way, ios_base::openmode which = ios_base::in) {
if(way == ios_base::beg) {
std::streampos StreamBufferAdapter::seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which = std::ios_base::in) {
if(way == std::ios_base::beg) {
setg(eback(), eback()+off, egptr());
} else if(way == ios_base::cur) {
gbump(off);
} else if(way == std::ios_base::cur) {
gbump((int)off);
} else {
setg(eback(), egptr()-off, egptr());
}
return gptr() - eback();
}
streampos StreamBufferAdapter::seekpos(streampos sp, ios_base::openmode which = ios_base::in) {
std::streampos StreamBufferAdapter::seekpos(std::streampos sp, std::ios_base::openmode which = std::ios_base::in) {
return seekoff(sp - pos_type(off_type(0)), std::ios_base::beg, which);
}
}

View File

@@ -6,7 +6,7 @@ import 'dart:html';
import 'dart:ui';
import 'dart:web_gl';
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_web_plugins/flutter_web_plugins.dart';

View File

@@ -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';
class AnimationBuilder {

View File

@@ -1,7 +1,6 @@
import 'dart:io';
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';
enum RotationMode { ZYX, XYZ }

View File

@@ -1,8 +1,8 @@
import 'dart:async';
import 'dart:math';
import 'package:flutter/services.dart';
import 'package:flutter_filament/filament_controller.dart';
import 'package:flutter_filament/filament/filament_controller.dart';
import 'package:flutter_filament/filament/utils/hardware_keyboard_listener.dart';
import 'package:vector_math/vector_math_64.dart' as v;
class EntityTransformController {
@@ -178,4 +178,26 @@ class EntityTransformController {
void mouse2Up() 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;
}
}

View 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);
}
}

View File

@@ -1,17 +1,32 @@
// ignore_for_file: constant_identifier_names
import 'dart:async';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/widgets.dart';
import 'package:flutter_filament/animations/animation_data.dart';
import 'package:flutter_filament/entities/entity_transform_controller.dart';
import 'package:flutter_filament/generated_bindings.dart';
import 'package:flutter_filament/filament/entities/gizmo.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
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.
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 }
// see filament Manipulator.h for more details
@@ -29,17 +44,6 @@ class TextureDetails {
}
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.
/// (FilamentViewer is a C++ type, hence why it is not referenced) here.
@@ -65,7 +69,7 @@ abstract class FilamentController {
/// This may be a broadcast stream, so you should ensure you have subscribed to this stream before calling [pick].
/// If [pick] is called without an active subscription to this stream, the results will be silently discarded.
///
Stream<FilamentEntity?> get pickResult;
Stream<FilamentPickResult> get pickResult;
///
/// Whether the controller is currently rendering at [framerate].
@@ -206,7 +210,22 @@ abstract class FilamentController {
///
/// Load the .glb asset at the given path and insert into the scene.
///
Future<FilamentEntity> loadGlb(String path, {bool unlit = false});
Future<FilamentEntity> loadGlb(String path, {int numInstances = 1});
///
/// Create a new instance of [entity].
///
Future<FilamentEntity> createInstance(FilamentEntity entity);
///
/// Returns the number of instances of the asset associated with [entity].
///
Future<int> getInstanceCount(FilamentEntity entity);
///
/// Returns all instances of [entity].
///
Future<List<FilamentEntity>> getInstances(FilamentEntity entity);
///
/// Load the .gltf asset at the given path and insert into the scene.
@@ -281,16 +300,11 @@ abstract class FilamentController {
Future resetBones(FilamentEntity entity);
///
/// Starts animating a bone (joint) according to the specified [animation].
/// Transforms the bone(s)/joint(s) according [animation].
/// To set the instantaneous transform, just use a single frame.
///
Future addBoneAnimation(FilamentEntity entity, BoneAnimationData animation);
///
/// Sets the local joint transform for the bone at the given index in [entity] for the mesh under [meshName].
///
Future setBoneTransform(
FilamentEntity entity, String meshName, String boneName, Matrix4 data);
///
/// Removes/destroys the specified entity from the scene.
/// [entity] will no longer be a valid handle after this method is called; ensure you immediately discard all references once this method is complete.
@@ -338,7 +352,9 @@ abstract class FilamentController {
Future setAnimationFrame(
FilamentEntity entity, int index, int animationFrame);
Future stopAnimation(FilamentEntity entity, int animationIndex);
Future stopAnimationByName(FilamentEntity entity, String name);
///
/// Sets the current scene camera to the glTF camera under [name] in [entity].
@@ -350,6 +366,11 @@ abstract class FilamentController {
///
Future setMainCamera();
///
/// Returns the entity associated with the main camera.
///
Future<FilamentEntity> getMainCamera();
///
/// Sets the current scene camera to the glTF camera under [name] in [entity].
///
@@ -450,7 +471,7 @@ abstract class FilamentController {
///
/// Rotate the camera by [rads] around the given axis. Note this is not persistent - any viewport navigation will reset the camera transform.
///
Future setCameraRotation(double rads, double x, double y, double z);
Future setCameraRotation(Quaternion quaternion);
///
/// Sets the camera model matrix.
@@ -524,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.
///
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].
@@ -563,9 +584,10 @@ abstract class FilamentController {
FilamentEntity parent, String childName);
///
/// Lists all child meshes under the given entity.
/// List all child entities under the given entity.
///
Future<List<String>> getMeshNames(FilamentEntity entity, {bool async = true});
Future<List<String>> getChildEntities(FilamentEntity entity,
{bool renderableOnly = true});
///
/// If [recording] is set to true, each frame the framebuffer/texture will be written to /tmp/output_*.png.
@@ -578,28 +600,32 @@ abstract class FilamentController {
///
Future setRecordingOutputDirectory(String outputDirectory);
// Stream get keyboardFocusRequested;
// void requestKeyboardFocus();
Future<EntityTransformController> control(FilamentEntity entity,
{double? translationSpeed, String? forwardAnimation});
///
/// 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.
///
Future addAnimationComponent(FilamentEntity entity);
///
/// Makes [collidableEntity] collidable with
/// (a) any entity whose transform is being controlled (via [control]) or
/// (b) any entity that has been marked as non-transformable but collidable (via [markNonTransformableCollidable])
/// These are differentiated because a collision will affect the transform for controlled entities
/// (e.g. if a controlled entity collides with a wall, we ignore the control update to the transform so it doesn't go through the wall)
/// Makes [entity] collidable.
/// This allows you to call [testCollisions] with any other entity ("entity B") to see if [entity] has collided with entity B. The callback will be invoked if so.
/// Alternatively, if [affectsTransform] is true and this entity collides with another entity, any queued position updates to the latter entity will be ignored.
///
Future addCollisionComponent(FilamentEntity collidableEntity,
Future addCollisionComponent(FilamentEntity entity,
{void Function(int entityId1, int entityId2)? callback,
bool affectsCollingTransform = false});
bool affectsTransform = false});
///
/// Removes the collision component from [entity], meaning this will no longer be tested when [testCollisions] or [queuePositionUpdate] is called with another entity.
///
Future removeCollisionComponent(FilamentEntity entity);
///
/// Creates a (renderable) entity with the specified geometry and adds to the scene.
///
Future createGeometry(
List<double> vertices, List<int> indices, String? materialPath);
Future createGeometry(List<double> vertices, List<int> indices,
{String? materialPath,
PrimitiveType primitiveType = PrimitiveType.TRIANGLES});
///
/// Sets the parent transform of [child] to the transform of [parent].
@@ -611,4 +637,59 @@ abstract class FilamentController {
/// This method returns void; the relevant callback passed to [addCollisionComponent] will be fired if a collision is detected.
///
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;
}

View File

@@ -3,19 +3,18 @@ import 'dart:ffi';
import 'dart:io';
import 'dart:ui' as ui;
import 'dart:developer' as dev;
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:ffi/ffi.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_filament/entities/entity_transform_controller.dart';
import 'package:flutter_filament/filament_controller.dart';
import 'package:flutter_filament/animations/animation_data.dart';
import 'package:flutter_filament/generated_bindings.dart';
import 'package:flutter_filament/hardware/hardware_keyboard_listener.dart';
import 'package:flutter_filament/rendering_surface.dart';
import 'package:flutter_filament/filament/animations/animation_data.dart';
import 'package:flutter_filament/filament/entities/gizmo.dart';
import 'package:flutter_filament/filament/filament_controller.dart';
import 'package:flutter_filament/filament/generated_bindings.dart';
import 'package:flutter_filament/filament/generated_bindings.dart' as gb;
import 'package:flutter_filament/filament/rendering_surface.dart';
import 'package:vector_math/vector_math_64.dart';
import 'scene.dart';
// ignore: constant_identifier_names
const FilamentEntity _FILAMENT_ASSET_ERROR = 0;
@@ -23,6 +22,9 @@ const FilamentEntity _FILAMENT_ASSET_ERROR = 0;
class FilamentControllerFFI extends FilamentController {
final _channel = const MethodChannel("app.polyvox.filament/event");
late SceneImpl _scene;
Scene get scene => _scene;
///
/// This will be set on constructor invocation.
/// On Windows, this will be set to the value returned by the [usesBackingWindow] method call.
@@ -52,30 +54,17 @@ class FilamentControllerFFI extends FilamentController {
final hasViewer = ValueNotifier<bool>(false);
@override
Stream<FilamentEntity> get pickResult => _pickResultController.stream;
final _pickResultController = StreamController<FilamentEntity>.broadcast();
Stream<FilamentPickResult> get pickResult => _pickResultController.stream;
final _pickResultController =
StreamController<FilamentPickResult>.broadcast();
int? _resizingWidth;
int? _resizingHeight;
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;
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.
/// 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.
@@ -111,6 +100,9 @@ class FilamentControllerFFI extends FilamentController {
_usesBackingWindow = result;
});
}
_onPickResultCallable =
NativeCallable<Void Function(Int32 entityId, Int x, Int y)>.listener(
_onPickResult);
}
bool _rendering = false;
@@ -138,17 +130,16 @@ class FilamentControllerFFI extends FilamentController {
Future setFrameRate(int framerate) async {
final interval = 1000.0 / framerate;
set_frame_interval_ffi(interval);
dev.log("Set frame interval to $interval");
}
@override
Future setDimensions(Rect rect, double pixelRatio) async {
_pixelRatio = pixelRatio;
this._rect.value = Rect.fromLTWH(
(rect.left * _pixelRatio).floor().toDouble(),
rect.top * _pixelRatio.floor().toDouble(),
(rect.width * _pixelRatio).ceil().toDouble(),
(rect.height * _pixelRatio).ceil().toDouble());
_pixelRatio = pixelRatio;
if (!_rectCompleter.isCompleted) {
_rectCompleter.complete(this._rect.value);
}
@@ -180,7 +171,82 @@ class FilamentControllerFFI extends FilamentController {
await _channel.invokeMethod(
"destroyTexture", textureDetails.value!.textureId);
}
dev.log("Texture destroyed");
print("Texture destroyed");
}
Future<void> _withVoidCallback(
Function(Pointer<NativeFunction<Void Function()>>) func) async {
final completer = Completer();
// ignore: prefer_function_declarations_over_variables
void Function() callback = () {
completer.complete();
};
final nativeCallable = NativeCallable<Void Function()>.listener(callback);
func.call(nativeCallable.nativeFunction);
await completer.future;
nativeCallable.close();
}
Future<Pointer<Void>> _withVoidPointerCallback(
Function(Pointer<NativeFunction<Void Function(Pointer<Void>)>>)
func) async {
final completer = Completer<Pointer<Void>>();
// ignore: prefer_function_declarations_over_variables
void Function(Pointer<Void>) callback = (Pointer<Void> ptr) {
completer.complete(ptr);
};
final nativeCallable =
NativeCallable<Void Function(Pointer<Void>)>.listener(callback);
func.call(nativeCallable.nativeFunction);
await completer.future;
nativeCallable.close();
return completer.future;
}
Future<bool> _withBoolCallback(
Function(Pointer<NativeFunction<Void Function(Bool)>>) func) async {
final completer = Completer<bool>();
// ignore: prefer_function_declarations_over_variables
void Function(bool) callback = (bool result) {
completer.complete(result);
};
final nativeCallable =
NativeCallable<Void Function(Bool)>.listener(callback);
func.call(nativeCallable.nativeFunction);
await completer.future;
nativeCallable.close();
return completer.future;
}
Future<int> _withIntCallback(
Function(Pointer<NativeFunction<Void Function(Int32)>>) func) async {
final completer = Completer<int>();
// ignore: prefer_function_declarations_over_variables
void Function(int) callback = (int result) {
completer.complete(result);
};
final nativeCallable =
NativeCallable<Void Function(Int32)>.listener(callback);
func.call(nativeCallable.nativeFunction);
await completer.future;
nativeCallable.close();
return completer.future;
}
Future<String> _withCharPtrCallback(
Function(Pointer<NativeFunction<Void Function(Pointer<Char>)>>)
func) async {
final completer = Completer<String>();
// ignore: prefer_function_declarations_over_variables
void Function(Pointer<Char>) callback = (Pointer<Char> result) {
completer.complete(result.cast<Utf8>().toDartString());
};
final nativeCallable =
NativeCallable<Void Function(Pointer<Char>)>.listener(callback);
func.call(nativeCallable.nativeFunction);
await completer.future;
nativeCallable.close();
return completer.future;
}
bool _creating = false;
@@ -188,6 +254,7 @@ class FilamentControllerFFI extends FilamentController {
///
/// Called by `FilamentWidget`. You do not need to call this yourself.
///
///
@override
Future createViewer() async {
if (_creating) {
@@ -218,6 +285,8 @@ class FilamentControllerFFI extends FilamentController {
throw Exception("Failed to get resource loader");
}
var renderingSurface = await _createRenderingSurface();
if (Platform.isWindows && requiresTextureWidget) {
_driver = Pointer<Void>.fromAddress(
await _channel.invokeMethod("getDriverPlatform"));
@@ -230,45 +299,67 @@ class FilamentControllerFFI extends FilamentController {
var renderCallbackOwner =
Pointer<Void>.fromAddress(renderCallbackResult[1]);
var renderingSurface = await _createRenderingSurface();
dev.log("Got rendering surface");
print("Got rendering surface");
final uberarchivePtr =
uberArchivePath?.toNativeUtf8().cast<Char>() ?? nullptr;
_viewer = create_filament_viewer_ffi(
Pointer<Void>.fromAddress(renderingSurface.sharedContext),
_driver,
uberarchivePtr,
loader,
renderCallback,
renderCallbackOwner);
_viewer = await _withVoidPointerCallback((callback) =>
create_filament_viewer_ffi(
Pointer<Void>.fromAddress(renderingSurface.sharedContext),
_driver,
uberarchivePtr,
loader,
renderCallback,
renderCallbackOwner,
callback));
allocator.free(uberarchivePtr);
dev.log("Created viewer");
print("Created viewer ${_viewer!.address}");
if (_viewer!.address == 0) {
throw Exception("Failed to create viewer. Check logs for details");
}
_sceneManager = get_scene_manager(_viewer!);
create_swap_chain_ffi(_viewer!, renderingSurface.surface,
_rect.value!.width.toInt(), _rect.value!.height.toInt());
dev.log("Created swap chain");
await _withVoidCallback((callback) {
create_swap_chain_ffi(_viewer!, renderingSurface.surface,
_rect.value!.width.toInt(), _rect.value!.height.toInt(), callback);
});
print("Created swap chain");
if (renderingSurface.textureHandle != 0) {
dev.log(
print(
"Creating render target from native texture ${renderingSurface.textureHandle}");
create_render_target_ffi(_viewer!, renderingSurface.textureHandle,
_rect.value!.width.toInt(), _rect.value!.height.toInt());
await _withVoidCallback((callback) => create_render_target_ffi(
_viewer!,
renderingSurface.textureHandle,
_rect.value!.width.toInt(),
_rect.value!.height.toInt(),
callback));
}
textureDetails.value = TextureDetails(
textureId: renderingSurface.flutterTextureId,
width: _rect.value!.width.toInt(),
height: _rect.value!.height.toInt());
dev.log("texture details ${textureDetails.value}");
update_viewport_and_camera_projection_ffi(
_viewer!, _rect.value!.width.toInt(), _rect.value!.height.toInt(), 1.0);
await _withVoidCallback((callback) {
update_viewport_and_camera_projection_ffi(
_viewer!,
_rect.value!.width.toInt(),
_rect.value!.height.toInt(),
1.0,
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;
_creating = false;
}
@@ -363,7 +454,8 @@ class FilamentControllerFFI extends FilamentController {
set_rendering_ffi(_viewer!, false);
if (!_usesBackingWindow) {
destroy_swap_chain_ffi(_viewer!);
await _withVoidCallback(
(callback) => destroy_swap_chain_ffi(_viewer!, callback));
}
if (requiresTextureWidget) {
@@ -372,7 +464,7 @@ class FilamentControllerFFI extends FilamentController {
"destroyTexture", textureDetails.value!.textureId);
}
} else if (Platform.isWindows) {
dev.log("Resizing window with rect ${_rect.value}");
print("Resizing window with rect ${_rect.value}");
await _channel.invokeMethod("resizeWindow", [
_rect.value!.width,
_rect.value!.height,
@@ -388,15 +480,23 @@ class FilamentControllerFFI extends FilamentController {
}
if (!_usesBackingWindow) {
create_swap_chain_ffi(_viewer!, renderingSurface.surface,
_rect.value!.width.toInt(), _rect.value!.height.toInt());
_withVoidCallback((callback) => create_swap_chain_ffi(
_viewer!,
renderingSurface.surface,
_rect.value!.width.toInt(),
_rect.value!.height.toInt(),
callback));
}
if (renderingSurface.textureHandle != 0) {
dev.log(
print(
"Creating render target from native texture ${renderingSurface.textureHandle}");
create_render_target_ffi(_viewer!, renderingSurface.textureHandle,
_rect.value!.width.toInt(), _rect.value!.height.toInt());
_withVoidCallback((callback) => create_render_target_ffi(
_viewer!,
renderingSurface.textureHandle,
_rect.value!.width.toInt(),
_rect.value!.height.toInt(),
callback));
}
textureDetails.value = TextureDetails(
@@ -404,8 +504,12 @@ class FilamentControllerFFI extends FilamentController {
width: _rect.value!.width.toInt(),
height: _rect.value!.height.toInt());
update_viewport_and_camera_projection_ffi(_viewer!,
_rect.value!.width.toInt(), _rect.value!.height.toInt(), 1.0);
_withVoidCallback((callback) => update_viewport_and_camera_projection_ffi(
_viewer!,
_rect.value!.width.toInt(),
_rect.value!.height.toInt(),
1.0,
callback));
await setRendering(_rendering);
} finally {
@@ -516,10 +620,21 @@ class FilamentControllerFFI extends FilamentController {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
var entity = add_light_ffi(_viewer!, type, colour, intensity, posX, posY,
posZ, dirX, dirY, dirZ, castShadows);
_onLoadController.sink.add(entity);
_lights.add(entity);
var entity = await _withIntCallback((callback) => add_light_ffi(
_viewer!,
type,
colour,
intensity,
posX,
posY,
posZ,
dirX,
dirY,
dirZ,
castShadows,
callback));
_scene.registerLight(entity);
return entity;
}
@@ -528,9 +643,8 @@ class FilamentControllerFFI extends FilamentController {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
_lights.remove(entity);
_scene.unregisterLight(entity);
remove_light_ffi(_viewer!, entity);
_onUnloadController.add(entity);
}
@override
@@ -539,14 +653,41 @@ class FilamentControllerFFI extends FilamentController {
throw Exception("No viewer available, ignoring");
}
clear_lights_ffi(_viewer!);
for (final entity in _lights) {
_onUnloadController.add(entity);
}
_lights.clear();
_scene.clearLights();
}
@override
Future<FilamentEntity> loadGlb(String path, {bool unlit = false}) async {
Future<FilamentEntity> createInstance(FilamentEntity entity) async {
var created = await _withIntCallback(
(callback) => create_instance(_sceneManager!, entity));
if (created == _FILAMENT_ASSET_ERROR) {
throw Exception("Failed to create instance");
}
return created;
}
@override
Future<int> getInstanceCount(FilamentEntity entity) async {
return get_instance_count(_sceneManager!, entity);
}
@override
Future<List<FilamentEntity>> getInstances(FilamentEntity entity) async {
var count = await getInstanceCount(entity);
var out = allocator<Int32>(count);
get_instances(_sceneManager!, entity, out);
var instances = <FilamentEntity>[];
for (int i = 0; i < count; i++) {
instances.add(out[i]);
}
allocator.free(out);
return instances;
}
@override
Future<FilamentEntity> loadGlb(String path,
{bool unlit = false, int numInstances = 1}) async {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
@@ -554,13 +695,14 @@ class FilamentControllerFFI extends FilamentController {
throw Exception("Not yet implemented");
}
final pathPtr = path.toNativeUtf8().cast<Char>();
var entity = load_glb_ffi(_sceneManager!, pathPtr, unlit);
var entity = await _withIntCallback((callback) =>
load_glb_ffi(_sceneManager!, pathPtr, numInstances, callback));
allocator.free(pathPtr);
if (entity == _FILAMENT_ASSET_ERROR) {
throw Exception("An error occurred loading the asset at $path");
}
_entities.add(entity);
_onLoadController.sink.add(entity);
_scene.registerEntity(entity);
return entity;
}
@@ -577,15 +719,15 @@ class FilamentControllerFFI extends FilamentController {
final pathPtr = path.toNativeUtf8().cast<Char>();
final relativeResourcePathPtr =
relativeResourcePath.toNativeUtf8().cast<Char>();
var entity =
load_gltf_ffi(_sceneManager!, pathPtr, relativeResourcePathPtr);
var entity = await _withIntCallback((callback) => load_gltf_ffi(
_sceneManager!, pathPtr, relativeResourcePathPtr, callback));
allocator.free(pathPtr);
allocator.free(relativeResourcePathPtr);
if (entity == _FILAMENT_ASSET_ERROR) {
throw Exception("An error occurred loading the asset at $path");
}
_entities.add(entity);
_onLoadController.sink.add(entity);
_scene.registerEntity(entity);
return entity;
}
@@ -663,8 +805,9 @@ class FilamentControllerFFI extends FilamentController {
}
var names = <String>[];
var meshNamePtr = meshName.toNativeUtf8().cast<Char>();
var count =
get_morph_target_name_count_ffi(_sceneManager!, entity, meshNamePtr);
var count = await _withIntCallback((callback) =>
get_morph_target_name_count_ffi(
_sceneManager!, entity, meshNamePtr, callback));
var outPtr = allocator<Char>(255);
for (int i = 0; i < count; i++) {
get_morph_target_name(_sceneManager!, entity, meshNamePtr, outPtr, i);
@@ -684,9 +827,10 @@ class FilamentControllerFFI extends FilamentController {
var names = <String>[];
var outPtr = allocator<Char>(255);
for (int i = 0; i < animationCount; i++) {
get_animation_name_ffi(_sceneManager!, entity, outPtr, i);
get_animation_name(_sceneManager!, entity, outPtr, i);
names.add(outPtr.cast<Utf8>().toDartString());
}
allocator.free(outPtr);
return names;
}
@@ -819,9 +963,10 @@ class FilamentControllerFFI extends FilamentController {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
_entities.remove(entity);
remove_entity_ffi(_viewer!, entity);
_onUnloadController.add(entity);
_scene.unregisterEntity(entity);
await _withVoidCallback(
(callback) => remove_entity_ffi(_viewer!, entity, callback));
}
@override
@@ -829,12 +974,9 @@ class FilamentControllerFFI extends FilamentController {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
clear_entities_ffi(_viewer!);
for (final entity in _entities) {
_onUnloadController.add(entity);
}
_entities.clear();
await _withVoidCallback(
(callback) => clear_entities_ffi(_viewer!, callback));
_scene.clearEntities();
}
@override
@@ -870,10 +1012,24 @@ class FilamentControllerFFI extends FilamentController {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
play_animation_ffi(
play_animation(
_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
Future stopAnimationByName(FilamentEntity entity, String name) async {
var animations = await getAnimationNames(entity);
await stopAnimation(entity, animations.indexOf(name));
}
@override
Future playAnimationByName(FilamentEntity entity, String name,
{bool loop = false,
@@ -897,19 +1053,15 @@ class FilamentControllerFFI extends FilamentController {
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
Future setMainCamera() async {
set_main_camera(_viewer!);
}
Future<FilamentEntity> getMainCamera() async {
return get_main_camera(_viewer!);
}
@override
Future setCamera(FilamentEntity entity, String? name) async {
if (_viewer == null) {
@@ -1027,11 +1179,12 @@ class FilamentControllerFFI extends FilamentController {
}
@override
Future setCameraRotation(double rads, double x, double y, double z) async {
Future setCameraRotation(Quaternion quaternion) async {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
set_camera_rotation(_viewer!, rads, x, y, z);
set_camera_rotation(
_viewer!, quaternion.w, quaternion.x, quaternion.y, quaternion.z);
}
@override
@@ -1088,16 +1241,6 @@ class FilamentControllerFFI extends FilamentController {
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
Future setRotationQuat(FilamentEntity entity, Quaternion rotation,
{bool relative = false}) async {
@@ -1108,6 +1251,16 @@ class FilamentControllerFFI extends FilamentController {
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
Future setScale(FilamentEntity entity, double scale) async {
if (_viewer == null) {
@@ -1116,6 +1269,26 @@ class FilamentControllerFFI extends FilamentController {
set_scale(_sceneManager!, entity, scale);
}
Future queueRotationUpdateQuat(FilamentEntity entity, Quaternion rotation,
{bool relative = false}) async {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
queue_rotation_update(_sceneManager!, entity, rotation.radians, rotation.x,
rotation.y, rotation.z, rotation.w, relative);
}
@override
Future queueRotationUpdate(
FilamentEntity entity, double rads, double x, double y, double z,
{bool relative = false}) async {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
var quat = Quaternion.axisAngle(Vector3(x, y, z), rads);
await queueRotationUpdateQuat(entity, quat, relative: relative);
}
@override
Future queuePositionUpdate(
FilamentEntity entity, double x, double y, double z,
@@ -1127,26 +1300,6 @@ class FilamentControllerFFI extends FilamentController {
queue_position_update(_sceneManager!, entity, x, y, z, relative);
}
@override
Future queueRotationUpdate(
FilamentEntity entity, double rads, double x, double y, double z,
{bool relative = false}) async {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
var quat = Quaternion.axisAngle(Vector3(x, y, z), rads);
await queueRotationUpdateQuat(entity, quat, relative: relative);
}
Future queueRotationUpdateQuat(FilamentEntity entity, Quaternion rotation,
{bool relative = false}) async {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
queue_rotation_update(_sceneManager!, entity, rotation.radians, rotation.x,
rotation.y, rotation.z, rotation.w, relative);
}
@override
Future hide(FilamentEntity entity, String? meshName) async {
if (_viewer == null) {
@@ -1158,13 +1311,13 @@ class FilamentControllerFFI extends FilamentController {
}
@override
Future reveal(FilamentEntity entity, String meshName) async {
Future reveal(FilamentEntity entity, String? meshName) async {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
final meshNamePtr = meshName.toNativeUtf8().cast<Char>();
final result = reveal_mesh(_sceneManager!, entity, meshNamePtr) != 1;
final meshNamePtr = meshName?.toNativeUtf8().cast<Char>() ?? nullptr;
final result = reveal_mesh(_sceneManager!, entity, meshNamePtr) == 1;
allocator.free(meshNamePtr);
if (!result) {
throw Exception("Failed to reveal mesh $meshName");
@@ -1180,27 +1333,31 @@ class FilamentControllerFFI extends FilamentController {
return result.cast<Utf8>().toDartString();
}
void _onPickResult(FilamentEntity entityId, int x, int y) {
_pickResultController.add((
entity: entityId,
x: (x / _pixelRatio).toDouble(),
y: (textureDetails.value!.height - y) / _pixelRatio
));
_scene.registerSelected(entityId);
}
late NativeCallable<Void Function(Int32 entityId, Int x, Int y)>
_onPickResultCallable;
@override
void pick(int x, int y) async {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
final outPtr = allocator<EntityId>(1);
outPtr.value = 0;
pick_ffi(_viewer!, x, textureDetails.value!.height - y, outPtr);
int wait = 0;
while (outPtr.value == 0) {
await Future.delayed(const Duration(milliseconds: 32));
wait++;
if (wait > 10) {
allocator.free(outPtr);
throw Exception("Failed to get picking result");
}
}
var entityId = outPtr.value;
_pickResultController.add(entityId);
allocator.free(outPtr);
_scene.unregisterSelected();
gb.pick(
_viewer!,
(x * _pixelRatio).toInt(),
textureDetails.value!.height - (y * _pixelRatio).toInt(),
_onPickResultCallable.nativeFunction);
}
@override
@@ -1331,28 +1488,6 @@ class FilamentControllerFFI extends FilamentController {
return frustum;
}
@override
Future setBoneTransform(FilamentEntity entity, String meshName,
String boneName, Matrix4 data) async {
var ptr = allocator<Float>(16);
for (int i = 0; i < 16; i++) {
ptr.elementAt(i).value = data.storage[i];
}
var meshNamePtr = meshName.toNativeUtf8(allocator: allocator).cast<Char>();
var boneNamePtr = boneName.toNativeUtf8(allocator: allocator).cast<Char>();
var result = set_bone_transform_ffi(
_sceneManager!, entity, meshNamePtr, ptr, boneNamePtr);
allocator.free(ptr);
allocator.free(meshNamePtr);
allocator.free(boneNamePtr);
if (!result) {
throw Exception("Failed to set bone transform. See logs for details");
}
}
@override
Future<FilamentEntity> getChildEntity(
FilamentEntity parent, String childName) async {
@@ -1369,12 +1504,13 @@ class FilamentControllerFFI extends FilamentController {
return childEntity;
}
Future<List<String>> getMeshNames(FilamentEntity entity,
{bool async = false}) async {
var count = get_entity_count(_sceneManager!, entity, true);
@override
Future<List<String>> getChildEntities(FilamentEntity entity,
{bool renderableOnly = false}) async {
var count = get_entity_count(_sceneManager!, entity, renderableOnly);
var names = <String>[];
for (int i = 0; i < count; i++) {
var name = get_entity_name_at(_sceneManager!, entity, i, true);
var name = get_entity_name_at(_sceneManager!, entity, i, renderableOnly);
if (name == nullptr) {
throw Exception("Failed to find mesh at index $i");
}
@@ -1395,32 +1531,12 @@ class FilamentControllerFFI extends FilamentController {
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>{};
@override
Future addCollisionComponent(FilamentEntity collidableEntity,
Future addCollisionComponent(FilamentEntity entity,
{void Function(int entityId1, int entityId2)? callback,
bool affectsCollingTransform = false}) async {
bool affectsTransform = false}) async {
if (_sceneManager == null) {
throw Exception("SceneManager must be non-null");
}
@@ -1429,17 +1545,30 @@ class FilamentControllerFFI extends FilamentController {
if (callback != null) {
var ptr = NativeCallable<
Void Function(Int32 entityId1, Int32 entityId2)>.listener(callback);
add_collision_component(_sceneManager!, collidableEntity,
ptr.nativeFunction, affectsCollingTransform);
add_collision_component(
_sceneManager!, entity, ptr.nativeFunction, affectsTransform);
_collisions[entity] = ptr;
} else {
add_collision_component(
_sceneManager!, collidableEntity, nullptr, affectsCollingTransform);
_sceneManager!, entity, nullptr, affectsTransform);
}
}
@override
Future removeCollisionComponent(FilamentEntity entity) async {
remove_collision_component(_sceneManager!, entity);
}
@override
Future addAnimationComponent(FilamentEntity entity) async {
add_animation_component(_sceneManager!, entity);
}
@override
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) {
throw Exception("Viewer must not be null");
}
@@ -1456,14 +1585,20 @@ class FilamentControllerFFI extends FilamentController {
indicesPtr.elementAt(i).value = indices[i];
}
var entity = create_geometry_ffi(_viewer!, vertexPtr, vertices.length,
indicesPtr, indices.length, materialPathPtr.cast<Char>());
var entity = await _withIntCallback((callback) => create_geometry_ffi(
_viewer!,
vertexPtr,
vertices.length,
indicesPtr,
indices.length,
primitiveType.index,
materialPathPtr.cast<Char>(),
callback));
if (entity == _FILAMENT_ASSET_ERROR) {
throw Exception("Failed to create geometry");
}
_entities.add(entity);
_onLoadController.sink.add(entity);
_scene.registerEntity(entity);
allocator.free(materialPathPtr);
allocator.free(vertexPtr);
@@ -1484,4 +1619,9 @@ class FilamentControllerFFI extends FilamentController {
Future testCollisions(FilamentEntity entity) async {
test_collisions(_sceneManager!, entity);
}
@override
Future setPriority(FilamentEntity entityId, int priority) async {
set_priority(_sceneManager!, entityId, priority);
}
}

View File

@@ -183,11 +183,21 @@ external void clear_lights(
@ffi.Native<
EntityId Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Char>,
ffi.Bool)>(symbol: 'load_glb', assetId: 'flutter_filament_plugin')
ffi.Int)>(symbol: 'load_glb', assetId: 'flutter_filament_plugin')
external int load_glb(
ffi.Pointer<ffi.Void> sceneManager,
ffi.Pointer<ffi.Char> assetPath,
bool unlit,
int numInstances,
);
@ffi.Native<
EntityId Function(
ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>, ffi.Size)>(
symbol: 'load_glb_from_buffer', assetId: 'flutter_filament_plugin')
external int load_glb_from_buffer(
ffi.Pointer<ffi.Void> sceneManager,
ffi.Pointer<ffi.Void> data,
int length,
);
@ffi.Native<
@@ -200,12 +210,42 @@ external int load_gltf(
ffi.Pointer<ffi.Char> relativePath,
);
@ffi.Native<EntityId Function(ffi.Pointer<ffi.Void>, EntityId)>(
symbol: 'create_instance', assetId: 'flutter_filament_plugin')
external int create_instance(
ffi.Pointer<ffi.Void> sceneManager,
int id,
);
@ffi.Native<ffi.Int Function(ffi.Pointer<ffi.Void>, EntityId)>(
symbol: 'get_instance_count', assetId: 'flutter_filament_plugin')
external int get_instance_count(
ffi.Pointer<ffi.Void> sceneManager,
int entityId,
);
@ffi.Native<
ffi.Void Function(
ffi.Pointer<ffi.Void>, EntityId, ffi.Pointer<EntityId>)>(
symbol: 'get_instances', assetId: 'flutter_filament_plugin')
external void get_instances(
ffi.Pointer<ffi.Void> sceneManager,
int entityId,
ffi.Pointer<EntityId> out,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>)>(
symbol: 'set_main_camera', assetId: 'flutter_filament_plugin')
external void set_main_camera(
ffi.Pointer<ffi.Void> viewer,
);
@ffi.Native<EntityId Function(ffi.Pointer<ffi.Void>)>(
symbol: 'get_main_camera', assetId: 'flutter_filament_plugin')
external int get_main_camera(
ffi.Pointer<ffi.Void> viewer,
);
@ffi.Native<
ffi.Bool Function(
ffi.Pointer<ffi.Void>, EntityId, ffi.Pointer<ffi.Char>)>(
@@ -653,7 +693,7 @@ external void get_camera_position(
symbol: 'set_camera_rotation', assetId: 'flutter_filament_plugin')
external void set_camera_rotation(
ffi.Pointer<ffi.Void> viewer,
double rads,
double w,
double x,
double y,
double z,
@@ -802,13 +842,22 @@ external void set_antialiasing(
@ffi.Native<
ffi.Void Function(
ffi.Pointer<ffi.Void>, ffi.Int, ffi.Int, ffi.Pointer<EntityId>)>(
ffi.Pointer<ffi.Void>,
ffi.Int,
ffi.Int,
ffi.Pointer<
ffi.NativeFunction<
ffi.Void Function(
EntityId entityId, ffi.Int x, ffi.Int y)>>)>(
symbol: 'pick', assetId: 'flutter_filament_plugin')
external void pick(
ffi.Pointer<ffi.Void> viewer,
int x,
int y,
ffi.Pointer<EntityId> entityId,
ffi.Pointer<
ffi.NativeFunction<
ffi.Void Function(EntityId entityId, ffi.Int x, ffi.Int y)>>
callback,
);
@ffi.Native<ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Void>, EntityId)>(
@@ -891,9 +940,29 @@ external void add_collision_component(
bool affectsCollidingTransform,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId)>(
symbol: 'remove_collision_component', assetId: 'flutter_filament_plugin')
external void remove_collision_component(
ffi.Pointer<ffi.Void> sceneManager,
int entityId,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId)>(
symbol: 'add_animation_component', assetId: 'flutter_filament_plugin')
external void add_animation_component(
ffi.Pointer<ffi.Void> sceneManager,
int entityId,
);
@ffi.Native<
EntityId Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Float>,
ffi.Int, ffi.Pointer<ffi.Uint16>, ffi.Int, ffi.Pointer<ffi.Char>)>(
EntityId Function(
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')
external int create_geometry(
ffi.Pointer<ffi.Void> viewer,
@@ -901,6 +970,7 @@ external int create_geometry(
int numVertices,
ffi.Pointer<ffi.Uint16> indices,
int numIndices,
int primitiveType,
ffi.Pointer<ffi.Char> materialPath,
);
@@ -919,8 +989,23 @@ external void test_collisions(
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.Pointer<ffi.Void> Function(
ffi.Void Function(
ffi.Pointer<ffi.Void>,
ffi.Pointer<ffi.Void>,
ffi.Pointer<ffi.Char>,
@@ -930,9 +1015,12 @@ external void test_collisions(
ffi.NativeFunction<
ffi.Void Function(
ffi.Pointer<ffi.Void> renderCallbackOwner)>>,
ffi.Pointer<ffi.Void>)>(
ffi.Pointer<ffi.Void>,
ffi.Pointer<
ffi.NativeFunction<
ffi.Void Function(ffi.Pointer<ffi.Void> viewer)>>)>(
symbol: 'create_filament_viewer_ffi', assetId: 'flutter_filament_plugin')
external ffi.Pointer<ffi.Void> create_filament_viewer_ffi(
external void create_filament_viewer_ffi(
ffi.Pointer<ffi.Void> context,
ffi.Pointer<ffi.Void> platform,
ffi.Pointer<ffi.Char> uberArchivePath,
@@ -942,34 +1030,46 @@ external ffi.Pointer<ffi.Void> create_filament_viewer_ffi(
ffi.Void Function(ffi.Pointer<ffi.Void> renderCallbackOwner)>>
renderCallback,
ffi.Pointer<ffi.Void> renderCallbackOwner,
ffi.Pointer<
ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void> viewer)>>
callback,
);
@ffi.Native<
ffi.Void Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>,
ffi.Uint32, ffi.Uint32)>(
ffi.Void Function(
ffi.Pointer<ffi.Void>,
ffi.Pointer<ffi.Void>,
ffi.Uint32,
ffi.Uint32,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(
symbol: 'create_swap_chain_ffi', assetId: 'flutter_filament_plugin')
external void create_swap_chain_ffi(
ffi.Pointer<ffi.Void> viewer,
ffi.Pointer<ffi.Void> surface,
int width,
int height,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>)>(
symbol: 'destroy_swap_chain_ffi', assetId: 'flutter_filament_plugin')
external void destroy_swap_chain_ffi(
ffi.Pointer<ffi.Void> viewer,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete,
);
@ffi.Native<
ffi.Void Function(
ffi.Pointer<ffi.Void>, ffi.IntPtr, ffi.Uint32, ffi.Uint32)>(
ffi.Void Function(ffi.Pointer<ffi.Void>,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(
symbol: 'destroy_swap_chain_ffi', assetId: 'flutter_filament_plugin')
external void destroy_swap_chain_ffi(
ffi.Pointer<ffi.Void> viewer,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete,
);
@ffi.Native<
ffi.Void Function(ffi.Pointer<ffi.Void>, ffi.IntPtr, ffi.Uint32,
ffi.Uint32, ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(
symbol: 'create_render_target_ffi', assetId: 'flutter_filament_plugin')
external void create_render_target_ffi(
ffi.Pointer<ffi.Void> viewer,
int nativeTextureId,
int width,
int height,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>)>(
@@ -1005,8 +1105,8 @@ external void set_frame_interval_ffi(
);
@ffi.Native<
ffi.Void Function(
ffi.Pointer<ffi.Void>, ffi.Uint32, ffi.Uint32, ffi.Float)>(
ffi.Void Function(ffi.Pointer<ffi.Void>, ffi.Uint32, ffi.Uint32,
ffi.Float, ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(
symbol: 'update_viewport_and_camera_projection_ffi',
assetId: 'flutter_filament_plugin')
external void update_viewport_and_camera_projection_ffi(
@@ -1014,6 +1114,7 @@ external void update_viewport_and_camera_projection_ffi(
int width,
int height,
double scaleFactor,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete,
);
@ffi.Native<
@@ -1099,19 +1200,21 @@ external void remove_ibl_ffi(
);
@ffi.Native<
EntityId Function(
ffi.Pointer<ffi.Void>,
ffi.Uint8,
ffi.Float,
ffi.Float,
ffi.Float,
ffi.Float,
ffi.Float,
ffi.Float,
ffi.Float,
ffi.Float,
ffi.Bool)>(symbol: 'add_light_ffi', assetId: 'flutter_filament_plugin')
external int add_light_ffi(
ffi.Void Function(
ffi.Pointer<ffi.Void>,
ffi.Uint8,
ffi.Float,
ffi.Float,
ffi.Float,
ffi.Float,
ffi.Float,
ffi.Float,
ffi.Float,
ffi.Float,
ffi.Bool,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>>)>(
symbol: 'add_light_ffi', assetId: 'flutter_filament_plugin')
external void add_light_ffi(
ffi.Pointer<ffi.Void> viewer,
int type,
double colour,
@@ -1123,6 +1226,7 @@ external int add_light_ffi(
double dirY,
double dirZ,
bool shadows,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>> callback,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId)>(
@@ -1139,45 +1243,87 @@ external void clear_lights_ffi(
);
@ffi.Native<
EntityId Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Char>,
ffi.Bool)>(symbol: 'load_glb_ffi', assetId: 'flutter_filament_plugin')
external int load_glb_ffi(
ffi.Pointer<ffi.Void> assetManager,
ffi.Void Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Char>, ffi.Int,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>>)>(
symbol: 'load_glb_ffi', assetId: 'flutter_filament_plugin')
external void load_glb_ffi(
ffi.Pointer<ffi.Void> sceneManager,
ffi.Pointer<ffi.Char> assetPath,
bool unlit,
int numInstances,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>> callback,
);
@ffi.Native<
EntityId Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Char>)>(
symbol: 'load_gltf_ffi', assetId: 'flutter_filament_plugin')
external int load_gltf_ffi(
ffi.Pointer<ffi.Void> assetManager,
ffi.Pointer<ffi.Char> assetPath,
ffi.Pointer<ffi.Char> relativePath,
ffi.Void Function(
ffi.Pointer<ffi.Void>,
ffi.Pointer<ffi.Void>,
ffi.Size,
ffi.Int,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>>)>(
symbol: 'load_glb_from_buffer_ffi', assetId: 'flutter_filament_plugin')
external void load_glb_from_buffer_ffi(
ffi.Pointer<ffi.Void> sceneManager,
ffi.Pointer<ffi.Void> data,
int length,
int numInstances,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>> callback,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId)>(
@ffi.Native<
ffi.Void Function(
ffi.Pointer<ffi.Void>,
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>>)>(
symbol: 'load_gltf_ffi', assetId: 'flutter_filament_plugin')
external void load_gltf_ffi(
ffi.Pointer<ffi.Void> sceneManager,
ffi.Pointer<ffi.Char> assetPath,
ffi.Pointer<ffi.Char> relativePath,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>> callback,
);
@ffi.Native<
ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>>)>(
symbol: 'create_instance_ffi', assetId: 'flutter_filament_plugin')
external void create_instance_ffi(
ffi.Pointer<ffi.Void> sceneManager,
int entityId,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>> callback,
);
@ffi.Native<
ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(
symbol: 'remove_entity_ffi', assetId: 'flutter_filament_plugin')
external void remove_entity_ffi(
ffi.Pointer<ffi.Void> viewer,
int asset,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>)>(
symbol: 'clear_entities_ffi', assetId: 'flutter_filament_plugin')
external void clear_entities_ffi(
ffi.Pointer<ffi.Void> viewer,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> callback,
);
@ffi.Native<
ffi.Bool Function(
ffi.Pointer<ffi.Void>, EntityId, ffi.Pointer<ffi.Char>)>(
ffi.Void Function(ffi.Pointer<ffi.Void>,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(
symbol: 'clear_entities_ffi', assetId: 'flutter_filament_plugin')
external void clear_entities_ffi(
ffi.Pointer<ffi.Void> viewer,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> callback,
);
@ffi.Native<
ffi.Void Function(
ffi.Pointer<ffi.Void>,
EntityId,
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Bool)>>)>(
symbol: 'set_camera_ffi', assetId: 'flutter_filament_plugin')
external bool set_camera_ffi(
external void set_camera_ffi(
ffi.Pointer<ffi.Void> viewer,
int asset,
ffi.Pointer<ffi.Char> nodeName,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Bool)>> callback,
);
@ffi.Native<
@@ -1185,7 +1331,7 @@ external bool set_camera_ffi(
ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Float>, ffi.Int)>(
symbol: 'apply_weights_ffi', assetId: 'flutter_filament_plugin')
external void apply_weights_ffi(
ffi.Pointer<ffi.Void> assetManager,
ffi.Pointer<ffi.Void> sceneManager,
int asset,
ffi.Pointer<ffi.Char> entityName,
ffi.Pointer<ffi.Float> weights,
@@ -1197,7 +1343,7 @@ external void apply_weights_ffi(
ffi.Bool, ffi.Bool, ffi.Float)>(
symbol: 'play_animation_ffi', assetId: 'flutter_filament_plugin')
external void play_animation_ffi(
ffi.Pointer<ffi.Void> assetManager,
ffi.Pointer<ffi.Void> sceneManager,
int asset,
int index,
bool loop,
@@ -1210,7 +1356,7 @@ external void play_animation_ffi(
ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId, ffi.Int, ffi.Int)>(
symbol: 'set_animation_frame_ffi', assetId: 'flutter_filament_plugin')
external void set_animation_frame_ffi(
ffi.Pointer<ffi.Void> assetManager,
ffi.Pointer<ffi.Void> sceneManager,
int asset,
int animationIndex,
int animationFrame,
@@ -1219,50 +1365,68 @@ external void set_animation_frame_ffi(
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId, ffi.Int)>(
symbol: 'stop_animation_ffi', assetId: 'flutter_filament_plugin')
external void stop_animation_ffi(
ffi.Pointer<ffi.Void> assetManager,
ffi.Pointer<ffi.Void> sceneManager,
int asset,
int index,
);
@ffi.Native<ffi.Int Function(ffi.Pointer<ffi.Void>, EntityId)>(
symbol: 'get_animation_count_ffi', assetId: 'flutter_filament_plugin')
external int get_animation_count_ffi(
ffi.Pointer<ffi.Void> assetManager,
int asset,
);
@ffi.Native<
ffi.Void Function(
ffi.Pointer<ffi.Void>, EntityId, ffi.Pointer<ffi.Char>, ffi.Int)>(
symbol: 'get_animation_name_ffi', assetId: 'flutter_filament_plugin')
external void get_animation_name_ffi(
ffi.Pointer<ffi.Void> assetManager,
int asset,
ffi.Pointer<ffi.Char> outPtr,
int index,
);
@ffi.Native<
ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId,
ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>, ffi.Int)>(
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Int)>>)>(
symbol: 'get_animation_count_ffi', assetId: 'flutter_filament_plugin')
external void get_animation_count_ffi(
ffi.Pointer<ffi.Void> sceneManager,
int asset,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Int)>> callback,
);
@ffi.Native<
ffi.Void Function(
ffi.Pointer<ffi.Void>,
EntityId,
ffi.Pointer<ffi.Char>,
ffi.Int,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(
symbol: 'get_animation_name_ffi', assetId: 'flutter_filament_plugin')
external void get_animation_name_ffi(
ffi.Pointer<ffi.Void> sceneManager,
int asset,
ffi.Pointer<ffi.Char> outPtr,
int index,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> callback,
);
@ffi.Native<
ffi.Void Function(
ffi.Pointer<ffi.Void>,
EntityId,
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Char>,
ffi.Int,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(
symbol: 'get_morph_target_name_ffi', assetId: 'flutter_filament_plugin')
external void get_morph_target_name_ffi(
ffi.Pointer<ffi.Void> assetManager,
ffi.Pointer<ffi.Void> sceneManager,
int asset,
ffi.Pointer<ffi.Char> meshName,
ffi.Pointer<ffi.Char> outPtr,
int index,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> callback,
);
@ffi.Native<
ffi.Int Function(
ffi.Pointer<ffi.Void>, EntityId, ffi.Pointer<ffi.Char>)>(
ffi.Void Function(
ffi.Pointer<ffi.Void>,
EntityId,
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Int32)>>)>(
symbol: 'get_morph_target_name_count_ffi',
assetId: 'flutter_filament_plugin')
external int get_morph_target_name_count_ffi(
ffi.Pointer<ffi.Void> assetManager,
external void get_morph_target_name_count_ffi(
ffi.Pointer<ffi.Void> sceneManager,
int asset,
ffi.Pointer<ffi.Char> meshName,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Int32)>> callback,
);
@ffi.Native<
@@ -1270,7 +1434,7 @@ external int get_morph_target_name_count_ffi(
ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Float>, ffi.Int)>(
symbol: 'set_morph_target_weights_ffi', assetId: 'flutter_filament_plugin')
external void set_morph_target_weights_ffi(
ffi.Pointer<ffi.Void> assetManager,
ffi.Pointer<ffi.Void> sceneManager,
int asset,
ffi.Pointer<ffi.Char> entityName,
ffi.Pointer<ffi.Float> morphData,
@@ -1278,7 +1442,7 @@ external void set_morph_target_weights_ffi(
);
@ffi.Native<
ffi.Bool Function(
ffi.Void Function(
ffi.Pointer<ffi.Void>,
EntityId,
ffi.Pointer<ffi.Char>,
@@ -1286,10 +1450,11 @@ external void set_morph_target_weights_ffi(
ffi.Pointer<ffi.Int>,
ffi.Int,
ffi.Int,
ffi.Float)>(
ffi.Float,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Bool)>>)>(
symbol: 'set_morph_animation_ffi', assetId: 'flutter_filament_plugin')
external bool set_morph_animation_ffi(
ffi.Pointer<ffi.Void> assetManager,
external void set_morph_animation_ffi(
ffi.Pointer<ffi.Void> sceneManager,
int asset,
ffi.Pointer<ffi.Char> entityName,
ffi.Pointer<ffi.Float> morphData,
@@ -1297,22 +1462,25 @@ external bool set_morph_animation_ffi(
int numMorphTargets,
int numFrames,
double frameLengthInMs,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Bool)>> callback,
);
@ffi.Native<
ffi.Bool Function(
ffi.Void Function(
ffi.Pointer<ffi.Void>,
EntityId,
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.Float>,
ffi.Pointer<ffi.Char>)>(
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Bool)>>)>(
symbol: 'set_bone_transform_ffi', assetId: 'flutter_filament_plugin')
external bool set_bone_transform_ffi(
ffi.Pointer<ffi.Void> assetManager,
external void set_bone_transform_ffi(
ffi.Pointer<ffi.Void> sceneManager,
int asset,
ffi.Pointer<ffi.Char> entityName,
ffi.Pointer<ffi.Float> transform,
ffi.Pointer<ffi.Char> boneName,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Bool)>> callback,
);
@ffi.Native<
@@ -1328,7 +1496,7 @@ external bool set_bone_transform_ffi(
ffi.Bool)>(
symbol: 'add_bone_animation_ffi', assetId: 'flutter_filament_plugin')
external void add_bone_animation_ffi(
ffi.Pointer<ffi.Void> assetManager,
ffi.Pointer<ffi.Void> sceneManager,
int asset,
ffi.Pointer<ffi.Float> frameData,
int numFrames,
@@ -1346,21 +1514,10 @@ external void set_post_processing_ffi(
bool enabled,
);
@ffi.Native<
ffi.Void Function(
ffi.Pointer<ffi.Void>, ffi.Int, ffi.Int, ffi.Pointer<EntityId>)>(
symbol: 'pick_ffi', assetId: 'flutter_filament_plugin')
external void pick_ffi(
ffi.Pointer<ffi.Void> viewer,
int x,
int y,
ffi.Pointer<EntityId> entityId,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId)>(
symbol: 'reset_to_rest_pose_ffi', assetId: 'flutter_filament_plugin')
external void reset_to_rest_pose_ffi(
ffi.Pointer<ffi.Void> assetManager,
ffi.Pointer<ffi.Void> sceneManager,
int entityId,
);
@@ -1369,16 +1526,25 @@ external void reset_to_rest_pose_ffi(
external void ios_dummy_ffi();
@ffi.Native<
EntityId Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Float>,
ffi.Int, ffi.Pointer<ffi.Uint16>, ffi.Int, ffi.Pointer<ffi.Char>)>(
ffi.Void Function(
ffi.Pointer<ffi.Void>,
ffi.Pointer<ffi.Float>,
ffi.Int,
ffi.Pointer<ffi.Uint16>,
ffi.Int,
ffi.Int,
ffi.Pointer<ffi.Char>,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>>)>(
symbol: 'create_geometry_ffi', assetId: 'flutter_filament_plugin')
external int create_geometry_ffi(
external void create_geometry_ffi(
ffi.Pointer<ffi.Void> viewer,
ffi.Pointer<ffi.Float> vertices,
int numVertices,
ffi.Pointer<ffi.Uint16> indices,
int numIndices,
int primitiveType,
ffi.Pointer<ffi.Char> materialPath,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>> callback,
);
final class __mbstate_t extends ffi.Union {

118
lib/filament/scene.dart Normal file
View 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);
}
}

View File

@@ -0,0 +1,15 @@
import 'package:vector_math/vector_math_64.dart' as v;
class CameraOrientation {
v.Vector3 position = v.Vector3(0, 0, 0);
var rotationX = 0.0;
var rotationY = 0.0;
var rotationZ = 0.0;
v.Quaternion compose() {
return v.Quaternion.axisAngle(v.Vector3(0, 0, 1), rotationZ) *
v.Quaternion.axisAngle(v.Vector3(0, 1, 0), rotationY) *
v.Quaternion.axisAngle(v.Vector3(1, 0, 0), rotationX);
}
}

View File

@@ -1,6 +1,5 @@
import 'package:flutter/services.dart';
import 'package:flutter_filament/entities/entity_transform_controller.dart';
import 'package:flutter_filament/filament_controller.dart';
import 'package:flutter_filament/filament/entities/entity_transform_controller.dart';
class HardwareKeyboardListener {
final EntityTransformController _controller;

View File

@@ -0,0 +1,40 @@
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_filament/filament/entities/entity_transform_controller.dart';
class HardwareKeyboardPoll {
final EntityTransformController _controller;
late Timer _timer;
HardwareKeyboardPoll(this._controller) {
_timer = Timer.periodic(const Duration(milliseconds: 16), (_) {
if (RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.keyW)) {
_controller.forwardPressed();
} else {
_controller.forwardReleased();
}
if (RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.keyS)) {
_controller.backPressed();
} else {
_controller.backReleased();
}
if (RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.keyA)) {
_controller.strafeLeftPressed();
} else {
_controller.strafeLeftReleased();
}
if (RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.keyD)) {
_controller.strafeRightPressed();
} else {
_controller.strafeRightReleased();
}
});
}
void dispose() {
_timer.cancel();
}
}

View File

@@ -0,0 +1,29 @@
import 'package:vector_math/vector_math_64.dart' as v;
class LightOptions {
String? iblPath;
double iblIntensity;
int directionalType;
double directionalColor;
double directionalIntensity;
bool directionalCastShadows;
late v.Vector3 directionalPosition;
late v.Vector3 directionalDirection;
LightOptions(
{required this.iblPath,
required this.iblIntensity,
required this.directionalType,
required this.directionalColor,
required this.directionalIntensity,
required this.directionalCastShadows,
v.Vector3? directionalDirection,
v.Vector3? directionalPosition}) {
this.directionalDirection = directionalDirection == null
? v.Vector3(0, -1, 0)
: directionalDirection;
this.directionalPosition = directionalPosition == null
? v.Vector3(0, 100, 0)
: directionalPosition;
}
}

View 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);
}

View File

@@ -0,0 +1,233 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_filament/filament/filament_controller.dart';
import 'package:flutter_filament/filament/utils/camera_orientation.dart';
import 'dart:math';
import 'package:vector_math/vector_math_64.dart' as v64;
class CameraOptionsWidget extends StatefulWidget {
final FilamentController controller;
final CameraOrientation cameraOrientation;
final List<({FilamentEntity entity, String name})> cameras;
CameraOptionsWidget(
{super.key,
required this.controller,
required this.cameras,
required this.cameraOrientation}) {}
@override
State<StatefulWidget> createState() => _CameraOptionsWidgetState();
}
class _CameraOptionsWidgetState extends State<CameraOptionsWidget> {
final _apertureController = TextEditingController();
final _speedController = TextEditingController();
final _sensitivityController = TextEditingController();
@override
void initState() {
_apertureController.text = "0";
_speedController.text = "0";
_sensitivityController.text = "0";
_apertureController.addListener(() {
_set();
setState(() {});
});
_speedController.addListener(() {
_set();
setState(() {});
});
_sensitivityController.addListener(() {
_set();
setState(() {});
});
super.initState();
}
@override
void didUpdateWidget(CameraOptionsWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.cameras.length != widget.cameras.length) {
setState(() {});
}
}
Future _set() async {
await widget.controller.setCameraExposure(
double.parse(_apertureController.text),
double.parse(_speedController.text),
double.parse(_sensitivityController.text));
await widget.controller.setCameraPosition(
widget.cameraOrientation.position.x,
widget.cameraOrientation.position.y,
widget.cameraOrientation.position.z);
var rotation = widget.cameraOrientation.compose();
await widget.controller.setCameraRotation(rotation);
print(
"Camera : ${widget.cameraOrientation.position} ${widget.cameraOrientation.rotationX} ${widget.cameraOrientation.rotationY} ${widget.cameraOrientation.rotationZ}");
setState(() {});
}
double _bloom = 0.0;
double _focalLength = 26.0;
@override
Widget build(BuildContext context) {
return Theme(
data: ThemeData(platform: TargetPlatform.android),
child: Container(
decoration: BoxDecoration(color: Colors.white.withOpacity(0.5)),
child: SliderTheme(
data: SliderThemeData(
showValueIndicator: ShowValueIndicator.always,
valueIndicatorTextStyle: TextStyle(color: Colors.black)),
child: Column(mainAxisSize: MainAxisSize.min, children: [
Row(children: [
Text("Aperture"),
Expanded(
child: TextField(
controller: _apertureController,
)),
Text("Speed"),
Expanded(child: TextField(controller: _speedController)),
Text("Sensitivity"),
Expanded(
child: TextField(controller: _sensitivityController)),
]),
Row(children: [
Text("Bloom: ${_bloom.toStringAsFixed(2)}"),
Slider(
value: _bloom,
min: 0.0,
max: 1.0,
onChanged: (v) async {
setState(() {
_bloom = v;
});
await widget.controller.setBloom(_bloom);
})
]),
Row(children: [
Text("Focal length"),
Slider(
label: _focalLength.toString(),
value: _focalLength,
min: 1.0,
max: 100.0,
onChanged: (v) async {
setState(() {
_focalLength = v;
});
await widget.controller
.setCameraFocalLength(_focalLength);
})
]),
Row(children: [
Text("X"),
Slider(
label: widget.cameraOrientation.position.x.toString(),
value: widget.cameraOrientation.position.x,
min: -100.0,
max: 100.0,
onChanged: (v) async {
setState(() {
widget.cameraOrientation.position.x = v;
});
_set();
})
]),
Row(children: [
Text("Y"),
Slider(
label: widget.cameraOrientation.position.y.toString(),
value: widget.cameraOrientation.position.y,
min: -100.0,
max: 100.0,
onChanged: (v) async {
setState(() {
widget.cameraOrientation.position.y = v;
});
_set();
})
]),
Row(children: [
Text("Z"),
Slider(
label: widget.cameraOrientation.position.z.toString(),
value: widget.cameraOrientation.position.z,
min: -100.0,
max: 100.0,
onChanged: (v) async {
setState(() {
widget.cameraOrientation.position.z = v;
});
_set();
})
]),
Row(children: [
Text("ROTX"),
Slider(
label: widget.cameraOrientation.rotationX.toString(),
value: widget.cameraOrientation.rotationX,
min: -pi,
max: pi,
onChanged: (value) async {
setState(() {
widget.cameraOrientation.rotationX = value;
});
_set();
})
]),
Row(children: [
Text("ROTY"),
Slider(
label: widget.cameraOrientation.rotationY.toString(),
value: widget.cameraOrientation.rotationY,
min: -pi,
max: pi,
onChanged: (v) async {
setState(() {
widget.cameraOrientation.rotationY = v;
});
_set();
}),
]),
Row(children: [
Text("ROTZ"),
Slider(
label: widget.cameraOrientation.rotationZ.toString(),
value: widget.cameraOrientation.rotationZ,
min: -pi,
max: pi,
onChanged: (v) async {
setState(() {
widget.cameraOrientation.rotationZ = v;
});
_set();
})
]),
Wrap(
children: [
GestureDetector(
child: Text("Main "),
onTap: () {
widget.controller.setMainCamera();
},
),
...widget.cameras
.map((camera) => GestureDetector(
onTap: () {
widget.controller
.setCamera(camera.entity, camera.name);
},
child: Text(camera.name)))
.toList()
],
)
]))));
}
}

View 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()))));
}
}

View File

@@ -1,8 +1,10 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_filament/entities/entity_transform_controller.dart';
import 'dart:async';
import 'package:flutter_filament/filament/entities/entity_transform_controller.dart';
///
/// A widget that translates mouse gestures to zoom/pan/rotate actions.
///

View File

@@ -0,0 +1,93 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_filament/filament/widgets/filament_gesture_detector_desktop.dart';
import 'package:flutter_filament/filament/widgets/filament_gesture_detector_mobile.dart';
import '../filament_controller.dart';
enum GestureType { rotateCamera, panCamera, panBackground }
///
/// A widget that translates finger/mouse gestures to zoom/pan/rotate actions.
///
class FilamentGestureDetector extends StatelessWidget {
///
/// The content to display below the gesture detector/listener widget.
/// This will usually be a FilamentWidget (so you can navigate by directly interacting with the viewport), but this is not necessary.
/// It is equally possible to render the viewport/gesture controls elsewhere in the widget hierarchy. The only requirement is that they share the same [FilamentController].
///
final Widget? child;
///
/// The [controller] attached to the [FilamentWidget] you wish to control.
///
final FilamentController controller;
///
/// If true, an overlay will be shown with buttons to toggle whether pointer movements are interpreted as:
/// 1) rotate or a pan (mobile only),
/// 2) moving the camera or the background image (TODO).
///
final bool showControlOverlay;
///
/// If false, gestures will not manipulate the active camera.
///
final bool enableCamera;
///
/// If false, pointer down events will not trigger hit-testing (picking).
///
final bool enablePicking;
final void Function(ScaleStartDetails)? onScaleStart;
final void Function(ScaleUpdateDetails)? onScaleUpdate;
final void Function(ScaleEndDetails)? onScaleEnd;
const FilamentGestureDetector(
{Key? key,
required this.controller,
this.child,
this.showControlOverlay = false,
this.enableCamera = true,
this.enablePicking = true,
this.onScaleStart,
this.onScaleUpdate,
this.onScaleEnd})
: super(key: key);
@override
Widget build(BuildContext context) {
return ValueListenableBuilder(
valueListenable: controller.hasViewer,
builder: (_, bool hasViewer, __) {
if (!hasViewer) {
return Container(child: child);
}
if (kIsWeb) {
throw Exception("TODO");
} else if (Platform.isLinux ||
Platform.isWindows ||
Platform.isMacOS) {
return FilamentGestureDetectorDesktop(
controller: controller,
child: child,
showControlOverlay: showControlOverlay,
enableCamera: enableCamera,
enablePicking: enablePicking,
);
} else {
return FilamentGestureDetectorMobile(
controller: controller,
child: child,
showControlOverlay: showControlOverlay,
enableCamera: enableCamera,
enablePicking: enablePicking,
onScaleStart: onScaleStart,
onScaleUpdate: onScaleUpdate,
onScaleEnd: onScaleEnd);
}
});
}
}

View File

@@ -2,6 +2,7 @@ import 'dart:async';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_filament/filament/entities/gizmo.dart';
import '../filament_controller.dart';
///
@@ -28,16 +29,22 @@ class FilamentGestureDetectorDesktop extends StatefulWidget {
final bool showControlOverlay;
///
/// If false, all gestures will be ignored.
/// If false, gestures will not manipulate the active camera.
///
final bool listenerEnabled;
final bool enableCamera;
///
/// If false, pointer down events will not trigger hit-testing (picking).
///
final bool enablePicking;
const FilamentGestureDetectorDesktop(
{Key? key,
required this.controller,
this.child,
this.showControlOverlay = false,
this.listenerEnabled = true})
this.enableCamera = true,
this.enablePicking = true})
: super(key: key);
@override
@@ -54,10 +61,18 @@ class _FilamentGestureDetectorDesktopState
bool _pointerMoving = false;
Gizmo get _gizmo => widget.controller.scene.gizmo;
@override
void initState() {
super.initState();
}
@override
void didUpdateWidget(FilamentGestureDetectorDesktop oldWidget) {
if (widget.showControlOverlay != oldWidget.showControlOverlay ||
widget.listenerEnabled != oldWidget.listenerEnabled) {
widget.enableCamera != oldWidget.enableCamera ||
widget.enablePicking != oldWidget.enablePicking) {
setState(() {});
}
@@ -84,15 +99,26 @@ class _FilamentGestureDetectorDesktopState
});
}
Timer? _pickTimer;
@override
Widget build(BuildContext context) {
if (!widget.listenerEnabled) {
return widget.child ?? Container();
}
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 {
if (pointerSignal is PointerScrollEvent) {
_zoom(pointerSignal);
if (widget.enableCamera) {
_zoom(pointerSignal);
}
} else {
throw Exception("TODO");
}
@@ -100,28 +126,38 @@ class _FilamentGestureDetectorDesktopState
onPointerPanZoomStart: (pzs) {
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) {
widget.controller
.pick(d.localPosition.dx.toInt(), d.localPosition.dy.toInt());
}
_pointerMoving = false;
},
// holding/moving the left mouse button is interpreted as a pan, middle mouse button as a rotate
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 (!_pointerMoving) {
if (d.buttons == kTertiaryButton) {
if (d.buttons == kTertiaryButton && widget.enableCamera) {
widget.controller
.rotateStart(d.localPosition.dx, d.localPosition.dy);
} else {
} else if (widget.enableCamera) {
widget.controller
.panStart(d.localPosition.dx, d.localPosition.dy);
}
}
// set the _pointerMoving flag so we don't call rotateStart/panStart on future move events
_pointerMoving = true;
if (d.buttons == kTertiaryButton) {
if (d.buttons == kTertiaryButton && widget.enableCamera) {
widget.controller
.rotateUpdate(d.localPosition.dx, d.localPosition.dy);
} else {
} else if (widget.enableCamera) {
widget.controller.panUpdate(d.localPosition.dx, d.localPosition.dy);
}
},
@@ -130,14 +166,16 @@ class _FilamentGestureDetectorDesktopState
// 2) if _pointerMoving is false, this is interpreted as a pick
// same applies to middle mouse button, but this is ignored as a pick
onPointerUp: (PointerUpEvent d) async {
if (d.buttons == kTertiaryButton) {
if (_gizmo.isActive) {
_gizmo.reset();
return;
}
if (d.buttons == kTertiaryButton && widget.enableCamera) {
widget.controller.rotateEnd();
} else {
if (_pointerMoving) {
if (_pointerMoving && widget.enableCamera) {
widget.controller.panEnd();
} else {
widget.controller
.pick(d.localPosition.dx.toInt(), d.localPosition.dy.toInt());
}
}
_pointerMoving = false;

View File

@@ -28,18 +28,31 @@ class FilamentGestureDetectorMobile extends StatefulWidget {
final bool showControlOverlay;
///
/// If false, all gestures will be ignored.
/// If false, gestures will not manipulate the active camera.
///
final bool listenerEnabled;
final bool enableCamera;
///
/// If false, pointer down events will not trigger hit-testing (picking).
///
final bool enablePicking;
final double zoomDelta;
final void Function(ScaleStartDetails)? onScaleStart;
final void Function(ScaleUpdateDetails)? onScaleUpdate;
final void Function(ScaleEndDetails)? onScaleEnd;
const FilamentGestureDetectorMobile(
{Key? key,
required this.controller,
this.child,
this.showControlOverlay = false,
this.listenerEnabled = true,
this.enableCamera = true,
this.enablePicking = true,
this.onScaleStart,
this.onScaleUpdate,
this.onScaleEnd,
this.zoomDelta = 1})
: super(key: key);
@@ -105,7 +118,8 @@ class _FilamentGestureDetectorMobileState
@override
void didUpdateWidget(FilamentGestureDetectorMobile oldWidget) {
if (widget.showControlOverlay != oldWidget.showControlOverlay ||
widget.listenerEnabled != oldWidget.listenerEnabled) {
widget.enableCamera != oldWidget.enableCamera ||
widget.enablePicking != oldWidget.enablePicking) {
setState(() {});
}
@@ -117,29 +131,35 @@ class _FilamentGestureDetectorMobileState
double _lastScale = 0;
// pinch zoom on mobile
// couldn't find any equivalent for pointerCount in Listener so we use two widgets:
// - outer is a GestureDetector only for pinch zoom
// - inner is a Listener for all other gestures (including scroll zoom on desktop)
// couldn't find any equivalent for pointerCount in Listener (?) so we use a GestureDetector
@override
Widget build(BuildContext context) {
if (!widget.listenerEnabled) {
return widget.child ?? Container();
}
return Stack(children: [
Positioned.fill(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
behavior: HitTestBehavior.translucent,
onTapDown: (d) {
if (!widget.enablePicking) {
return;
}
widget.controller.pick(
d.globalPosition.dx.toInt(), d.globalPosition.dy.toInt());
},
onDoubleTap: () {
setState(() {
_rotateOnPointerMove = !_rotateOnPointerMove;
});
},
onScaleStart: (d) async {
if (d.pointerCount == 2) {
if (widget.onScaleStart != null) {
widget.onScaleStart!.call(d);
return;
}
if (d.pointerCount == 2 && widget.enableCamera) {
_scaling = true;
await widget.controller.zoomBegin();
} else if (!_scaling) {
} else if (!_scaling && widget.enableCamera) {
if (_rotateOnPointerMove) {
widget.controller.rotateStart(
d.localFocalPoint.dx, d.localFocalPoint.dy);
@@ -150,7 +170,11 @@ class _FilamentGestureDetectorMobileState
}
},
onScaleUpdate: (ScaleUpdateDetails d) async {
if (d.pointerCount == 2) {
if (widget.onScaleUpdate != null) {
widget.onScaleUpdate!.call(d);
return;
}
if (d.pointerCount == 2 && widget.enableCamera) {
if (d.horizontalScale != _lastScale) {
widget.controller.zoomUpdate(
d.localFocalPoint.dx,
@@ -158,7 +182,7 @@ class _FilamentGestureDetectorMobileState
d.horizontalScale > _lastScale ? 0.1 : -0.1);
_lastScale = d.horizontalScale;
}
} else if (!_scaling) {
} else if (!_scaling && widget.enableCamera) {
if (_rotateOnPointerMove) {
widget.controller
.rotateUpdate(d.focalPoint.dx, d.focalPoint.dy);
@@ -169,9 +193,14 @@ class _FilamentGestureDetectorMobileState
}
},
onScaleEnd: (d) async {
if (d.pointerCount == 2) {
if (widget.onScaleEnd != null) {
widget.onScaleEnd!.call(d);
return;
}
if (d.pointerCount == 2 && widget.enableCamera) {
widget.controller.zoomEnd();
} else if (!_scaling) {
} else if (!_scaling && widget.enableCamera) {
if (_rotateOnPointerMove) {
widget.controller.rotateEnd();
} else {

View File

@@ -6,7 +6,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_filament/filament_controller.dart';
import 'package:flutter_filament/filament/filament_controller.dart';
import 'dart:async';
@@ -182,6 +182,7 @@ class _SizedFilamentWidgetState extends State<_SizedFilamentWidget> {
} finally {
_resizing = false;
completer.complete();
_resizeTimer?.cancel();
}
});
return completer.future;
@@ -190,7 +191,9 @@ class _SizedFilamentWidgetState extends State<_SizedFilamentWidget> {
@override
void didUpdateWidget(_SizedFilamentWidget oldWidget) {
super.didUpdateWidget(oldWidget);
_resize();
if (widget.height != oldWidget.height || widget.width != oldWidget.width) {
_resize();
}
}
@override
@@ -302,5 +305,5 @@ class TransparencyPainter extends CustomPainter {
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}

View File

@@ -2,7 +2,7 @@ import 'dart:math';
import 'package:flutter/material.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;
class IblRotationSliderWidget extends StatefulWidget {

View File

@@ -0,0 +1,193 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_filament/filament/filament_controller.dart';
import 'package:flutter_filament/filament/utils/light_options.dart';
import 'package:vector_math/vector_math_64.dart' as v;
class LightSliderWidget extends StatefulWidget {
final FilamentController controller;
final LightOptions options;
final bool showControls;
LightSliderWidget(
{super.key,
required this.controller,
this.showControls = false,
required this.options});
@override
State<StatefulWidget> createState() => _LightSliderWidgetState();
}
class _LightSliderWidgetState extends State<LightSliderWidget> {
FilamentEntity? _light;
@override
void initState() {
_set();
super.initState();
}
Future _set() async {
await widget.controller.clearLights();
if (widget.options.iblPath != null) {
_light = await widget.controller.loadIbl(widget.options.iblPath!,
intensity: widget.options.iblIntensity);
}
_light = await widget.controller.addLight(
widget.options.directionalType,
widget.options.directionalColor,
widget.options.directionalIntensity,
widget.options.directionalPosition.x,
widget.options.directionalPosition.y,
widget.options.directionalPosition.z,
widget.options.directionalDirection.x,
widget.options.directionalDirection.y,
widget.options.directionalDirection.z,
widget.options.directionalCastShadows);
setState(() {});
}
@override
Widget build(BuildContext context) {
if (_light == null || !widget.showControls) {
return Container();
}
return Theme(
data: ThemeData(platform: TargetPlatform.android),
child: Container(
decoration: BoxDecoration(color: Colors.white.withOpacity(0.5)),
child: SliderTheme(
data: SliderThemeData(
showValueIndicator: ShowValueIndicator.always,
valueIndicatorTextStyle: TextStyle(color: Colors.black)),
child: Column(mainAxisSize: MainAxisSize.min, children: [
Text("Directional"),
Row(children: [
Expanded(
child: Slider(
label:
"POSX ${widget.options.directionalPosition.x}",
value: widget.options.directionalPosition.x,
min: -10.0,
max: 10.0,
onChanged: (value) {
widget.options.directionalPosition.x = value;
_set();
})),
Expanded(
child: Slider(
label:
"POSY ${widget.options.directionalPosition.y}",
value: widget.options.directionalPosition.y,
min: -100.0,
max: 100.0,
onChanged: (value) {
widget.options.directionalPosition.y = value;
_set();
})),
Expanded(
child: Slider(
label:
"POSZ ${widget.options.directionalPosition.z}",
value: widget.options.directionalPosition.z,
min: -100.0,
max: 100.0,
onChanged: (value) {
widget.options.directionalPosition.z = value;
_set();
}))
]),
Row(children: [
Expanded(
child: Slider(
label: "DIRX",
value: widget.options.directionalDirection.x,
min: -1.0,
max: 1.0,
onChanged: (value) {
widget.options.directionalDirection.x = value;
_set();
})),
Expanded(
child: Slider(
label: "DIRY",
value: widget.options.directionalDirection.y,
min: -1.0,
max: 1.0,
onChanged: (value) {
widget.options.directionalDirection.y = value;
_set();
})),
Expanded(
child: Slider(
label: "DIRZ",
value: widget.options.directionalDirection.z,
min: -1.0,
max: 1.0,
onChanged: (value) {
widget.options.directionalDirection.z = value;
_set();
}))
]),
Slider(
label: "Color",
value: widget.options.directionalColor,
min: 0,
max: 16000,
onChanged: (value) {
widget.options.directionalColor = value;
_set();
}),
Slider(
label: "Intensity ${widget.options.directionalIntensity}",
value: widget.options.directionalIntensity,
min: 0,
max: 1000000,
onChanged: (value) {
widget.options.directionalIntensity = value;
_set();
}),
DropdownButton(
onChanged: (v) {
this.widget.options.directionalType = v;
_set();
},
value: this.widget.options.directionalType,
items: List<DropdownMenuItem>.generate(
5,
(idx) => DropdownMenuItem(
value: idx,
child: Text("$idx"),
))),
Row(children: [
Text(
"Shadows: ${this.widget.options.directionalCastShadows}"),
Checkbox(
value: widget.options.directionalCastShadows,
onChanged: (v) {
this.widget.options.directionalCastShadows = v!;
_set();
})
]),
Text("Indirect"),
Row(children: [
Expanded(
child: Slider(
label: "Intensity ${widget.options.iblIntensity}",
value: widget.options.iblIntensity,
min: 0.0,
max: 200000,
onChanged: (value) {
widget.options.iblIntensity = value;
_set();
})),
])
]))));
}
}

12
lib/flutter_filament.dart Normal file
View 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';

View File

@@ -1,104 +0,0 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_filament/filament_controller.dart';
import 'package:vector_math/vector_math_64.dart' as v;
class CameraOptionsWidget extends StatefulWidget {
final FilamentController controller;
final List<({FilamentEntity entity, String name})> cameras;
CameraOptionsWidget(
{super.key, required this.controller, required this.cameras}) {}
@override
State<StatefulWidget> createState() => _CameraOptionsWidgetState();
}
class _CameraOptionsWidgetState extends State<CameraOptionsWidget> {
final _apertureController = TextEditingController();
final _speedController = TextEditingController();
final _sensitivityController = TextEditingController();
@override
void initState() {
_apertureController.text = "0";
_speedController.text = "0";
_sensitivityController.text = "0";
_apertureController.addListener(() {
_set();
setState(() {});
});
_speedController.addListener(() {
_set();
setState(() {});
});
_sensitivityController.addListener(() {
_set();
setState(() {});
});
super.initState();
}
@override
void didUpdateWidget(CameraOptionsWidget oldWidget) {
if (oldWidget.cameras.length != widget.cameras.length) {
setState(() {});
}
}
Future _set() async {
await widget.controller.setCameraExposure(
double.parse(_apertureController.text),
double.parse(_speedController.text),
double.parse(_sensitivityController.text));
setState(() {});
}
@override
Widget build(BuildContext context) {
return Theme(
data: ThemeData(platform: TargetPlatform.android),
child: Container(
decoration: BoxDecoration(color: Colors.white.withOpacity(0.5)),
child: SliderTheme(
data: SliderThemeData(
showValueIndicator: ShowValueIndicator.always,
valueIndicatorTextStyle: TextStyle(color: Colors.black)),
child: Column(mainAxisSize: MainAxisSize.min, children: [
Row(children: [
Text("Aperture"),
Expanded(
child: TextField(
controller: _apertureController,
)),
Text("Speed"),
Expanded(child: TextField(controller: _speedController)),
Text("Sensitivity"),
Expanded(
child: TextField(controller: _sensitivityController)),
]),
Wrap(
children: [
GestureDetector(
child: Text("Main "),
onTap: () {
widget.controller.setMainCamera();
},
),
...widget.cameras
.map((camera) => GestureDetector(
onTap: () {
widget.controller
.setCamera(camera.entity, camera.name);
},
child: Text(camera.name)))
.toList()
],
)
]))));
}
}

View File

@@ -1,67 +0,0 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_filament/widgets/filament_gesture_detector_desktop.dart';
import 'package:flutter_filament/widgets/filament_gesture_detector_mobile.dart';
import '../filament_controller.dart';
enum GestureType { rotateCamera, panCamera, panBackground }
///
/// A widget that translates finger/mouse gestures to zoom/pan/rotate actions.
///
class FilamentGestureDetector extends StatelessWidget {
///
/// The content to display below the gesture detector/listener widget.
/// This will usually be a FilamentWidget (so you can navigate by directly interacting with the viewport), but this is not necessary.
/// It is equally possible to render the viewport/gesture controls elsewhere in the widget hierarchy. The only requirement is that they share the same [FilamentController].
///
final Widget? child;
///
/// The [controller] attached to the [FilamentWidget] you wish to control.
///
final FilamentController controller;
///
/// If true, an overlay will be shown with buttons to toggle whether pointer movements are interpreted as:
/// 1) rotate or a pan (mobile only),
/// 2) moving the camera or the background image (TODO).
///
final bool showControlOverlay;
///
/// If false, all gestures will be ignored.
///
final bool enabled;
const FilamentGestureDetector(
{Key? key,
required this.controller,
this.child,
this.showControlOverlay = false,
this.enabled = true})
: super(key: key);
@override
Widget build(BuildContext context) {
if (kIsWeb) {
throw Exception("TODO");
} else if (Platform.isLinux || Platform.isWindows || Platform.isMacOS) {
return FilamentGestureDetectorDesktop(
controller: controller,
child: child,
showControlOverlay: showControlOverlay,
listenerEnabled: enabled,
);
} else {
return FilamentGestureDetectorMobile(
controller: controller,
child: child,
showControlOverlay: showControlOverlay,
listenerEnabled: enabled,
);
}
}
}

View File

@@ -1,195 +0,0 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_filament/filament_controller.dart';
import 'package:vector_math/vector_math_64.dart' as v;
class LightSliderWidget extends StatefulWidget {
final FilamentController controller;
late final v.Vector3 initialPosition;
late final v.Vector3 initialDirection;
final int initialType;
final double initialColor;
final double initialIntensity;
final bool initialCastShadows;
final bool showControls;
LightSliderWidget(
{super.key,
required this.controller,
this.initialType = 0,
this.initialColor = 6500,
this.initialIntensity = 100000,
this.initialCastShadows = true,
this.showControls = false,
v.Vector3? initialDirection,
v.Vector3? initialPosition}) {
this.initialDirection = initialDirection ?? v.Vector3(0, 0.5, -1);
this.initialPosition = initialPosition ?? v.Vector3(0, 0.5, 1);
}
@override
State<StatefulWidget> createState() => _IblRotationSliderWidgetState();
}
class _IblRotationSliderWidgetState extends State<LightSliderWidget> {
v.Vector3 lightPos = v.Vector3(1, 0.1, 1);
v.Vector3 lightDir = v.Vector3(-1, 0.1, 0);
bool castShadows = true;
int type = 0;
double color = 6500;
double intensity = 100000;
FilamentEntity? _light;
@override
void initState() {
type = widget.initialType;
castShadows = widget.initialCastShadows;
color = widget.initialColor;
lightPos = widget.initialPosition;
lightDir = widget.initialDirection;
intensity = widget.initialIntensity;
_set();
super.initState();
}
Future _set() async {
if (_light != null) await widget.controller.removeLight(_light!);
_light = await widget.controller.addLight(
type,
color,
intensity,
lightPos.x,
lightPos.y,
lightPos.z,
lightDir.x,
lightDir.y,
lightDir.z,
castShadows);
setState(() {});
}
@override
Widget build(BuildContext context) {
if (_light == null || !widget.showControls) {
return Container();
}
return Theme(
data: ThemeData(platform: TargetPlatform.android),
child: Container(
decoration: BoxDecoration(color: Colors.white.withOpacity(0.5)),
child: SliderTheme(
data: SliderThemeData(
showValueIndicator: ShowValueIndicator.always,
valueIndicatorTextStyle: TextStyle(color: Colors.black)),
child: Column(mainAxisSize: MainAxisSize.min, children: [
Row(children: [
Expanded(
child: Slider(
label: "POSX",
value: lightPos.x,
min: -10.0,
max: 10.0,
onChanged: (value) {
lightPos.x = value;
_set();
})),
Expanded(
child: Slider(
label: "POSY",
value: lightPos.y,
min: -10.0,
max: 10.0,
onChanged: (value) {
lightPos.y = value;
_set();
})),
Expanded(
child: Slider(
label: "POSZ",
value: lightPos.z,
min: -10.0,
max: 10.0,
onChanged: (value) {
lightPos.z = value;
_set();
}))
]),
Row(children: [
Expanded(
child: Slider(
label: "DIRX",
value: lightDir.x,
min: -10.0,
max: 10.0,
onChanged: (value) {
lightDir.x = value;
_set();
})),
Expanded(
child: Slider(
label: "DIRY",
value: lightDir.y,
min: -10.0,
max: 10.0,
onChanged: (value) {
lightDir.y = value;
_set();
})),
Expanded(
child: Slider(
label: "DIRZ",
value: lightDir.z,
min: -10.0,
max: 10.0,
onChanged: (value) {
lightDir.z = value;
_set();
}))
]),
Slider(
label: "Color",
value: color,
min: 0,
max: 16000,
onChanged: (value) {
color = value;
_set();
}),
Slider(
label: "Intensity",
value: intensity,
min: 0,
max: 1000000,
onChanged: (value) {
intensity = value;
_set();
}),
DropdownButton(
onChanged: (v) {
this.type = v;
_set();
},
value: type,
items: List<DropdownMenuItem>.generate(
5,
(idx) => DropdownMenuItem(
value: idx,
child: Text("$idx"),
))),
Row(children: [
Text("Shadows: $castShadows"),
Checkbox(
value: castShadows,
onChanged: (v) {
this.castShadows = v!;
_set();
})
])
]))));
}
}

View File

@@ -43,7 +43,7 @@ struct _FlutterFilamentPlugin {
double width = 0;
double height = 0;
bool rendering = false;
polyvox::FilamentViewer* viewer;
flutter_filament::FilamentViewer* viewer;
};
G_DEFINE_TYPE(FlutterFilamentPlugin, flutter_filament_plugin, g_object_get_type())
@@ -71,7 +71,7 @@ static FlMethodResponse* _create_filament_viewer(FlutterFilamentPlugin* self, Fl
self->height = height;
auto context = glXGetCurrentContext();
self->viewer = (polyvox::FilamentViewer*)create_filament_viewer(
self->viewer = (flutter_filament::FilamentViewer*)create_filament_viewer(
(void*)context,
callback
);
@@ -698,7 +698,7 @@ static FlMethodResponse* _get_morph_target_names(FlutterFilamentPlugin* self, Fl
static FlMethodResponse* _set_tone_mapping(FlutterFilamentPlugin* self, FlMethodCall* method_call) {
FlValue* args = fl_method_call_get_args(method_call);
polyvox::ToneMapping toneMapping = static_cast<polyvox::ToneMapping>(fl_value_get_int(args));
flutter_filament::ToneMapping toneMapping = static_cast<flutter_filament::ToneMapping>(fl_value_get_int(args));
set_tone_mapping(self->viewer, toneMapping);
return FL_METHOD_RESPONSE(fl_method_success_response_new(fl_value_new_bool(true)));
}

View File

@@ -12,14 +12,13 @@
#include "ResourceBuffer.hpp"
using namespace std;
static map<uint32_t, void*> _file_assets;
static std::map<uint32_t, void*> _file_assets;
static uint32_t _i = 0;
ResourceBuffer loadResource(const char* name) {
std::cout << "LOADING RESOURCE" << std::endl;
std::cout << "LOADING RESOURCE" << std::endl;
char cwd[PATH_MAX];
if (getcwd(cwd, sizeof(cwd)) != NULL) {

View File

@@ -1,31 +0,0 @@
#include <flutter_linux/flutter_linux.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "include/flutter_filament/flutter_filament_plugin.h"
#include "flutter_filament_plugin_private.h"
// This demonstrates a simple unit test of the C portion of this plugin's
// implementation.
//
// Once you have built the plugin's example app, you can run these tests
// from the command line. For instance, for a plugin called my_plugin
// built for x64 debug, run:
// $ build/linux/x64/debug/plugins/my_plugin/my_plugin_test
namespace flutter_filament {
namespace test {
TEST(FlutterFilamentPlugin, GetPlatformVersion) {
g_autoptr(FlMethodResponse) response = get_platform_version();
ASSERT_NE(response, nullptr);
ASSERT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
FlValue* result = fl_method_success_response_get_result(
FL_METHOD_SUCCESS_RESPONSE(response));
ASSERT_EQ(fl_value_get_type(result), FL_VALUE_TYPE_STRING);
// The full string varies, so just validate that it has the right format.
EXPECT_THAT(fl_value_get_string(result), testing::StartsWith("Linux "));
}
} // namespace test
} // namespace flutter_filament

View File

@@ -14,7 +14,7 @@ A new Flutter plugin project.
s.author = { 'Your Company' => 'email@example.com' }
s.source = { :path => '.' }
s.source_files = 'Classes/*', 'src/*', "src/camutils/*", 'include/filament/*', 'include/*', 'include/material/*.c'
s.source_files = 'Classes/*', 'src/*', "src/camutils/*", 'include/filament/*', 'include/*', 'include/components/*', 'include/material/*.c'
s.public_header_files = 'include/SwiftFlutterFilamentPlugin-Bridging-Header.h', 'include/FlutterFilamentApi.h', 'include/FlutterFilamentFFIApi.h', 'include/ResourceBuffer.hpp' #, 'include/filament/*'
s.dependency 'FlutterMacOS'
@@ -33,7 +33,7 @@ A new Flutter plugin project.
s.pod_target_xcconfig = {
'DEFINES_MODULE' => 'YES',
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17",
'OTHER_CXXFLAGS' => '"--std=c++17" "-fmodules" "-fcxx-modules" "-fvisibility=default" "$(inherited)"',
'OTHER_CXXFLAGS' => '"--std=c++17" "-fmodules" "-fcxx-modules" "-fvisibility=default" "-Wno-documentation-deprecated-sync" "$(inherited)"',
'OTHER_CFLAGS' => '"-fvisibility=default" "$(inherited)"',
'USER_HEADER_SEARCH_PATHS' => '"${PODS_TARGET_SRCROOT}/include" "${PODS_TARGET_SRCROOT}/include/filament" "$(inherited)"',
'ALWAYS_SEARCH_USER_PATHS' => 'YES',

40
materials/gizmo.mat Normal file
View 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);
}
}

View File

@@ -120,6 +120,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.2"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "41b90ceaec6d79819f31e975e61d479516efe701dea35f891b2f986c1b031422"
url: "https://pub.dev"
source: hosted
version: "9.0.17"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "54808cfcfa87dbc0d74c61ac063d624adf1bd5c0407301f32b06c783c60dc4ca"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "7e71be3c161472f6c9158ac8875dd8de575060d60b5d159ebca3600ea32c9116"
url: "https://pub.dev"
source: hosted
version: "1.0.6"
lints:
dependency: transitive
description:
@@ -140,26 +164,26 @@ packages:
dependency: transitive
description:
name: matcher
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.dev"
source: hosted
version: "0.12.16"
version: "0.12.16+1"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
url: "https://pub.dev"
source: hosted
version: "0.5.0"
version: "0.8.0"
meta:
dependency: transitive
description:
name: meta
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
url: "https://pub.dev"
source: hosted
version: "1.10.0"
version: "1.11.0"
package_config:
dependency: transitive
description:
@@ -172,10 +196,10 @@ packages:
dependency: transitive
description:
name: path
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.dev"
source: hosted
version: "1.8.3"
version: "1.9.0"
plugin_platform_interface:
dependency: "direct main"
description:
@@ -261,14 +285,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
web:
vm_service:
dependency: transitive
description:
name: web
sha256: "14f1f70c51119012600c5f1f60ca68efda5a9b6077748163c6af2893ec5df8fc"
name: vm_service
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
url: "https://pub.dev"
source: hosted
version: "0.2.1-beta"
version: "13.0.0"
yaml:
dependency: transitive
description:
@@ -286,5 +310,5 @@ packages:
source: hosted
version: "2.1.1"
sdks:
dart: ">=3.2.0-157.0.dev <4.0.0"
dart: ">=3.2.0-0 <4.0.0"
flutter: ">=3.16.0-0.2.pre"

View File

@@ -24,7 +24,7 @@ dev_dependencies:
ffigen: ^9.0.1
ffigen:
output: 'lib/generated_bindings.dart'
output: 'lib/filament/generated_bindings.dart'
headers:
entry-points:
- 'ios/include/FlutterFilamentFFIApi.h'

View File

@@ -63,7 +63,20 @@ int main(int argc, char** argv) {
auto sceneManager = SceneManager(&loader, engine, scene, nullptr);
auto shapes = sceneManager.loadGlb("../example/assets/shapes/shapes.glb", 1);
auto shapes = sceneManager.loadGlb("../example/assets/shapes/shapes.glb", 2);
sceneManager.remove(shapes);
shapes = sceneManager.loadGlb("../example/assets/shapes/shapes.glb", 2);
auto instanceCount = sceneManager.getInstanceCount(shapes);
assert(instanceCount == 2);
EntityId instances[instanceCount];
sceneManager.getInstances(shapes, instances);
sceneManager.transformToUnitCube(shapes);
auto morphTargetNames = sceneManager.getMorphTargetNames(shapes, "Cylinder");
assert(morphTargetNames->size() == 4);
@@ -73,5 +86,9 @@ int main(int argc, char** argv) {
morphTargetNames = sceneManager.getMorphTargetNames(shapes, "Cone");
assert(morphTargetNames->size() == 8);
math::mat4f boneTransform;
sceneManager.setBoneTransform(shapes, "Cylinder", 0, "Bone", boneTransform);
sceneManager.destroyAll();
}

View File

@@ -29,7 +29,9 @@ if(USE_ANGLE)
add_compile_definitions(USE_ANGLE)
list(APPEND PLUGIN_SOURCES "flutter_angle_texture.cpp" "egl_context.cpp" )
else()
add_compile_definitions(WGL_USE_BACKING_WINDOW)
if(WGL_USE_BACKING_WINDOW)
add_compile_definitions(WGL_USE_BACKING_WINDOW)
endif()
list(APPEND PLUGIN_SOURCES "wgl_context.cpp" "opengl_texture_buffer.cpp" "backing_window.cpp")
# if(WGL_USE_BACKING_WINDOW)
# list(APPEND PLUGIN_SOURCES )

View File

@@ -5,7 +5,6 @@
#include <chrono>
#include <thread>
#include <Commctrl.h>
#include <Windows.h>
#include <dwmapi.h>
#include <ShObjIdl.h>

View File

@@ -1,8 +1,17 @@
#include "egl_context.h"
#define FILAMENT_USE_EXTERNAL_GLES3
#include <gl_headers.h>
#pragma comment(lib, "dwmapi.lib")
#pragma comment(lib, "comctl32.lib")
namespace flutter_filament {
EGLContext::EGLContext(flutter::PluginRegistrarWindows* pluginRegistrar, flutter::TextureRegistrar* textureRegistrar); {
FlutterEGLContext::FlutterEGLContext(
flutter::PluginRegistrarWindows* pluginRegistrar,
flutter::TextureRegistrar* textureRegistrar) : FlutterRenderContext(pluginRegistrar, textureRegistrar) {
_platform = new filament::backend::PlatformEGL();
@@ -34,7 +43,7 @@ EGLContext::EGLContext(flutter::PluginRegistrarWindows* pluginRegistrar, flutter
dxgi->Release();
if (!adapter_) {
std::cout << "Failed to locate default D3D adapter" << std::endl;
return false;
return;
}
DXGI_ADAPTER_DESC adapter_desc_;
@@ -49,7 +58,7 @@ EGLContext::EGLContext(flutter::PluginRegistrarWindows* pluginRegistrar, flutter
if (FAILED(hr)) {
std::cout << "Failed to create D3D device" << std::endl;
return false;
return;
}
Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device = nullptr;
@@ -74,7 +83,7 @@ EGLContext::EGLContext(flutter::PluginRegistrarWindows* pluginRegistrar, flutter
EGLBoolean bindAPI = eglBindAPI(EGL_OPENGL_ES_API);
if (UTILS_UNLIKELY(!bindAPI)) {
std::cout << "eglBindAPI EGL_OPENGL_ES_API failed" << std::endl;
return false;
return;
}
_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
@@ -106,10 +115,10 @@ EGLContext::EGLContext(flutter::PluginRegistrarWindows* pluginRegistrar, flutter
if (UTILS_UNLIKELY(!initialized)) {
std::cout << "eglInitialize failed" << std::endl;
return false;
return;
}
importGLESExtensionsEntryPoints();
glext::importGLESExtensionsEntryPoints();
EGLint configsCount;
@@ -126,53 +135,66 @@ EGLContext::EGLContext(flutter::PluginRegistrarWindows* pluginRegistrar, flutter
// find an opaque config
if (!eglChooseConfig(_eglDisplay, configAttribs, &_eglConfig, 1,
&configsCount)) {
return false;
std::cout << "Failed to find EGL config" << std::endl;
return;
}
_context = (void *)eglCreateContext(_eglDisplay, _eglConfig, EGL_NO_CONTEXT,
contextAttribs);
auto ctx = eglCreateContext(_eglDisplay, _eglConfig, EGL_NO_CONTEXT,contextAttribs);
_context = (void*)ctx;
if (UTILS_UNLIKELY(_context == EGL_NO_CONTEXT)) {
return false;
return;
}
}
EGLContext::CreateRenderingSurface(
void FlutterEGLContext::CreateRenderingSurface(
uint32_t width, uint32_t height,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result,
uint32_t left, uint32_t top
) {
importGLESExtensionsEntryPoints();
glext::importGLESExtensionsEntryPoints();
if(left != 0 || top != 0) {
result->Error("ERROR",
"Rendering with EGL uses a Texture render target/Flutter widget and does not need a window offset.");
return false;
return;
}
if (_active.get()) {
result->Error("ERROR",
"Texture already exists. You must call destroyTexture before "
"attempting to create a new one.");
return false;
return;
}
_active = std::make_unique<FlutterAngleTexture>(
std::unique_ptr<FlutterTextureBuffer> active = std::make_unique<FlutterAngleTexture>(
_pluginRegistrar, _textureRegistrar, std::move(result), width, height,
_D3D11Device, _D3D11DeviceContext, _eglConfig, _eglDisplay, _context,
[=](size_t width, size_t height) {
std::cout << "RESIZE" << std::endl;
std::vector<int64_t> list;
list.push_back((int64_t)width);
list.push_back((int64_t)height);
auto val = std::make_unique<flutter::EncodableValue>(list);
this->_channel->InvokeMethod("resize", std::move(val), nullptr);
// auto val = std::make_unique<flutter::EncodableValue>(list);
// this->_channel->InvokeMethod("resize", std::move(val), nullptr);
});
_active = std::move(active);
return _active->flutterTextureId != -1;
}
EGLContext::GetSharedContext() {
return (void*)_eglContext;
void FlutterEGLContext::RenderCallback() {
if(_active.get()) {
((FlutterAngleTexture*)_active.get())->RenderCallback();
}
}
void* FlutterEGLContext::GetSharedContext() {
return (void*)_context;
}
void* FlutterEGLContext::GetPlatform() {
return (void*)_platform;
}
}

View File

@@ -1,24 +1,35 @@
#ifndef _EGL_CONTEXT_H
#define _EGL_CONTEXT_H
#include <Windows.h>
#include <flutter/method_channel.h>
#include <flutter/plugin_registrar_windows.h>
#include <flutter/standard_method_codec.h>
#include <flutter/texture_registrar.h>
#include "flutter_angle_texture.h"
#include "backend/platforms/PlatformEGL.h"
#include "flutter_render_context.h"
namespace flutter_filament {
class EGLContext : public FlutterRenderingContext {
class FlutterEGLContext : public FlutterRenderContext {
public:
EGLContext(flutter::PluginRegistrarWindows* pluginRegistrar, flutter::TextureRegistrar* textureRegistrar);
FlutterEGLContext(flutter::PluginRegistrarWindows* pluginRegistrar, flutter::TextureRegistrar* textureRegistrar);
void* GetSharedContext();
void RenderCallback();
void* GetPlatform();
void CreateRenderingSurface(uint32_t width, uint32_t height, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result, uint32_t left, uint32_t top );
private:
EGLContext _context = NULL;
void* _context = nullptr;
EGLConfig _eglConfig = NULL;
EGLDisplay _eglDisplay = NULL;
std::unique_ptr<FlutterAngleTexture> _active = nullptr;
std::unique_ptr<FlutterAngleTexture> _inactive = nullptr;
ID3D11Device* _D3D11Device = nullptr;
ID3D11DeviceContext* _D3D11DeviceContext = nullptr;
filament::backend::Platform* _platform = nullptr;
}
};
}

View File

@@ -236,6 +236,7 @@ FlutterAngleTexture::FlutterAngleTexture(
resultList.push_back(flutter::EncodableValue(flutterTextureId));
resultList.push_back(flutter::EncodableValue((int64_t) nullptr));
resultList.push_back(flutter::EncodableValue(glTextureId));
resultList.push_back(flutter::EncodableValue((int64_t) eglContext));
result->Success(resultList);
}

View File

@@ -28,7 +28,7 @@ typedef uint32_t GLuint;
namespace flutter_filament {
class FlutterAngleTexture : FlutterTextureBuffer {
class FlutterAngleTexture : public FlutterTextureBuffer {
public:
FlutterAngleTexture(
flutter::PluginRegistrarWindows* pluginRegistrar,

View File

@@ -3,11 +3,7 @@
#include "flutter_filament_plugin.h"
// This must be included before many other Windows headers.
#include <windows.h>
// For getPlatformVersion; remove unless needed for your plugin implementation.
#include <VersionHelpers.h>
#include <Windows.h>
#include <flutter/method_channel.h>
#include <flutter/plugin_registrar_windows.h>
@@ -18,7 +14,7 @@
#include <cstring>
#include <filesystem>
#include <fstream>
#include <future>
#include <iostream>
#include <locale>
#include <map>
@@ -31,11 +27,6 @@
#include "FlutterFilamentApi.h"
#include <Commctrl.h>
#include <Windows.h>
#include <dwmapi.h>
#include <wrl.h>
#include "flutter_render_context.h"
#if USE_ANGLE
@@ -44,10 +35,11 @@
#include "wgl_context.h"
#endif
using namespace std::chrono_literals;
namespace flutter_filament {
using namespace std::chrono_literals;
void FlutterFilamentPlugin::RegisterWithRegistrar(
flutter::PluginRegistrarWindows *registrar) {
auto channel =
@@ -153,10 +145,16 @@ void render_callback(void *owner) {
void FlutterFilamentPlugin::RenderCallback() {
if (_context) {
auto flutterTextureId = _context->GetFlutterTextureId();
if(flutterTextureId == -1) {
std::cout << "Bad texture" << std::endl;
return;
}
#ifdef USE_ANGLE
_active->RenderCallback();
_context->RenderCallback();
#endif
#if !WGL_USE_BACKING_WINDOW
_textureRegistrar->MarkTextureFrameAvailable(flutterTextureId);
#endif
}
}
@@ -182,7 +180,7 @@ void FlutterFilamentPlugin::CreateTexture(
// this will be used to create a backing texture and passed to Filament
if (!_context) {
#ifdef USE_ANGLE
_context = std::make_unique<EGLContext>(_pluginRegistrar);
_context = std::make_unique<FlutterEGLContext>(_pluginRegistrar, _textureRegistrar);
#else
_context = std::make_unique<WGLContext>(_pluginRegistrar, _textureRegistrar);
#endif
@@ -194,7 +192,6 @@ void FlutterFilamentPlugin::DestroyTexture(
const flutter::MethodCall<flutter::EncodableValue> &methodCall,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
const auto *flutterTextureId = std::get_if<int64_t>(methodCall.arguments());
if (!flutterTextureId) {
@@ -260,7 +257,7 @@ void FlutterFilamentPlugin::HandleMethodCall(
result->Success(resultList);
} else if (methodCall.method_name() == "getDriverPlatform") {
#ifdef USE_ANGLE
result->Success(flutter::EncodableValue((int64_t)_platform));
result->Success(flutter::EncodableValue((int64_t)_context->GetPlatform()));
#else
result->Success(flutter::EncodableValue((int64_t) nullptr));
#endif

View File

@@ -16,7 +16,7 @@
#include "FlutterFilamentApi.h"
#if ANGLE
#if USE_ANGLE
#include "egl_context.h"
#else
#include "wgl_context.h"
@@ -60,7 +60,7 @@ public:
private:
#ifdef USE_ANGLE
std::unique_ptr<EGLContext> _context = nullptr;
std::unique_ptr<FlutterEGLContext> _context = nullptr;
#else
std::unique_ptr<WGLContext> _context = nullptr;
#endif

View File

@@ -12,6 +12,7 @@ namespace flutter_filament {
class FlutterRenderContext {
public:
void CreateRenderingSurface(uint32_t width, uint32_t height, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result, uint32_t left, uint32_t top );
void DestroyTexture(std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
@@ -33,11 +34,15 @@ namespace flutter_filament {
});
}
int64_t GetFlutterTextureId() {
if(!_active) {
return -1;
}
return _active->flutterTextureId;
}
void* sharedContext = nullptr;
protected:
FlutterRenderContext( flutter::PluginRegistrarWindows* pluginRegistrar, flutter::TextureRegistrar* textureRegistrar) : _pluginRegistrar(pluginRegistrar), _textureRegistrar(textureRegistrar) {};
flutter::PluginRegistrarWindows* _pluginRegistrar;
flutter::TextureRegistrar* _textureRegistrar;
std::unique_ptr<FlutterTextureBuffer> _active = nullptr;

View File

@@ -11,7 +11,7 @@ namespace flutter_filament {
class FlutterTextureBuffer {
public:
int64_t flutterTextureId = 0;
int64_t flutterTextureId = -1;
};
}

View File

@@ -117,11 +117,15 @@ OpenGLTextureBuffer::OpenGLTextureBuffer(
flutterTextureId = textureRegistrar->RegisterTexture(texture.get());
std::cout << "Registered Flutter texture ID " << flutterTextureId
<< std::endl;
std::vector<flutter::EncodableValue> resultList;
resultList.push_back(flutter::EncodableValue(flutterTextureId));
resultList.push_back(flutter::EncodableValue((int64_t) nullptr));
resultList.push_back(flutter::EncodableValue(glTextureId));
result->Success(resultList);
if (flutterTextureId != -1) {
std::vector<flutter::EncodableValue> resultList;
resultList.push_back(flutter::EncodableValue(flutterTextureId));
resultList.push_back(flutter::EncodableValue((int64_t) nullptr));
resultList.push_back(flutter::EncodableValue(glTextureId));
resultList.push_back(flutter::EncodableValue((int64_t)_context));
result->Success(resultList);
}
}
OpenGLTextureBuffer::~OpenGLTextureBuffer() {

View File

@@ -10,7 +10,11 @@ namespace flutter_filament {
WGLContext::WGLContext(flutter::PluginRegistrarWindows *pluginRegistrar,
flutter::TextureRegistrar *textureRegistrar)
: _pluginRegistrar(pluginRegistrar), _textureRegistrar(textureRegistrar) {
: FlutterRenderContext(pluginRegistrar, textureRegistrar) {
#if WGL_USE_BACKING_WINDOW
return;
#endif
auto hwnd = pluginRegistrar->GetView()->GetNativeWindow();
@@ -25,9 +29,9 @@ WGLContext::WGLContext(flutter::PluginRegistrarWindows *pluginRegistrar,
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, // Flags
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, // Flags
PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette.
32, // Colordepth of the framebuffer.
24, // Colordepth of the framebuffer.
0,
0,
0,
@@ -41,7 +45,7 @@ WGLContext::WGLContext(flutter::PluginRegistrarWindows *pluginRegistrar,
0,
0,
0,
32, // Number of bits for the depthbuffer
16, // Number of bits for the depthbuffer
0, // Number of bits for the stencilbuffer
0, // Number of Aux buffers in the framebuffer.
PFD_MAIN_PLANE,
@@ -97,7 +101,9 @@ WGLContext::WGLContext(flutter::PluginRegistrarWindows *pluginRegistrar,
}
void WGLContext::ResizeRenderingSurface(uint32_t width, uint32_t height, uint32_t left, uint32_t top) {
#if WGL_USE_BACKING_WINDOW
_backingWindow->Resize(width, height, left, top);
#endif
}
void WGLContext::CreateRenderingSurface(
@@ -116,7 +122,7 @@ void WGLContext::CreateRenderingSurface(
resultList.push_back(
flutter::EncodableValue((int64_t)_backingWindow->GetHandle()));
resultList.push_back(flutter::EncodableValue((int64_t) nullptr));
resultList.push_back(flutter::EncodableValue((int64_t)sharedContext));
resultList.push_back(flutter::EncodableValue((int64_t)_context));
result->Success(resultList);
#else
if(left != 0 || top != 0) {
@@ -128,20 +134,10 @@ void WGLContext::CreateRenderingSurface(
"attempting to create a new one.");
} else {
_active = std::make_unique<OpenGLTextureBuffer>(
auto active = std::make_unique<OpenGLTextureBuffer>(
_pluginRegistrar, _textureRegistrar, std::move(result), width, height,
_context);
if (_active->flutterTextureId != -1) {
std::vector<flutter::EncodableValue> resultList;
resultList.push_back(flutter::EncodableValue((int64_t) nullptr));
resultList.push_back(flutter::EncodableValue((int64_t) nullptr));
resultList.push_back(flutter::EncodableValue((int64_t) nullptr));
resultList.push_back(flutter::EncodableValue((int64_t)sharedContext));
result->Success(resultList);
} else {
result->Error("NO_FLUTTER_TEXTURE", "Unknown error registering texture with Flutter.", nullptr);
}
_active = std::move(active);
}
#endif
}

View File

@@ -20,9 +20,6 @@ namespace flutter_filament {
uint32_t width, uint32_t height, uint32_t left, uint32_t top
);
private:
flutter::PluginRegistrarWindows* _pluginRegistrar = nullptr;
flutter::TextureRegistrar* _textureRegistrar = nullptr;
HGLRC _context = NULL;
#if WGL_USE_BACKING_WINDOW
std::unique_ptr<BackingWindow> _backingWindow = nullptr;