This commit is contained in:
Nick Fisher
2024-03-01 22:48:39 +08:00
parent 9295059885
commit 6c6bcfe7a4
30 changed files with 1432 additions and 1052 deletions

View File

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

View File

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

View File

@@ -73,8 +73,6 @@ class ExampleWidgetState extends State<ExampleWidget> {
static FilamentEntity? flightHelmet; static FilamentEntity? flightHelmet;
static FilamentEntity? buster; static FilamentEntity? buster;
static List<String>? animations;
static FilamentEntity? directionalLight; static FilamentEntity? directionalLight;
static bool loop = false; static bool loop = false;
@@ -90,8 +88,6 @@ class ExampleWidgetState extends State<ExampleWidget> {
setState(() { setState(() {
_filamentController = FilamentControllerFFI(); _filamentController = FilamentControllerFFI();
}); });
await Future.delayed(const Duration(milliseconds: 100));
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await _filamentController!.createViewer(); await _filamentController!.createViewer();
_createEntityLoadListener(); _createEntityLoadListener();
@@ -101,8 +97,7 @@ class ExampleWidgetState extends State<ExampleWidget> {
await _filamentController!.setRendering(true); await _filamentController!.setRendering(true);
assets.add( assets.add(
await _filamentController!.loadGlb("assets/shapes/shapes.glb")); await _filamentController!.loadGlb("assets/shapes/shapes.glb"));
ExampleWidgetState.animations =
await _filamentController!.getAnimationNames(assets.first);
await _filamentController! await _filamentController!
.setCameraManipulatorOptions(zoomSpeed: 1.0); .setCameraManipulatorOptions(zoomSpeed: 1.0);
@@ -123,7 +118,6 @@ class ExampleWidgetState extends State<ExampleWidget> {
_listener = _listener =
_filamentController!.onLoad.listen((FilamentEntity entity) async { _filamentController!.onLoad.listen((FilamentEntity entity) async {
assets.add(entity); assets.add(entity);
animations = await _filamentController!.getAnimationNames(entity);
if (mounted) { if (mounted) {
setState(() {}); setState(() {});
} }
@@ -136,37 +130,65 @@ class ExampleWidgetState extends State<ExampleWidget> {
final _sharedFocusNode = FocusNode(); final _sharedFocusNode = FocusNode();
Widget _assetEntry(FilamentEntity entity) { Widget _assetEntry(FilamentEntity entity) {
return Row(children: [ return FutureBuilder(
Text("Asset ${entity}"), future: _filamentController!.getAnimationNames(entity),
IconButton( builder: (_, animations) {
tooltip: "Transform to unit cube", if (animations.data == null) {
onPressed: () async { return Container();
await _filamentController!.transformToUnitCube(entity); }
}, return Row(children: [
icon: const Icon(Icons.settings_overscan_outlined)), Text("Asset ${entity}"),
IconButton( IconButton(
onPressed: () async { iconSize: 14,
_transformController?.dispose(); tooltip: "Transform to unit cube",
if (_controlled == entity) { onPressed: () async {
_controlled = null; await _filamentController!.transformToUnitCube(entity);
} else { },
_controlled = entity; icon: const Icon(Icons.settings_overscan_outlined)),
_transformController?.dispose(); IconButton(
_transformController = iconSize: 14,
EntityTransformController(_filamentController!, entity); tooltip: "Attach mouse control",
} onPressed: () async {
setState(() {}); _transformController?.dispose();
}, if (_controlled == entity) {
icon: Icon(Icons.control_camera, _controlled = null;
color: _controlled == entity ? Colors.green : Colors.black)), } else {
IconButton( _controlled = entity;
onPressed: () async { _transformController?.dispose();
await _filamentController!.removeEntity(entity); _transformController =
assets.remove(entity); EntityTransformController(_filamentController!, entity);
setState(() {}); }
}, setState(() {});
icon: const Icon(Icons.cancel_sharp)), },
]); icon: Icon(Icons.control_camera,
color:
_controlled == entity ? Colors.green : Colors.black)),
IconButton(
iconSize: 14,
tooltip: "Remove",
onPressed: () async {
await _filamentController!.removeEntity(entity);
assets.remove(entity);
setState(() {});
},
icon: const Icon(Icons.cancel_sharp)),
if (animations.data!.isNotEmpty)
PopupMenuButton(
tooltip: "Animations",
itemBuilder: (_) => animations.data!
.map((a) => PopupMenuItem<String>(
value: a,
child: Text(a),
))
.toList(),
onSelected: (value) async {
print("Playing animation $value");
await _filamentController!
.playAnimation(entity, animations.data!.indexOf(value!));
},
)
]);
});
} }
@override @override
@@ -222,8 +244,9 @@ class ExampleWidgetState extends State<ExampleWidget> {
), ),
GestureDetector( GestureDetector(
onTap: () async { onTap: () async {
await _filamentController! await _filamentController!.loadGlb(
.loadGlb('assets/shapes/shapes.glb'); 'assets/shapes/shapes.glb',
numInstances: 10);
}, },
child: Container( child: Container(
color: Colors.transparent, color: Colors.transparent,

View File

@@ -43,16 +43,25 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
child: const Text('Find Cylinder entity by name')), child: const Text('Find Cylinder entity by name')),
MenuItemButton( MenuItemButton(
onPressed: () async { onPressed: () async {
Timer.periodic(const Duration(milliseconds: 50), (_) async { await widget.controller.addBoneAnimation(
await widget.controller.setBoneTransform( ExampleWidgetState.assets.last,
ExampleWidgetState.assets.last, BoneAnimationData([
"Cylinder", "Bone"
"Bone", ], [
Matrix4.rotationX(pi / 2)); "Cylinder"
}); ], [
[v.Quaternion.axisAngle(v.Vector3(1, 1, 1), pi / 2)]
], [
[v.Vector3.zero()]
], 16));
}, },
child: child:
const Text('Set bone transform for Cylinder (pi/2 rotation X)')), const Text('Set bone transform for Cylinder (pi/2 rotation X)')),
MenuItemButton(
onPressed: () async {
await widget.controller.resetBones(ExampleWidgetState.assets.last);
},
child: const Text('Reset bones for Cylinder')),
MenuItemButton( MenuItemButton(
onPressed: () async { onPressed: () async {
await widget.controller.addBoneAnimation( await widget.controller.addBoneAnimation(
@@ -72,9 +81,11 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
}, },
child: const Text('Set bone transform animation for Cylinder')), child: const Text('Set bone transform animation for Cylinder')),
MenuItemButton( MenuItemButton(
closeOnActivate: false,
onPressed: () async { onPressed: () async {
var names = await widget.controller.getMorphTargetNames( var names = await widget.controller.getMorphTargetNames(
ExampleWidgetState.assets.last, "Cylinder"); ExampleWidgetState.assets.last, "Cylinder");
print("NAMES : $names");
await showDialog( await showDialog(
context: context, context: context,
builder: (ctx) { builder: (ctx) {
@@ -134,26 +145,6 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
child: Text( child: Text(
"Toggle animation looping ${ExampleWidgetState.loop ? "OFF" : "ON"}")) "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")); return SubmenuButton(menuChildren: children, child: const Text("Shapes"));
} }
@@ -213,8 +204,6 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
force: true); force: true);
await widget.controller await widget.controller
.playAnimation(ExampleWidgetState.buster!, 0, loop: true); .playAnimation(ExampleWidgetState.buster!, 0, loop: true);
ExampleWidgetState.animations = await widget.controller
.getAnimationNames(ExampleWidgetState.assets.last);
} else { } else {
await widget.controller await widget.controller
.removeEntity(ExampleWidgetState.buster!); .removeEntity(ExampleWidgetState.buster!);
@@ -242,7 +231,7 @@ class _AssetSubmenuState extends State<AssetSubmenu> {
: 'Remove flight helmet')), : 'Remove flight helmet')),
MenuItemButton( MenuItemButton(
onPressed: () { onPressed: () {
widget.controller.setBackgroundColor(const Color(0xFF73C9FA)); widget.controller.setBackgroundColor(const Color(0xAA73C9FA));
}, },
child: const Text("Set background color")), child: const Text("Set background color")),
MenuItemButton( MenuItemButton(

View File

@@ -2,7 +2,7 @@ import 'dart:async';
import 'dart:math'; import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:vector_math/vector_math_64.dart' as v;
import 'package:flutter_filament/filament_controller.dart'; import 'package:flutter_filament/filament_controller.dart';
import 'package:flutter_filament_example/main.dart'; import 'package:flutter_filament_example/main.dart';
@@ -99,7 +99,8 @@ class _CameraSubmenuState extends State<CameraSubmenu> {
MenuItemButton( MenuItemButton(
onPressed: () async { onPressed: () async {
widget.controller.setCameraPosition(0.0, 0.0, 0.0); 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'), child: const Text('Move to 0,0,0, facing towards 0,0,-1'),
), ),
@@ -119,7 +120,8 @@ class _CameraSubmenuState extends State<CameraSubmenu> {
), ),
MenuItemButton( MenuItemButton(
onPressed: () { 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"), child: const Text("Rotate camera 45 degrees around y axis"),
), ),

View File

@@ -9,11 +9,8 @@ class PickerResultWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return StreamBuilder( return StreamBuilder(
stream: controller.pickResult.map((FilamentEntity? entityId) { stream: controller.pickResult.map((result) {
if (entityId == null) { return controller.getNameForEntity(result.entity);
return null;
}
return controller.getNameForEntity(entityId);
}), }),
builder: (ctx, snapshot) => snapshot.data == null builder: (ctx, snapshot) => snapshot.data == null
? Container() ? Container()

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 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 remove_light(const void *const viewer, EntityId entityId);
FLUTTER_PLUGIN_EXPORT void clear_lights(const void *const viewer); 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 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 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 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 set_view_frustum_culling(const void *const viewer, bool enabled);
FLUTTER_PLUGIN_EXPORT void render( 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_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 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 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 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_model_matrix(const void *const viewer);
FLUTTER_PLUGIN_EXPORT const double *const get_camera_view_matrix(const void *const viewer); FLUTTER_PLUGIN_EXPORT const double *const get_camera_view_matrix(const void *const viewer);

View File

@@ -38,34 +38,36 @@ 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 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 remove_light_ffi(void* const viewer, EntityId entityId);
FLUTTER_PLUGIN_EXPORT void clear_lights_ffi(void* const viewer); 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_glb_ffi(void* const sceneManager, const char *assetPath, int numInstances);
FLUTTER_PLUGIN_EXPORT EntityId load_gltf_ffi(void* const assetManager, const char *assetPath, const char *relativePath); FLUTTER_PLUGIN_EXPORT EntityId load_glb_from_buffer_ffi(void* const sceneManager, const void* const data, size_t length, int numInstances);
FLUTTER_PLUGIN_EXPORT EntityId load_gltf_ffi(void* const sceneManager, const char *assetPath, const char *relativePath);
FLUTTER_PLUGIN_EXPORT EntityId create_instance_ffi(void* const sceneManager, EntityId entityId);
FLUTTER_PLUGIN_EXPORT void remove_entity_ffi(void* const viewer, EntityId asset); 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 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 bool set_camera_ffi(void* const viewer, EntityId asset, const char *nodeName);
FLUTTER_PLUGIN_EXPORT void apply_weights_ffi( FLUTTER_PLUGIN_EXPORT void apply_weights_ffi(
void* const assetManager, void* const sceneManager,
EntityId asset, EntityId asset,
const char *const entityName, const char *const entityName,
float *const weights, float *const weights,
int count 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 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 assetManager, EntityId asset, int animationIndex, int animationFrame); 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 assetManager, EntityId asset, int index); FLUTTER_PLUGIN_EXPORT void stop_animation_ffi(void* const sceneManager, EntityId asset, int index);
FLUTTER_PLUGIN_EXPORT int get_animation_count_ffi(void* const assetManager, EntityId asset); FLUTTER_PLUGIN_EXPORT int get_animation_count_ffi(void* const sceneManager, 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_animation_name_ffi(void* const sceneManager, 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 void get_morph_target_name_ffi(void* const sceneManager, 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 int get_morph_target_name_count_ffi(void* const sceneManager, EntityId asset, const char *meshName);
FLUTTER_PLUGIN_EXPORT void set_morph_target_weights_ffi(void* const assetManager, FLUTTER_PLUGIN_EXPORT void set_morph_target_weights_ffi(void* const sceneManager,
EntityId asset, EntityId asset,
const char *const entityName, const char *const entityName,
const float *const morphData, const float *const morphData,
int numWeights int numWeights
); );
FLUTTER_PLUGIN_EXPORT bool set_morph_animation_ffi( FLUTTER_PLUGIN_EXPORT bool set_morph_animation_ffi(
void *assetManager, void *sceneManager,
EntityId asset, EntityId asset,
const char *const entityName, const char *const entityName,
const float *const morphData, const float *const morphData,
@@ -74,13 +76,13 @@ FLUTTER_PLUGIN_EXPORT bool set_morph_animation_ffi(
int numFrames, int numFrames,
float frameLengthInMs); float frameLengthInMs);
FLUTTER_PLUGIN_EXPORT bool set_bone_transform_ffi( FLUTTER_PLUGIN_EXPORT bool set_bone_transform_ffi(
void *assetManager, void *sceneManager,
EntityId asset, EntityId asset,
const char *entityName, const char *entityName,
const float *const transform, const float *const transform,
const char *boneName); const char *boneName);
FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi( FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi(
void *assetManager, void *sceneManager,
EntityId asset, EntityId asset,
const float *const frameData, const float *const frameData,
int numFrames, int numFrames,
@@ -91,7 +93,7 @@ FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi(
bool isModelSpace); bool isModelSpace);
FLUTTER_PLUGIN_EXPORT void set_post_processing_ffi(void* const viewer, bool enabled); FLUTTER_PLUGIN_EXPORT void set_post_processing_ffi(void* const viewer, bool enabled);
FLUTTER_PLUGIN_EXPORT void pick_ffi(void* const viewer, int x, int y, EntityId* entityId); 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 reset_to_rest_pose_ffi(void* const sceneManager, EntityId entityId);
FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi(); FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi();
FLUTTER_PLUGIN_EXPORT EntityId create_geometry_ffi(void* const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath); FLUTTER_PLUGIN_EXPORT EntityId create_geometry_ffi(void* const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath);

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,64 @@
#pragma once #pragma once
#include <mutex> #include <mutex>
#include <vector>
#include <memory>
#include <map>
#include <filament/Scene.h> #include <filament/Scene.h>
#include <gltfio/AssetLoader.h> #include <gltfio/AssetLoader.h>
#include <gltfio/FilamentAsset.h> #include <gltfio/FilamentAsset.h>
#include <gltfio/FilamentInstance.h>
#include <gltfio/ResourceLoader.h> #include <gltfio/ResourceLoader.h>
#include "SceneAsset.hpp" #include "utils/NameComponentManager.h"
#include "ResourceBuffer.hpp" #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;
using namespace filament::gltfio; using namespace filament::gltfio;
using namespace utils;
using std::vector;
using std::unique_ptr;
using std::string;
class SceneManager class SceneManager
{ {
public: public:
SceneManager(const ResourceLoaderWrapper *const loader, SceneManager(const ResourceLoaderWrapper *const loader,
NameComponentManager *ncm,
Engine *engine, Engine *engine,
Scene *scene, Scene *scene,
const char *uberArchivePath); const char *uberArchivePath);
~SceneManager(); ~SceneManager();
EntityId loadGltf(const char *uri, const char *relativeResourcePath); EntityId loadGltf(const char *uri, const char *relativeResourcePath);
EntityId loadGlb(const char *uri, bool unlit);
FilamentAsset *getAssetByEntityId(EntityId entityId); ////
/// @brief
/// @param uri
/// @param numInstances
/// @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 remove(EntityId entity);
void destroyAll(); void destroyAll();
unique_ptr<vector<string>> getAnimationNames(EntityId entity); unique_ptr<vector<string>> getAnimationNames(EntityId entity);
float getAnimationDuration(EntityId entity, int animationIndex); 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); void transformToUnitCube(EntityId e);
inline void updateTransform(EntityId e); inline void updateTransform(EntityId e);
void setScale(EntityId e, float scale); void setScale(EntityId e, float scale);
@@ -45,8 +68,8 @@ namespace polyvox
void queueRotationUpdate(EntityId e, float rads, float x, float y, float z, float w, bool relative); void queueRotationUpdate(EntityId e, float rads, float x, float y, float z, float w, bool relative);
const utils::Entity *getCameraEntities(EntityId e); const utils::Entity *getCameraEntities(EntityId e);
size_t getCameraEntityCount(EntityId e); size_t getCameraEntityCount(EntityId e);
const utils::Entity *getLightEntities(EntityId e) const noexcept; const utils::Entity *getLightEntities(EntityId e) noexcept;
size_t getLightEntityCount(EntityId e) const noexcept; size_t getLightEntityCount(EntityId e) noexcept;
void updateAnimations(); void updateAnimations();
void updateTransforms(); void updateTransforms();
void testCollisions(EntityId entity); void testCollisions(EntityId entity);
@@ -107,29 +130,48 @@ namespace polyvox
void addCollisionComponent(EntityId entity, void (*onCollisionCallback)(const EntityId entityId1, const EntityId entityId2), bool affectsCollidingTransform); void addCollisionComponent(EntityId entity, void (*onCollisionCallback)(const EntityId entityId1, const EntityId entityId2), bool affectsCollidingTransform);
void removeCollisionComponent(EntityId entityId); void removeCollisionComponent(EntityId entityId);
void setParent(EntityId child, EntityId parent); void setParent(EntityId child, EntityId parent);
void addAnimatableComponent(EntityId entity);
/// @brief returns the number of instances of the FilamentAsset represented by the given entity.
/// @param entityId
/// @return
int getInstanceCount(EntityId entityId);
/// @brief returns an array containing all instances of the FilamentAsset represented by the given entity.
/// @param entityId
/// @return
void getInstances(EntityId entityId, EntityId* out);
friend class FilamentViewer;
private: private:
AssetLoader *_assetLoader = nullptr; gltfio::AssetLoader *_assetLoader = nullptr;
const ResourceLoaderWrapper *const _resourceLoaderWrapper; const ResourceLoaderWrapper *const _resourceLoaderWrapper;
NameComponentManager *_ncm = nullptr;
Engine *_engine; Engine *_engine;
Scene *_scene; Scene *_scene;
MaterialProvider *_ubershaderProvider = nullptr; gltfio::MaterialProvider *_ubershaderProvider = nullptr;
gltfio::ResourceLoader *_gltfResourceLoader = nullptr; gltfio::ResourceLoader *_gltfResourceLoader = nullptr;
gltfio::TextureProvider *_stbDecoder = nullptr; gltfio::TextureProvider *_stbDecoder = nullptr;
gltfio::TextureProvider *_ktxDecoder = nullptr; gltfio::TextureProvider *_ktxDecoder = nullptr;
std::mutex _mutex; std::mutex _mutex;
vector<SceneAsset> _assets; utils::NameComponentManager* _ncm;
tsl::robin_map<EntityId, int> _entityIdLookup;
tsl::robin_map<EntityId, std::tuple<math::float3,bool,math::quatf,bool,float>> _transformUpdates;
std::vector<EntityId> _nonTransformableCollidableEntities;
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;
AnimationComponentManager* _animationComponentManager = nullptr;
CollisionComponentManager* _collisionComponentManager = nullptr; CollisionComponentManager* _collisionComponentManager = nullptr;
gltfio::FilamentInstance* getInstanceByEntityId(EntityId entityId);
gltfio::FilamentAsset* getAssetByEntityId(EntityId entityId);
utils::Entity findEntityByName( utils::Entity findEntityByName(
SceneAsset asset, const gltfio::FilamentInstance* instance,
const char *entityName); const char *entityName);
}; };

View File

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

View File

@@ -91,11 +91,6 @@
#include "TimeIt.hpp" #include "TimeIt.hpp"
#include "ThreadPool.hpp" #include "ThreadPool.hpp"
using namespace filament;
using namespace filament::math;
using namespace gltfio;
using namespace utils;
using namespace image;
namespace filament namespace filament
{ {
@@ -103,9 +98,18 @@ namespace filament
class LightManager; class LightManager;
} // namespace filament } // 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 kAperture = 1.0f;
// const float kShutterSpeed = 1.0f; // const float kShutterSpeed = 1.0f;
// const float kSensitivity = 50.0f; // const float kSensitivity = 50.0f;
@@ -135,6 +139,8 @@ namespace polyvox
_engine = Engine::create(Engine::Backend::OPENGL, (backend::Platform *)platform, (void *)sharedContext, nullptr); _engine = Engine::create(Engine::Backend::OPENGL, (backend::Platform *)platform, (void *)sharedContext, nullptr);
#endif #endif
_engine->setAutomaticInstancingEnabled(true);
_renderer = _engine->createRenderer(); _renderer = _engine->createRenderer();
_frameInterval = 1000.0f / 60.0f; _frameInterval = 1000.0f / 60.0f;
@@ -151,6 +157,7 @@ namespace polyvox
Log("Main camera created"); Log("Main camera created");
_view = _engine->createView(); _view = _engine->createView();
Log("View created"); Log("View created");
setToneMapping(ToneMapping::ACES); setToneMapping(ToneMapping::ACES);
@@ -201,14 +208,10 @@ namespace polyvox
setAntiAliasing(false, true, false); setAntiAliasing(false, true, false);
EntityManager &em = EntityManager::get(); EntityManager &em = EntityManager::get();
_ncm = new NameComponentManager(em);
_sceneManager = new SceneManager( _sceneManager = new SceneManager(
_resourceLoaderWrapper, _resourceLoaderWrapper,
_ncm,
_engine, _engine,
_scene, _scene,
uberArchivePath); uberArchivePath);
@@ -339,7 +342,7 @@ 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) int32_t FilamentViewer::addLight(LightManager::Type t, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows)
{ {
auto light = EntityManager::get().create(); auto light = EntityManager::get().create();
LightManager::Builder(t) auto builder = LightManager::Builder(t)
.color(Color::cct(colour)) .color(Color::cct(colour))
.intensity(intensity) .intensity(intensity)
.position(math::float3(posX, posY, posZ)) .position(math::float3(posX, posY, posZ))
@@ -377,7 +380,7 @@ namespace polyvox
_lights.clear(); _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; return path.compare(path.length() - ending.length(), ending.length(), ending) == 0;
} }
@@ -435,7 +438,7 @@ namespace polyvox
void FilamentViewer::loadPngTexture(string path, ResourceBuffer rb) 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); std::istream inputStream(&sb);
@@ -480,7 +483,7 @@ namespace polyvox
void FilamentViewer::loadTextureFromPath(string path) void FilamentViewer::loadTextureFromPath(string path)
{ {
string ktxExt(".ktx"); std::string ktxExt(".ktx");
string ktx2Ext(".ktx2"); string ktx2Ext(".ktx2");
string pngExt(".png"); string pngExt(".png");
@@ -802,10 +805,22 @@ namespace polyvox
cam.setFocusDistance(_cameraFocusDistance); cam.setFocusDistance(_cameraFocusDistance);
} }
///
///
///
void FilamentViewer::setMainCamera() { void FilamentViewer::setMainCamera() {
_view->setCamera(_mainCamera); _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). /// 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 - /// N.B. Blender will generally export a three-node hierarchy -
@@ -833,17 +848,15 @@ namespace polyvox
if (!cameraName) if (!cameraName)
{ {
auto inst = _ncm->getInstance(cameras[0]);
const char *name = _ncm->getName(inst);
target = cameras[0]; target = cameras[0];
const char *name = asset->getName(target);
Log("No camera specified, using first camera node found (%s)", name); Log("No camera specified, using first camera node found (%s)", name);
} }
else else
{ {
for (int j = 0; j < count; j++) for (int j = 0; j < count; j++)
{ {
auto inst = _ncm->getInstance(cameras[j]); const char *name = asset->getName(cameras[j]);
const char *name = _ncm->getName(inst);
if (strcmp(name, cameraName) == 0) if (strcmp(name, cameraName) == 0)
{ {
target = cameras[j]; target = cameras[j];
@@ -1138,7 +1151,7 @@ namespace polyvox
std::string filename = stringStream.str(); 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, LinearImage image(toLinearWithAlpha<uint8_t>(vp.width, vp.height, vp.width * 4,
static_cast<uint8_t *>(buf))); static_cast<uint8_t *>(buf)));
@@ -1228,7 +1241,7 @@ namespace polyvox
Camera &cam = _view->getCamera(); Camera &cam = _view->getCamera();
_cameraPosition = math::mat4f::translation(math::float3(x, y, z)); _cameraPosition = math::mat4f::translation(math::float3(x, y, z));
cam.setModelMatrix(_cameraPosition * _cameraRotation); cam.setModelMatrix(_cameraRotation * _cameraPosition);
} }
void FilamentViewer::moveCameraToAsset(EntityId entityId) void FilamentViewer::moveCameraToAsset(EntityId entityId)
@@ -1249,11 +1262,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()); 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(); Camera &cam = _view->getCamera();
_cameraRotation = math::mat4f::rotation(rads, math::float3(x, y, z)); _cameraRotation = math::mat4f(math::quatf(w, x, y, z));
cam.setModelMatrix(_cameraPosition * _cameraRotation); cam.setModelMatrix(_cameraRotation * _cameraPosition);
} }
void FilamentViewer::setCameraModelMatrix(const float *const matrix) void FilamentViewer::setCameraModelMatrix(const float *const matrix)
@@ -1455,7 +1468,10 @@ namespace polyvox
void FilamentViewer::pick(uint32_t x, uint32_t y, EntityId *entityId) void FilamentViewer::pick(uint32_t x, uint32_t y, EntityId *entityId)
{ {
_view->pick(x, y, [=](filament::View::PickingQueryResult const &result) _view->pick(x, y, [=](filament::View::PickingQueryResult const &result)
{ *entityId = Entity::smuggle(result.renderable); }); {
*entityId = Entity::smuggle(result.renderable);
});
} }
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, const char* materialPath)
@@ -1523,4 +1539,4 @@ namespace polyvox
return Entity::smuggle(renderable); return Entity::smuggle(renderable);
} }
} // namespace polyvox } // namespace flutter_filament

View File

@@ -8,7 +8,7 @@
#include <thread> #include <thread>
#include <functional> #include <functional>
using namespace polyvox; using namespace flutter_filament;
extern "C" extern "C"
{ {
@@ -114,9 +114,26 @@ extern "C"
((FilamentViewer *)viewer)->clearLights(); ((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) FLUTTER_PLUGIN_EXPORT EntityId load_gltf(void *sceneManager, const char *assetPath, const char *relativePath)
@@ -129,6 +146,12 @@ extern "C"
return ((FilamentViewer *)viewer)->setMainCamera(); 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) FLUTTER_PLUGIN_EXPORT bool set_camera(const void *const viewer, EntityId asset, const char *nodeName)
{ {
return ((FilamentViewer *)viewer)->setCamera(asset, nodeName); return ((FilamentViewer *)viewer)->setCamera(asset, nodeName);
@@ -236,9 +259,9 @@ extern "C"
((FilamentViewer *)viewer)->setCameraPosition(x, y, z); ((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) FLUTTER_PLUGIN_EXPORT void set_camera_model_matrix(const void *const viewer, const float *const matrix)
@@ -448,20 +471,20 @@ extern "C"
int index) int index)
{ {
auto names = ((SceneManager *)sceneManager)->getAnimationNames(asset); auto names = ((SceneManager *)sceneManager)->getAnimationNames(asset);
string name = names->at(index); std::string name = names->at(index);
strcpy(outPtr, name.c_str()); strcpy(outPtr, name.c_str());
} }
FLUTTER_PLUGIN_EXPORT int get_morph_target_name_count(void *sceneManager, EntityId asset, const char *meshName) 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(); return (int)names->size();
} }
FLUTTER_PLUGIN_EXPORT void get_morph_target_name(void *sceneManager, EntityId asset, const char *meshName, char *const outPtr, int index) 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); std::unique_ptr<std::vector<std::string>> names = ((SceneManager *)sceneManager)->getMorphTargetNames(asset, meshName);
string name = names->at(index); std::string name = names->at(index);
strcpy(outPtr, name.c_str()); strcpy(outPtr, name.c_str());
} }
@@ -572,7 +595,7 @@ extern "C"
FLUTTER_PLUGIN_EXPORT EntityId find_child_entity_by_name(void *const sceneManager, const EntityId parent, const char* name) { FLUTTER_PLUGIN_EXPORT EntityId find_child_entity_by_name(void *const sceneManager, const EntityId parent, const char* name) {
auto entity = ((SceneManager*)sceneManager)->findChildEntityByName(parent, 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) { FLUTTER_PLUGIN_EXPORT void set_parent(void *const sceneManager, EntityId child, EntityId parent) {

View File

@@ -31,7 +31,7 @@ extern "C"
#include <pthread.h> #include <pthread.h>
#endif #endif
using namespace polyvox; using namespace flutter_filament;
using namespace std::chrono_literals; using namespace std::chrono_literals;
class RenderLoop { class RenderLoop {
@@ -262,21 +262,30 @@ set_background_color_ffi(void *const viewer, const float r, const float g,
fut.wait(); fut.wait();
} }
FLUTTER_PLUGIN_EXPORT EntityId load_gltf_ffi(void *const assetManager, FLUTTER_PLUGIN_EXPORT EntityId load_gltf_ffi(void *const sceneManager,
const char *path, const char *path,
const char *relativeResourcePath) { const char *relativeResourcePath) {
std::packaged_task<EntityId()> lambda([&]() mutable { std::packaged_task<EntityId()> lambda([&]() mutable {
return load_gltf(assetManager, path, relativeResourcePath); return load_gltf(sceneManager, path, relativeResourcePath);
}); });
auto fut = _rl->add_task(lambda); auto fut = _rl->add_task(lambda);
fut.wait(); fut.wait();
return fut.get(); return fut.get();
} }
FLUTTER_PLUGIN_EXPORT EntityId load_glb_ffi(void *const assetManager, FLUTTER_PLUGIN_EXPORT EntityId load_glb_ffi(void *const sceneManager,
const char *path, bool unlit) { const char *path, int numInstances) {
std::packaged_task<EntityId()> lambda( std::packaged_task<EntityId()> lambda(
[&]() mutable { return load_glb(assetManager, path, unlit); }); [&]() mutable { return load_glb(sceneManager, path, numInstances); });
auto fut = _rl->add_task(lambda);
fut.wait();
return fut.get();
}
FLUTTER_PLUGIN_EXPORT EntityId load_glb_from_buffer_ffi(void *const sceneManager,
const void *const data, size_t length, int numInstances) {
std::packaged_task<EntityId()> lambda(
[&]() mutable { return load_glb_from_buffer(sceneManager, data, length); });
auto fut = _rl->add_task(lambda); auto fut = _rl->add_task(lambda);
fut.wait(); fut.wait();
return fut.get(); return fut.get();
@@ -388,20 +397,20 @@ FLUTTER_PLUGIN_EXPORT bool set_camera_ffi(void *const viewer, EntityId asset,
} }
FLUTTER_PLUGIN_EXPORT void FLUTTER_PLUGIN_EXPORT void
get_morph_target_name_ffi(void *assetManager, EntityId asset, get_morph_target_name_ffi(void *sceneManager, EntityId asset,
const char *meshName, char *const outPtr, int index) { const char *meshName, char *const outPtr, int index) {
std::packaged_task<void()> lambda([&] { std::packaged_task<void()> lambda([&] {
get_morph_target_name(assetManager, asset, meshName, outPtr, index); get_morph_target_name(sceneManager, asset, meshName, outPtr, index);
}); });
auto fut = _rl->add_task(lambda); auto fut = _rl->add_task(lambda);
fut.wait(); fut.wait();
} }
FLUTTER_PLUGIN_EXPORT int FLUTTER_PLUGIN_EXPORT int
get_morph_target_name_count_ffi(void *assetManager, EntityId asset, get_morph_target_name_count_ffi(void *sceneManager, EntityId asset,
const char *meshName) { const char *meshName) {
std::packaged_task<int()> lambda([&] { std::packaged_task<int()> lambda([&] {
return get_morph_target_name_count(assetManager, asset, meshName); return get_morph_target_name_count(sceneManager, asset, meshName);
}); });
auto fut = _rl->add_task(lambda); auto fut = _rl->add_task(lambda);
fut.wait(); fut.wait();
@@ -410,52 +419,52 @@ get_morph_target_name_count_ffi(void *assetManager, EntityId asset,
FLUTTER_PLUGIN_EXPORT void play_animation_ffi(void *const assetManager, FLUTTER_PLUGIN_EXPORT void play_animation_ffi(void *const sceneManager,
EntityId asset, int index, EntityId asset, int index,
bool loop, bool reverse, bool loop, bool reverse,
bool replaceActive, bool replaceActive,
float crossfade) { float crossfade) {
std::packaged_task<void()> lambda([&] { std::packaged_task<void()> lambda([&] {
play_animation(assetManager, asset, index, loop, reverse, replaceActive, play_animation(sceneManager, asset, index, loop, reverse, replaceActive,
crossfade); crossfade);
}); });
auto fut = _rl->add_task(lambda); auto fut = _rl->add_task(lambda);
fut.wait(); 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, EntityId asset,
int animationIndex, int animationIndex,
int animationFrame) { int animationFrame) {
std::packaged_task<void()> lambda([&] { std::packaged_task<void()> lambda([&] {
set_animation_frame(assetManager, asset, animationIndex, animationFrame); set_animation_frame(sceneManager, asset, animationIndex, animationFrame);
}); });
auto fut = _rl->add_task(lambda); auto fut = _rl->add_task(lambda);
fut.wait(); 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) { EntityId asset, int index) {
std::packaged_task<void()> lambda( std::packaged_task<void()> lambda(
[&] { stop_animation(assetManager, asset, index); }); [&] { stop_animation(sceneManager, asset, index); });
auto fut = _rl->add_task(lambda); auto fut = _rl->add_task(lambda);
fut.wait(); fut.wait();
} }
FLUTTER_PLUGIN_EXPORT int get_animation_count_ffi(void *const assetManager, FLUTTER_PLUGIN_EXPORT int get_animation_count_ffi(void *const sceneManager,
EntityId asset) { EntityId asset) {
std::packaged_task<int()> lambda( std::packaged_task<int()> lambda(
[&] { return get_animation_count(assetManager, asset); }); [&] { return get_animation_count(sceneManager, asset); });
auto fut = _rl->add_task(lambda); auto fut = _rl->add_task(lambda);
fut.wait(); fut.wait();
return fut.get(); 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, EntityId asset,
char *const outPtr, char *const outPtr,
int index) { int index) {
std::packaged_task<void()> lambda( std::packaged_task<void()> lambda(
[&] { get_animation_name(assetManager, asset, outPtr, index); }); [&] { get_animation_name(sceneManager, asset, outPtr, index); });
auto fut = _rl->add_task(lambda); auto fut = _rl->add_task(lambda);
fut.wait(); fut.wait();
} }
@@ -476,27 +485,27 @@ FLUTTER_PLUGIN_EXPORT void pick_ffi(void *const viewer, int x, int y,
} }
FLUTTER_PLUGIN_EXPORT const char * FLUTTER_PLUGIN_EXPORT const char *
get_name_for_entity_ffi(void *const assetManager, const EntityId entityId) { get_name_for_entity_ffi(void *const sceneManager, const EntityId entityId) {
std::packaged_task<const char *()> lambda( std::packaged_task<const char *()> lambda(
[&] { return get_name_for_entity(assetManager, entityId); }); [&] { return get_name_for_entity(sceneManager, entityId); });
auto fut = _rl->add_task(lambda); auto fut = _rl->add_task(lambda);
fut.wait(); fut.wait();
return fut.get(); return fut.get();
} }
void set_morph_target_weights_ffi(void *const assetManager, void set_morph_target_weights_ffi(void *const sceneManager,
EntityId asset, EntityId asset,
const char *const entityName, const char *const entityName,
const float *const morphData, const float *const morphData,
int numWeights) { int numWeights) {
std::packaged_task<void()> lambda( std::packaged_task<void()> lambda(
[&] { return set_morph_target_weights(assetManager, asset, entityName, morphData, numWeights); }); [&] { return set_morph_target_weights(sceneManager, asset, entityName, morphData, numWeights); });
auto fut = _rl->add_task(lambda); auto fut = _rl->add_task(lambda);
fut.wait(); fut.wait();
} }
bool set_morph_animation_ffi( bool set_morph_animation_ffi(
void *assetManager, void *sceneManager,
EntityId asset, EntityId asset,
const char *const entityName, const char *const entityName,
const float *const morphData, const float *const morphData,
@@ -506,7 +515,7 @@ bool set_morph_animation_ffi(
float frameLengthInMs) { float frameLengthInMs) {
std::packaged_task<bool()> lambda( std::packaged_task<bool()> lambda(
[&] { [&] {
return set_morph_animation(assetManager, asset, entityName, morphData, morphIndices, numMorphTargets, numFrames, frameLengthInMs); return set_morph_animation(sceneManager, asset, entityName, morphData, morphIndices, numMorphTargets, numFrames, frameLengthInMs);
}); });
auto fut = _rl->add_task(lambda); auto fut = _rl->add_task(lambda);
fut.wait(); fut.wait();
@@ -515,27 +524,27 @@ bool set_morph_animation_ffi(
FLUTTER_PLUGIN_EXPORT bool set_bone_transform_ffi( FLUTTER_PLUGIN_EXPORT bool set_bone_transform_ffi(
void *assetManager, void *sceneManager,
EntityId asset, EntityId asset,
const char *entityName, const char *entityName,
const float *const transform, const float *const transform,
const char *boneName) { const char *boneName) {
std::packaged_task<bool()> lambda( std::packaged_task<bool()> lambda(
[&] { return set_bone_transform(assetManager, asset, entityName, transform, boneName); }); [&] { return set_bone_transform(sceneManager, asset, entityName, transform, boneName); });
auto fut = _rl->add_task(lambda); auto fut = _rl->add_task(lambda);
fut.wait(); fut.wait();
return fut.get(); 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( 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); auto fut = _rl->add_task(lambda);
fut.wait(); fut.wait();
} }
FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi( FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi(
void *assetManager, void *sceneManager,
EntityId asset, EntityId asset,
const float *const frameData, const float *const frameData,
int numFrames, int numFrames,
@@ -547,7 +556,7 @@ FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi(
std::packaged_task<void()> lambda( 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); auto fut = _rl->add_task(lambda);
fut.wait(); fut.wait();

File diff suppressed because it is too large Load Diff

View File

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

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,7 @@
// ignore_for_file: constant_identifier_names // ignore_for_file: constant_identifier_names
import 'dart:async'; import 'dart:async';
import 'dart:typed_data';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
@@ -12,6 +13,9 @@ import 'package:vector_math/vector_math_64.dart';
// a handle that can be safely passed back to the rendering layer to manipulate an Entity // a handle that can be safely passed back to the rendering layer to manipulate an Entity
typedef FilamentEntity = int; typedef FilamentEntity = int;
// "picking" means clicking/tapping on the viewport, and unprojecting the X/Y coordinate to determine whether any renderable entities were present at those coordinates.
typedef FilamentPickResult = ({FilamentEntity entity, double x, double y});
enum ToneMapper { ACES, FILMIC, LINEAR } enum ToneMapper { ACES, FILMIC, LINEAR }
// see filament Manipulator.h for more details // see filament Manipulator.h for more details
@@ -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]. /// 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. /// 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]. /// Whether the controller is currently rendering at [framerate].
@@ -206,7 +210,35 @@ abstract class FilamentController {
/// ///
/// Load the .glb asset at the given path and insert into the scene. /// 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});
///
/// Load the .glb asset from the specified path (either Flutter asset URI or filepath) and insert into the scene.
/// If [cache] is true, the contents of the path will be cached locally and re-used for any future calls to load that asset.
/// See also [evictCache].
///
Future<FilamentEntity> loadGlbFromBuffer(String path,
{bool cache = false, int numInstances = 1});
///
/// Create a new instance of [entity].
///
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);
///
/// Frees all cached resources loaded via [loadGlbFromBuffer].
///
Future evictCache();
/// ///
/// Load the .gltf asset at the given path and insert into the scene. /// Load the .gltf asset at the given path and insert into the scene.
@@ -281,16 +313,11 @@ abstract class FilamentController {
Future resetBones(FilamentEntity entity); 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); 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. /// 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. /// [entity] will no longer be a valid handle after this method is called; ensure you immediately discard all references once this method is complete.
@@ -350,6 +377,11 @@ abstract class FilamentController {
/// ///
Future setMainCamera(); 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]. /// Sets the current scene camera to the glTF camera under [name] in [entity].
/// ///
@@ -450,7 +482,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. /// 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. /// Sets the camera model matrix.

View File

@@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:ffi'; import 'dart:ffi';
import 'dart:io'; import 'dart:io';
import 'dart:typed_data';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'dart:developer' as dev; import 'dart:developer' as dev;
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@@ -52,8 +53,9 @@ class FilamentControllerFFI extends FilamentController {
final hasViewer = ValueNotifier<bool>(false); final hasViewer = ValueNotifier<bool>(false);
@override @override
Stream<FilamentEntity> get pickResult => _pickResultController.stream; Stream<FilamentPickResult> get pickResult => _pickResultController.stream;
final _pickResultController = StreamController<FilamentEntity>.broadcast(); final _pickResultController =
StreamController<FilamentPickResult>.broadcast();
int? _resizingWidth; int? _resizingWidth;
int? _resizingHeight; int? _resizingHeight;
@@ -545,8 +547,82 @@ class FilamentControllerFFI extends FilamentController {
_lights.clear(); _lights.clear();
} }
final _assetCache = <String, (Pointer<Void>, int)>{};
@override @override
Future<FilamentEntity> loadGlb(String path, {bool unlit = false}) async { Future<FilamentEntity> loadGlbFromBuffer(String path,
{bool cache = false, int numInstances = 1}) async {
late (Pointer<Void>, int) data;
if (cache && _assetCache.containsKey(path)) {
data = _assetCache[path]!;
} else {
late ByteData asset;
if (path.startsWith("file://")) {
var raw = File(path.replaceAll("file://", "")).readAsBytesSync();
asset = raw.buffer.asByteData(raw.offsetInBytes);
} else {
asset = await rootBundle.load(path.replaceAll("asset://", ""));
}
var ptr = allocator<Char>(asset.lengthInBytes);
for (int i = 0; i < asset.lengthInBytes; i++) {
ptr[i] = asset.getUint8(i);
}
data = (ptr.cast<Void>(), asset.lengthInBytes);
}
var entity = load_glb_from_buffer_ffi(
_sceneManager!, data.$1, data.$2, numInstances);
if (!cache) {
allocator.free(data.$1);
} else {
_assetCache[path] = data;
}
if (entity == _FILAMENT_ASSET_ERROR) {
throw Exception("Failed to load GLB from path $path");
}
return entity;
}
@override
Future<FilamentEntity> createInstance(FilamentEntity entity) async {
var created = 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 evictCache() async {
for (final value in _assetCache.values) {
allocator.free(value.$1);
}
_assetCache.clear();
}
@override
Future<FilamentEntity> loadGlb(String path,
{bool unlit = false, int numInstances = 1}) async {
if (_viewer == null) { if (_viewer == null) {
throw Exception("No viewer available, ignoring"); throw Exception("No viewer available, ignoring");
} }
@@ -554,7 +630,7 @@ class FilamentControllerFFI extends FilamentController {
throw Exception("Not yet implemented"); throw Exception("Not yet implemented");
} }
final pathPtr = path.toNativeUtf8().cast<Char>(); final pathPtr = path.toNativeUtf8().cast<Char>();
var entity = load_glb_ffi(_sceneManager!, pathPtr, unlit); var entity = load_glb_ffi(_sceneManager!, pathPtr, numInstances);
allocator.free(pathPtr); allocator.free(pathPtr);
if (entity == _FILAMENT_ASSET_ERROR) { if (entity == _FILAMENT_ASSET_ERROR) {
throw Exception("An error occurred loading the asset at $path"); throw Exception("An error occurred loading the asset at $path");
@@ -910,6 +986,10 @@ class FilamentControllerFFI extends FilamentController {
set_main_camera(_viewer!); set_main_camera(_viewer!);
} }
Future<FilamentEntity> getMainCamera() async {
return get_main_camera(_viewer!);
}
@override @override
Future setCamera(FilamentEntity entity, String? name) async { Future setCamera(FilamentEntity entity, String? name) async {
if (_viewer == null) { if (_viewer == null) {
@@ -1027,11 +1107,12 @@ class FilamentControllerFFI extends FilamentController {
} }
@override @override
Future setCameraRotation(double rads, double x, double y, double z) async { Future setCameraRotation(Quaternion quaternion) async {
if (_viewer == null) { if (_viewer == null) {
throw Exception("No viewer available, ignoring"); 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 @override
@@ -1158,13 +1239,13 @@ class FilamentControllerFFI extends FilamentController {
} }
@override @override
Future reveal(FilamentEntity entity, String meshName) async { Future reveal(FilamentEntity entity, String? meshName) async {
if (_viewer == null) { if (_viewer == null) {
throw Exception("No viewer available, ignoring"); throw Exception("No viewer available, ignoring");
} }
final meshNamePtr = meshName.toNativeUtf8().cast<Char>(); final meshNamePtr = meshName?.toNativeUtf8().cast<Char>() ?? nullptr;
final result = reveal_mesh(_sceneManager!, entity, meshNamePtr) != 1; final result = reveal_mesh(_sceneManager!, entity, meshNamePtr) == 1;
allocator.free(meshNamePtr); allocator.free(meshNamePtr);
if (!result) { if (!result) {
throw Exception("Failed to reveal mesh $meshName"); throw Exception("Failed to reveal mesh $meshName");
@@ -1199,7 +1280,8 @@ class FilamentControllerFFI extends FilamentController {
} }
} }
var entityId = outPtr.value; var entityId = outPtr.value;
_pickResultController.add(entityId); _pickResultController
.add((entity: entityId, x: x.toDouble(), y: y.toDouble()));
allocator.free(outPtr); allocator.free(outPtr);
} }
@@ -1331,28 +1413,6 @@ class FilamentControllerFFI extends FilamentController {
return frustum; 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 @override
Future<FilamentEntity> getChildEntity( Future<FilamentEntity> getChildEntity(
FilamentEntity parent, String childName) async { FilamentEntity parent, String childName) async {

View File

@@ -183,11 +183,21 @@ external void clear_lights(
@ffi.Native< @ffi.Native<
EntityId Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Char>, 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( external int load_glb(
ffi.Pointer<ffi.Void> sceneManager, ffi.Pointer<ffi.Void> sceneManager,
ffi.Pointer<ffi.Char> assetPath, 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< @ffi.Native<
@@ -200,12 +210,42 @@ external int load_gltf(
ffi.Pointer<ffi.Char> relativePath, 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.Int Function(
ffi.Pointer<ffi.Void>, EntityId, ffi.Pointer<EntityId>)>(
symbol: 'get_instances', assetId: 'flutter_filament_plugin')
external int get_instances(
ffi.Pointer<ffi.Void> sceneManager,
int entityId,
ffi.Pointer<EntityId> out,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>)>( @ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>)>(
symbol: 'set_main_camera', assetId: 'flutter_filament_plugin') symbol: 'set_main_camera', assetId: 'flutter_filament_plugin')
external void set_main_camera( external void set_main_camera(
ffi.Pointer<ffi.Void> viewer, 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.Native<
ffi.Bool Function( ffi.Bool Function(
ffi.Pointer<ffi.Void>, EntityId, ffi.Pointer<ffi.Char>)>( 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') symbol: 'set_camera_rotation', assetId: 'flutter_filament_plugin')
external void set_camera_rotation( external void set_camera_rotation(
ffi.Pointer<ffi.Void> viewer, ffi.Pointer<ffi.Void> viewer,
double rads, double w,
double x, double x,
double y, double y,
double z, double z,
@@ -1140,11 +1180,22 @@ external void clear_lights_ffi(
@ffi.Native< @ffi.Native<
EntityId Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Char>, EntityId Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Char>,
ffi.Bool)>(symbol: 'load_glb_ffi', assetId: 'flutter_filament_plugin') ffi.Int)>(symbol: 'load_glb_ffi', assetId: 'flutter_filament_plugin')
external int load_glb_ffi( external int load_glb_ffi(
ffi.Pointer<ffi.Void> assetManager, ffi.Pointer<ffi.Void> sceneManager,
ffi.Pointer<ffi.Char> assetPath, ffi.Pointer<ffi.Char> assetPath,
bool unlit, int numInstances,
);
@ffi.Native<
EntityId Function(
ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>, ffi.Size, ffi.Int)>(
symbol: 'load_glb_from_buffer_ffi', assetId: 'flutter_filament_plugin')
external int load_glb_from_buffer_ffi(
ffi.Pointer<ffi.Void> sceneManager,
ffi.Pointer<ffi.Void> data,
int length,
int numInstances,
); );
@ffi.Native< @ffi.Native<
@@ -1152,11 +1203,18 @@ external int load_glb_ffi(
ffi.Pointer<ffi.Char>)>( ffi.Pointer<ffi.Char>)>(
symbol: 'load_gltf_ffi', assetId: 'flutter_filament_plugin') symbol: 'load_gltf_ffi', assetId: 'flutter_filament_plugin')
external int load_gltf_ffi( external int load_gltf_ffi(
ffi.Pointer<ffi.Void> assetManager, ffi.Pointer<ffi.Void> sceneManager,
ffi.Pointer<ffi.Char> assetPath, ffi.Pointer<ffi.Char> assetPath,
ffi.Pointer<ffi.Char> relativePath, ffi.Pointer<ffi.Char> relativePath,
); );
@ffi.Native<EntityId Function(ffi.Pointer<ffi.Void>, EntityId)>(
symbol: 'create_instance_ffi', assetId: 'flutter_filament_plugin')
external int create_instance_ffi(
ffi.Pointer<ffi.Void> sceneManager,
int entityId,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId)>( @ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId)>(
symbol: 'remove_entity_ffi', assetId: 'flutter_filament_plugin') symbol: 'remove_entity_ffi', assetId: 'flutter_filament_plugin')
external void remove_entity_ffi( external void remove_entity_ffi(
@@ -1185,7 +1243,7 @@ external bool set_camera_ffi(
ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Float>, ffi.Int)>( ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Float>, ffi.Int)>(
symbol: 'apply_weights_ffi', assetId: 'flutter_filament_plugin') symbol: 'apply_weights_ffi', assetId: 'flutter_filament_plugin')
external void apply_weights_ffi( external void apply_weights_ffi(
ffi.Pointer<ffi.Void> assetManager, ffi.Pointer<ffi.Void> sceneManager,
int asset, int asset,
ffi.Pointer<ffi.Char> entityName, ffi.Pointer<ffi.Char> entityName,
ffi.Pointer<ffi.Float> weights, ffi.Pointer<ffi.Float> weights,
@@ -1197,7 +1255,7 @@ external void apply_weights_ffi(
ffi.Bool, ffi.Bool, ffi.Float)>( ffi.Bool, ffi.Bool, ffi.Float)>(
symbol: 'play_animation_ffi', assetId: 'flutter_filament_plugin') symbol: 'play_animation_ffi', assetId: 'flutter_filament_plugin')
external void play_animation_ffi( external void play_animation_ffi(
ffi.Pointer<ffi.Void> assetManager, ffi.Pointer<ffi.Void> sceneManager,
int asset, int asset,
int index, int index,
bool loop, bool loop,
@@ -1210,7 +1268,7 @@ external void play_animation_ffi(
ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId, ffi.Int, ffi.Int)>( ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId, ffi.Int, ffi.Int)>(
symbol: 'set_animation_frame_ffi', assetId: 'flutter_filament_plugin') symbol: 'set_animation_frame_ffi', assetId: 'flutter_filament_plugin')
external void set_animation_frame_ffi( external void set_animation_frame_ffi(
ffi.Pointer<ffi.Void> assetManager, ffi.Pointer<ffi.Void> sceneManager,
int asset, int asset,
int animationIndex, int animationIndex,
int animationFrame, int animationFrame,
@@ -1219,7 +1277,7 @@ external void set_animation_frame_ffi(
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId, ffi.Int)>( @ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId, ffi.Int)>(
symbol: 'stop_animation_ffi', assetId: 'flutter_filament_plugin') symbol: 'stop_animation_ffi', assetId: 'flutter_filament_plugin')
external void stop_animation_ffi( external void stop_animation_ffi(
ffi.Pointer<ffi.Void> assetManager, ffi.Pointer<ffi.Void> sceneManager,
int asset, int asset,
int index, int index,
); );
@@ -1227,7 +1285,7 @@ external void stop_animation_ffi(
@ffi.Native<ffi.Int Function(ffi.Pointer<ffi.Void>, EntityId)>( @ffi.Native<ffi.Int Function(ffi.Pointer<ffi.Void>, EntityId)>(
symbol: 'get_animation_count_ffi', assetId: 'flutter_filament_plugin') symbol: 'get_animation_count_ffi', assetId: 'flutter_filament_plugin')
external int get_animation_count_ffi( external int get_animation_count_ffi(
ffi.Pointer<ffi.Void> assetManager, ffi.Pointer<ffi.Void> sceneManager,
int asset, int asset,
); );
@@ -1236,7 +1294,7 @@ external int get_animation_count_ffi(
ffi.Pointer<ffi.Void>, EntityId, ffi.Pointer<ffi.Char>, ffi.Int)>( ffi.Pointer<ffi.Void>, EntityId, ffi.Pointer<ffi.Char>, ffi.Int)>(
symbol: 'get_animation_name_ffi', assetId: 'flutter_filament_plugin') symbol: 'get_animation_name_ffi', assetId: 'flutter_filament_plugin')
external void get_animation_name_ffi( external void get_animation_name_ffi(
ffi.Pointer<ffi.Void> assetManager, ffi.Pointer<ffi.Void> sceneManager,
int asset, int asset,
ffi.Pointer<ffi.Char> outPtr, ffi.Pointer<ffi.Char> outPtr,
int index, int index,
@@ -1247,7 +1305,7 @@ external void get_animation_name_ffi(
ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>, ffi.Int)>( ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>, ffi.Int)>(
symbol: 'get_morph_target_name_ffi', assetId: 'flutter_filament_plugin') symbol: 'get_morph_target_name_ffi', assetId: 'flutter_filament_plugin')
external void get_morph_target_name_ffi( external void get_morph_target_name_ffi(
ffi.Pointer<ffi.Void> assetManager, ffi.Pointer<ffi.Void> sceneManager,
int asset, int asset,
ffi.Pointer<ffi.Char> meshName, ffi.Pointer<ffi.Char> meshName,
ffi.Pointer<ffi.Char> outPtr, ffi.Pointer<ffi.Char> outPtr,
@@ -1260,7 +1318,7 @@ external void get_morph_target_name_ffi(
symbol: 'get_morph_target_name_count_ffi', symbol: 'get_morph_target_name_count_ffi',
assetId: 'flutter_filament_plugin') assetId: 'flutter_filament_plugin')
external int get_morph_target_name_count_ffi( external int get_morph_target_name_count_ffi(
ffi.Pointer<ffi.Void> assetManager, ffi.Pointer<ffi.Void> sceneManager,
int asset, int asset,
ffi.Pointer<ffi.Char> meshName, ffi.Pointer<ffi.Char> meshName,
); );
@@ -1270,7 +1328,7 @@ external int get_morph_target_name_count_ffi(
ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Float>, ffi.Int)>( ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Float>, ffi.Int)>(
symbol: 'set_morph_target_weights_ffi', assetId: 'flutter_filament_plugin') symbol: 'set_morph_target_weights_ffi', assetId: 'flutter_filament_plugin')
external void set_morph_target_weights_ffi( external void set_morph_target_weights_ffi(
ffi.Pointer<ffi.Void> assetManager, ffi.Pointer<ffi.Void> sceneManager,
int asset, int asset,
ffi.Pointer<ffi.Char> entityName, ffi.Pointer<ffi.Char> entityName,
ffi.Pointer<ffi.Float> morphData, ffi.Pointer<ffi.Float> morphData,
@@ -1289,7 +1347,7 @@ external void set_morph_target_weights_ffi(
ffi.Float)>( ffi.Float)>(
symbol: 'set_morph_animation_ffi', assetId: 'flutter_filament_plugin') symbol: 'set_morph_animation_ffi', assetId: 'flutter_filament_plugin')
external bool set_morph_animation_ffi( external bool set_morph_animation_ffi(
ffi.Pointer<ffi.Void> assetManager, ffi.Pointer<ffi.Void> sceneManager,
int asset, int asset,
ffi.Pointer<ffi.Char> entityName, ffi.Pointer<ffi.Char> entityName,
ffi.Pointer<ffi.Float> morphData, ffi.Pointer<ffi.Float> morphData,
@@ -1308,7 +1366,7 @@ external bool set_morph_animation_ffi(
ffi.Pointer<ffi.Char>)>( ffi.Pointer<ffi.Char>)>(
symbol: 'set_bone_transform_ffi', assetId: 'flutter_filament_plugin') symbol: 'set_bone_transform_ffi', assetId: 'flutter_filament_plugin')
external bool set_bone_transform_ffi( external bool set_bone_transform_ffi(
ffi.Pointer<ffi.Void> assetManager, ffi.Pointer<ffi.Void> sceneManager,
int asset, int asset,
ffi.Pointer<ffi.Char> entityName, ffi.Pointer<ffi.Char> entityName,
ffi.Pointer<ffi.Float> transform, ffi.Pointer<ffi.Float> transform,
@@ -1328,7 +1386,7 @@ external bool set_bone_transform_ffi(
ffi.Bool)>( ffi.Bool)>(
symbol: 'add_bone_animation_ffi', assetId: 'flutter_filament_plugin') symbol: 'add_bone_animation_ffi', assetId: 'flutter_filament_plugin')
external void add_bone_animation_ffi( external void add_bone_animation_ffi(
ffi.Pointer<ffi.Void> assetManager, ffi.Pointer<ffi.Void> sceneManager,
int asset, int asset,
ffi.Pointer<ffi.Float> frameData, ffi.Pointer<ffi.Float> frameData,
int numFrames, int numFrames,
@@ -1360,7 +1418,7 @@ external void pick_ffi(
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId)>( @ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId)>(
symbol: 'reset_to_rest_pose_ffi', assetId: 'flutter_filament_plugin') symbol: 'reset_to_rest_pose_ffi', assetId: 'flutter_filament_plugin')
external void reset_to_rest_pose_ffi( external void reset_to_rest_pose_ffi(
ffi.Pointer<ffi.Void> assetManager, ffi.Pointer<ffi.Void> sceneManager,
int entityId, int entityId,
); );

View File

@@ -0,0 +1,42 @@
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_filament/entities/entity_transform_controller.dart';
import 'package:flutter_filament/filament_controller.dart';
class HardwareKeyboardPoll {
final EntityTransformController _controller;
late Timer _timer;
HardwareKeyboardPoll(this._controller) {
_timer = Timer.periodic(const Duration(milliseconds: 16), (_) {
print(RawKeyboard.instance.keysPressed);
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

@@ -1,16 +1,20 @@
import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_filament/camera/camera_orientation.dart';
import 'package:flutter_filament/filament_controller.dart'; import 'package:flutter_filament/filament_controller.dart';
import 'package:vector_math/vector_math_64.dart' as v; import 'dart:math';
import 'package:vector_math/vector_math_64.dart' as v64;
class CameraOptionsWidget extends StatefulWidget { class CameraOptionsWidget extends StatefulWidget {
final FilamentController controller; final FilamentController controller;
final CameraOrientation cameraOrientation;
final List<({FilamentEntity entity, String name})> cameras; final List<({FilamentEntity entity, String name})> cameras;
CameraOptionsWidget( CameraOptionsWidget(
{super.key, required this.controller, required this.cameras}) {} {super.key,
required this.controller,
required this.cameras,
required this.cameraOrientation}) {}
@override @override
State<StatefulWidget> createState() => _CameraOptionsWidgetState(); State<StatefulWidget> createState() => _CameraOptionsWidgetState();
@@ -45,6 +49,7 @@ class _CameraOptionsWidgetState extends State<CameraOptionsWidget> {
@override @override
void didUpdateWidget(CameraOptionsWidget oldWidget) { void didUpdateWidget(CameraOptionsWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.cameras.length != widget.cameras.length) { if (oldWidget.cameras.length != widget.cameras.length) {
setState(() {}); setState(() {});
} }
@@ -55,9 +60,20 @@ class _CameraOptionsWidgetState extends State<CameraOptionsWidget> {
double.parse(_apertureController.text), double.parse(_apertureController.text),
double.parse(_speedController.text), double.parse(_speedController.text),
double.parse(_sensitivityController.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(() {}); setState(() {});
} }
double _bloom = 0.0;
double _focalLength = 26.0;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Theme( return Theme(
@@ -81,6 +97,118 @@ class _CameraOptionsWidgetState extends State<CameraOptionsWidget> {
Expanded( Expanded(
child: TextField(controller: _sensitivityController)), 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( Wrap(
children: [ children: [
GestureDetector( GestureDetector(

View File

@@ -32,16 +32,22 @@ class FilamentGestureDetector extends StatelessWidget {
final bool showControlOverlay; final bool showControlOverlay;
/// ///
/// If false, all gestures will be ignored. /// If false, gestures will not manipulate the active camera.
/// ///
final bool enabled; final bool enableCamera;
///
/// If false, pointer down events will not trigger hit-testing (picking).
///
final bool enablePicking;
const FilamentGestureDetector( const FilamentGestureDetector(
{Key? key, {Key? key,
required this.controller, required this.controller,
this.child, this.child,
this.showControlOverlay = false, this.showControlOverlay = false,
this.enabled = true}) this.enableCamera = true,
this.enablePicking = true})
: super(key: key); : super(key: key);
@override @override
@@ -53,14 +59,16 @@ class FilamentGestureDetector extends StatelessWidget {
controller: controller, controller: controller,
child: child, child: child,
showControlOverlay: showControlOverlay, showControlOverlay: showControlOverlay,
listenerEnabled: enabled, enableCamera: enableCamera,
enablePicking: enablePicking,
); );
} else { } else {
return FilamentGestureDetectorMobile( return FilamentGestureDetectorMobile(
controller: controller, controller: controller,
child: child, child: child,
showControlOverlay: showControlOverlay, showControlOverlay: showControlOverlay,
listenerEnabled: enabled, enableCamera: enableCamera,
enablePicking: enablePicking,
); );
} }
} }

View File

@@ -28,16 +28,22 @@ class FilamentGestureDetectorDesktop extends StatefulWidget {
final bool showControlOverlay; 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( const FilamentGestureDetectorDesktop(
{Key? key, {Key? key,
required this.controller, required this.controller,
this.child, this.child,
this.showControlOverlay = false, this.showControlOverlay = false,
this.listenerEnabled = true}) this.enableCamera = true,
this.enablePicking = true})
: super(key: key); : super(key: key);
@override @override
@@ -57,7 +63,8 @@ class _FilamentGestureDetectorDesktopState
@override @override
void didUpdateWidget(FilamentGestureDetectorDesktop oldWidget) { void didUpdateWidget(FilamentGestureDetectorDesktop oldWidget) {
if (widget.showControlOverlay != oldWidget.showControlOverlay || if (widget.showControlOverlay != oldWidget.showControlOverlay ||
widget.listenerEnabled != oldWidget.listenerEnabled) { widget.enableCamera != oldWidget.enableCamera ||
widget.enablePicking != oldWidget.enablePicking) {
setState(() {}); setState(() {});
} }
@@ -86,12 +93,9 @@ class _FilamentGestureDetectorDesktopState
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (!widget.listenerEnabled) {
return widget.child ?? Container();
}
return Listener( return Listener(
onPointerSignal: (PointerSignalEvent pointerSignal) async { onPointerSignal: (PointerSignalEvent pointerSignal) async {
if (pointerSignal is PointerScrollEvent) { if (pointerSignal is PointerScrollEvent && widget.enableCamera) {
_zoom(pointerSignal); _zoom(pointerSignal);
} else { } else {
throw Exception("TODO"); throw Exception("TODO");
@@ -108,20 +112,20 @@ class _FilamentGestureDetectorDesktopState
onPointerMove: (PointerMoveEvent d) async { onPointerMove: (PointerMoveEvent d) async {
// if this is the first move event, we need to call rotateStart/panStart to set the first coordinates // if this is the first move event, we need to call rotateStart/panStart to set the first coordinates
if (!_pointerMoving) { if (!_pointerMoving) {
if (d.buttons == kTertiaryButton) { if (d.buttons == kTertiaryButton && widget.enableCamera) {
widget.controller widget.controller
.rotateStart(d.localPosition.dx, d.localPosition.dy); .rotateStart(d.localPosition.dx, d.localPosition.dy);
} else { } else if (widget.enableCamera) {
widget.controller widget.controller
.panStart(d.localPosition.dx, d.localPosition.dy); .panStart(d.localPosition.dx, d.localPosition.dy);
} }
} }
// set the _pointerMoving flag so we don't call rotateStart/panStart on future move events // set the _pointerMoving flag so we don't call rotateStart/panStart on future move events
_pointerMoving = true; _pointerMoving = true;
if (d.buttons == kTertiaryButton) { if (d.buttons == kTertiaryButton && widget.enableCamera) {
widget.controller widget.controller
.rotateUpdate(d.localPosition.dx, d.localPosition.dy); .rotateUpdate(d.localPosition.dx, d.localPosition.dy);
} else { } else if (widget.enableCamera) {
widget.controller.panUpdate(d.localPosition.dx, d.localPosition.dy); widget.controller.panUpdate(d.localPosition.dx, d.localPosition.dy);
} }
}, },
@@ -130,12 +134,12 @@ class _FilamentGestureDetectorDesktopState
// 2) if _pointerMoving is false, this is interpreted as a pick // 2) if _pointerMoving is false, this is interpreted as a pick
// same applies to middle mouse button, but this is ignored as a pick // same applies to middle mouse button, but this is ignored as a pick
onPointerUp: (PointerUpEvent d) async { onPointerUp: (PointerUpEvent d) async {
if (d.buttons == kTertiaryButton) { if (d.buttons == kTertiaryButton && widget.enableCamera) {
widget.controller.rotateEnd(); widget.controller.rotateEnd();
} else { } else {
if (_pointerMoving) { if (_pointerMoving && widget.enableCamera) {
widget.controller.panEnd(); widget.controller.panEnd();
} else { } else if (widget.enablePicking) {
widget.controller widget.controller
.pick(d.localPosition.dx.toInt(), d.localPosition.dy.toInt()); .pick(d.localPosition.dx.toInt(), d.localPosition.dy.toInt());
} }

View File

@@ -28,9 +28,14 @@ class FilamentGestureDetectorMobile extends StatefulWidget {
final bool showControlOverlay; 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 double zoomDelta;
@@ -39,7 +44,8 @@ class FilamentGestureDetectorMobile extends StatefulWidget {
required this.controller, required this.controller,
this.child, this.child,
this.showControlOverlay = false, this.showControlOverlay = false,
this.listenerEnabled = true, this.enableCamera = true,
this.enablePicking = true,
this.zoomDelta = 1}) this.zoomDelta = 1})
: super(key: key); : super(key: key);
@@ -105,7 +111,8 @@ class _FilamentGestureDetectorMobileState
@override @override
void didUpdateWidget(FilamentGestureDetectorMobile oldWidget) { void didUpdateWidget(FilamentGestureDetectorMobile oldWidget) {
if (widget.showControlOverlay != oldWidget.showControlOverlay || if (widget.showControlOverlay != oldWidget.showControlOverlay ||
widget.listenerEnabled != oldWidget.listenerEnabled) { widget.enableCamera != oldWidget.enableCamera ||
widget.enablePicking != oldWidget.enablePicking) {
setState(() {}); setState(() {});
} }
@@ -122,10 +129,6 @@ class _FilamentGestureDetectorMobileState
// - inner is a Listener for all other gestures (including scroll zoom on desktop) // - inner is a Listener for all other gestures (including scroll zoom on desktop)
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (!widget.listenerEnabled) {
return widget.child ?? Container();
}
return Stack(children: [ return Stack(children: [
Positioned.fill( Positioned.fill(
child: GestureDetector( child: GestureDetector(
@@ -136,10 +139,10 @@ class _FilamentGestureDetectorMobileState
}); });
}, },
onScaleStart: (d) async { onScaleStart: (d) async {
if (d.pointerCount == 2) { if (d.pointerCount == 2 && widget.enableCamera) {
_scaling = true; _scaling = true;
await widget.controller.zoomBegin(); await widget.controller.zoomBegin();
} else if (!_scaling) { } else if (!_scaling && widget.enableCamera) {
if (_rotateOnPointerMove) { if (_rotateOnPointerMove) {
widget.controller.rotateStart( widget.controller.rotateStart(
d.localFocalPoint.dx, d.localFocalPoint.dy); d.localFocalPoint.dx, d.localFocalPoint.dy);
@@ -150,7 +153,7 @@ class _FilamentGestureDetectorMobileState
} }
}, },
onScaleUpdate: (ScaleUpdateDetails d) async { onScaleUpdate: (ScaleUpdateDetails d) async {
if (d.pointerCount == 2) { if (d.pointerCount == 2 && widget.enableCamera) {
if (d.horizontalScale != _lastScale) { if (d.horizontalScale != _lastScale) {
widget.controller.zoomUpdate( widget.controller.zoomUpdate(
d.localFocalPoint.dx, d.localFocalPoint.dx,
@@ -158,7 +161,7 @@ class _FilamentGestureDetectorMobileState
d.horizontalScale > _lastScale ? 0.1 : -0.1); d.horizontalScale > _lastScale ? 0.1 : -0.1);
_lastScale = d.horizontalScale; _lastScale = d.horizontalScale;
} }
} else if (!_scaling) { } else if (!_scaling && widget.enableCamera) {
if (_rotateOnPointerMove) { if (_rotateOnPointerMove) {
widget.controller widget.controller
.rotateUpdate(d.focalPoint.dx, d.focalPoint.dy); .rotateUpdate(d.focalPoint.dx, d.focalPoint.dy);
@@ -169,9 +172,9 @@ class _FilamentGestureDetectorMobileState
} }
}, },
onScaleEnd: (d) async { onScaleEnd: (d) async {
if (d.pointerCount == 2) { if (d.pointerCount == 2 && widget.enableCamera) {
widget.controller.zoomEnd(); widget.controller.zoomEnd();
} else if (!_scaling) { } else if (!_scaling && widget.enableCamera) {
if (_rotateOnPointerMove) { if (_rotateOnPointerMove) {
widget.controller.rotateEnd(); widget.controller.rotateEnd();
} else { } else {

View File

@@ -3,72 +3,52 @@ import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_filament/filament_controller.dart'; import 'package:flutter_filament/filament_controller.dart';
import 'package:flutter_filament/lights/light_options.dart';
import 'package:vector_math/vector_math_64.dart' as v; import 'package:vector_math/vector_math_64.dart' as v;
class LightSliderWidget extends StatefulWidget { class LightSliderWidget extends StatefulWidget {
final FilamentController controller; final FilamentController controller;
late final v.Vector3 initialPosition; final LightOptions options;
late final v.Vector3 initialDirection;
final int initialType;
final double initialColor;
final double initialIntensity;
final bool initialCastShadows;
final bool showControls; final bool showControls;
LightSliderWidget( LightSliderWidget(
{super.key, {super.key,
required this.controller, required this.controller,
this.initialType = 0,
this.initialColor = 6500,
this.initialIntensity = 100000,
this.initialCastShadows = true,
this.showControls = false, this.showControls = false,
v.Vector3? initialDirection, required this.options});
v.Vector3? initialPosition}) {
this.initialDirection = initialDirection ?? v.Vector3(0, 0.5, -1);
this.initialPosition = initialPosition ?? v.Vector3(0, 0.5, 1);
}
@override @override
State<StatefulWidget> createState() => _IblRotationSliderWidgetState(); State<StatefulWidget> createState() => _LightSliderWidgetState();
} }
class _IblRotationSliderWidgetState extends State<LightSliderWidget> { class _LightSliderWidgetState 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; FilamentEntity? _light;
@override @override
void initState() { void initState() {
type = widget.initialType;
castShadows = widget.initialCastShadows;
color = widget.initialColor;
lightPos = widget.initialPosition;
lightDir = widget.initialDirection;
intensity = widget.initialIntensity;
_set(); _set();
super.initState(); super.initState();
} }
Future _set() async { Future _set() async {
if (_light != null) await widget.controller.removeLight(_light!); 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( _light = await widget.controller.addLight(
type, widget.options.directionalType,
color, widget.options.directionalColor,
intensity, widget.options.directionalIntensity,
lightPos.x, widget.options.directionalPosition.x,
lightPos.y, widget.options.directionalPosition.y,
lightPos.z, widget.options.directionalPosition.z,
lightDir.x, widget.options.directionalDirection.x,
lightDir.y, widget.options.directionalDirection.y,
lightDir.z, widget.options.directionalDirection.z,
castShadows); widget.options.directionalCastShadows);
setState(() {}); setState(() {});
} }
@@ -87,35 +67,39 @@ class _IblRotationSliderWidgetState extends State<LightSliderWidget> {
showValueIndicator: ShowValueIndicator.always, showValueIndicator: ShowValueIndicator.always,
valueIndicatorTextStyle: TextStyle(color: Colors.black)), valueIndicatorTextStyle: TextStyle(color: Colors.black)),
child: Column(mainAxisSize: MainAxisSize.min, children: [ child: Column(mainAxisSize: MainAxisSize.min, children: [
Text("Directional"),
Row(children: [ Row(children: [
Expanded( Expanded(
child: Slider( child: Slider(
label: "POSX", label:
value: lightPos.x, "POSX ${widget.options.directionalPosition.x}",
value: widget.options.directionalPosition.x,
min: -10.0, min: -10.0,
max: 10.0, max: 10.0,
onChanged: (value) { onChanged: (value) {
lightPos.x = value; widget.options.directionalPosition.x = value;
_set(); _set();
})), })),
Expanded( Expanded(
child: Slider( child: Slider(
label: "POSY", label:
value: lightPos.y, "POSY ${widget.options.directionalPosition.y}",
min: -10.0, value: widget.options.directionalPosition.y,
max: 10.0, min: -100.0,
max: 100.0,
onChanged: (value) { onChanged: (value) {
lightPos.y = value; widget.options.directionalPosition.y = value;
_set(); _set();
})), })),
Expanded( Expanded(
child: Slider( child: Slider(
label: "POSZ", label:
value: lightPos.z, "POSZ ${widget.options.directionalPosition.z}",
min: -10.0, value: widget.options.directionalPosition.z,
max: 10.0, min: -100.0,
max: 100.0,
onChanged: (value) { onChanged: (value) {
lightPos.z = value; widget.options.directionalPosition.z = value;
_set(); _set();
})) }))
]), ]),
@@ -123,58 +107,58 @@ class _IblRotationSliderWidgetState extends State<LightSliderWidget> {
Expanded( Expanded(
child: Slider( child: Slider(
label: "DIRX", label: "DIRX",
value: lightDir.x, value: widget.options.directionalDirection.x,
min: -10.0, min: -1.0,
max: 10.0, max: 1.0,
onChanged: (value) { onChanged: (value) {
lightDir.x = value; widget.options.directionalDirection.x = value;
_set(); _set();
})), })),
Expanded( Expanded(
child: Slider( child: Slider(
label: "DIRY", label: "DIRY",
value: lightDir.y, value: widget.options.directionalDirection.y,
min: -10.0, min: -1.0,
max: 10.0, max: 1.0,
onChanged: (value) { onChanged: (value) {
lightDir.y = value; widget.options.directionalDirection.y = value;
_set(); _set();
})), })),
Expanded( Expanded(
child: Slider( child: Slider(
label: "DIRZ", label: "DIRZ",
value: lightDir.z, value: widget.options.directionalDirection.z,
min: -10.0, min: -1.0,
max: 10.0, max: 1.0,
onChanged: (value) { onChanged: (value) {
lightDir.z = value; widget.options.directionalDirection.z = value;
_set(); _set();
})) }))
]), ]),
Slider( Slider(
label: "Color", label: "Color",
value: color, value: widget.options.directionalColor,
min: 0, min: 0,
max: 16000, max: 16000,
onChanged: (value) { onChanged: (value) {
color = value; widget.options.directionalColor = value;
_set(); _set();
}), }),
Slider( Slider(
label: "Intensity", label: "Intensity ${widget.options.directionalIntensity}",
value: intensity, value: widget.options.directionalIntensity,
min: 0, min: 0,
max: 1000000, max: 1000000,
onChanged: (value) { onChanged: (value) {
intensity = value; widget.options.directionalIntensity = value;
_set(); _set();
}), }),
DropdownButton( DropdownButton(
onChanged: (v) { onChanged: (v) {
this.type = v; this.widget.options.directionalType = v;
_set(); _set();
}, },
value: type, value: this.widget.options.directionalType,
items: List<DropdownMenuItem>.generate( items: List<DropdownMenuItem>.generate(
5, 5,
(idx) => DropdownMenuItem( (idx) => DropdownMenuItem(
@@ -182,13 +166,27 @@ class _IblRotationSliderWidgetState extends State<LightSliderWidget> {
child: Text("$idx"), child: Text("$idx"),
))), ))),
Row(children: [ Row(children: [
Text("Shadows: $castShadows"), Text(
"Shadows: ${this.widget.options.directionalCastShadows}"),
Checkbox( Checkbox(
value: castShadows, value: widget.options.directionalCastShadows,
onChanged: (v) { onChanged: (v) {
this.castShadows = v!; this.widget.options.directionalCastShadows = v!;
_set(); _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();
})),
]) ])
])))); ]))));
} }

View File

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

View File

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

View File

@@ -14,7 +14,7 @@ A new Flutter plugin project.
s.author = { 'Your Company' => 'email@example.com' } s.author = { 'Your Company' => 'email@example.com' }
s.source = { :path => '.' } 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.public_header_files = 'include/SwiftFlutterFilamentPlugin-Bridging-Header.h', 'include/FlutterFilamentApi.h', 'include/FlutterFilamentFFIApi.h', 'include/ResourceBuffer.hpp' #, 'include/filament/*'
s.dependency 'FlutterMacOS' s.dependency 'FlutterMacOS'