work
This commit is contained in:
15
lib/camera/camera_orientation.dart
Normal file
15
lib/camera/camera_orientation.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// ignore_for_file: constant_identifier_names
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui' as ui;
|
||||
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
|
||||
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 }
|
||||
|
||||
// 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].
|
||||
/// If [pick] is called without an active subscription to this stream, the results will be silently discarded.
|
||||
///
|
||||
Stream<FilamentEntity?> get pickResult;
|
||||
Stream<FilamentPickResult> get pickResult;
|
||||
|
||||
///
|
||||
/// Whether the controller is currently rendering at [framerate].
|
||||
@@ -206,7 +210,35 @@ abstract class FilamentController {
|
||||
///
|
||||
/// Load the .glb asset at the given path and insert into the scene.
|
||||
///
|
||||
Future<FilamentEntity> loadGlb(String path, {bool unlit = false});
|
||||
Future<FilamentEntity> loadGlb(String path, {int numInstances = 1});
|
||||
|
||||
///
|
||||
/// 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.
|
||||
@@ -281,16 +313,11 @@ abstract class FilamentController {
|
||||
Future resetBones(FilamentEntity entity);
|
||||
|
||||
///
|
||||
/// Starts animating a bone (joint) according to the specified [animation].
|
||||
/// Transforms the bone(s)/joint(s) according [animation].
|
||||
/// To set the instantaneous transform, just use a single frame.
|
||||
///
|
||||
Future addBoneAnimation(FilamentEntity entity, BoneAnimationData animation);
|
||||
|
||||
///
|
||||
/// Sets the local joint transform for the bone at the given index in [entity] for the mesh under [meshName].
|
||||
///
|
||||
Future setBoneTransform(
|
||||
FilamentEntity entity, String meshName, String boneName, Matrix4 data);
|
||||
|
||||
///
|
||||
/// Removes/destroys the specified entity from the scene.
|
||||
/// [entity] will no longer be a valid handle after this method is called; ensure you immediately discard all references once this method is complete.
|
||||
@@ -350,6 +377,11 @@ abstract class FilamentController {
|
||||
///
|
||||
Future setMainCamera();
|
||||
|
||||
///
|
||||
/// Returns the entity associated with the main camera.
|
||||
///
|
||||
Future<FilamentEntity> getMainCamera();
|
||||
|
||||
///
|
||||
/// Sets the current scene camera to the glTF camera under [name] in [entity].
|
||||
///
|
||||
@@ -450,7 +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.
|
||||
///
|
||||
Future setCameraRotation(double rads, double x, double y, double z);
|
||||
Future setCameraRotation(Quaternion quaternion);
|
||||
|
||||
///
|
||||
/// Sets the camera model matrix.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui' as ui;
|
||||
import 'dart:developer' as dev;
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -52,8 +53,9 @@ class FilamentControllerFFI extends FilamentController {
|
||||
final hasViewer = ValueNotifier<bool>(false);
|
||||
|
||||
@override
|
||||
Stream<FilamentEntity> get pickResult => _pickResultController.stream;
|
||||
final _pickResultController = StreamController<FilamentEntity>.broadcast();
|
||||
Stream<FilamentPickResult> get pickResult => _pickResultController.stream;
|
||||
final _pickResultController =
|
||||
StreamController<FilamentPickResult>.broadcast();
|
||||
|
||||
int? _resizingWidth;
|
||||
int? _resizingHeight;
|
||||
@@ -545,8 +547,82 @@ class FilamentControllerFFI extends FilamentController {
|
||||
_lights.clear();
|
||||
}
|
||||
|
||||
final _assetCache = <String, (Pointer<Void>, int)>{};
|
||||
|
||||
@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) {
|
||||
throw Exception("No viewer available, ignoring");
|
||||
}
|
||||
@@ -554,7 +630,7 @@ class FilamentControllerFFI extends FilamentController {
|
||||
throw Exception("Not yet implemented");
|
||||
}
|
||||
final pathPtr = path.toNativeUtf8().cast<Char>();
|
||||
var entity = load_glb_ffi(_sceneManager!, pathPtr, unlit);
|
||||
var entity = load_glb_ffi(_sceneManager!, pathPtr, numInstances);
|
||||
allocator.free(pathPtr);
|
||||
if (entity == _FILAMENT_ASSET_ERROR) {
|
||||
throw Exception("An error occurred loading the asset at $path");
|
||||
@@ -910,6 +986,10 @@ class FilamentControllerFFI extends FilamentController {
|
||||
set_main_camera(_viewer!);
|
||||
}
|
||||
|
||||
Future<FilamentEntity> getMainCamera() async {
|
||||
return get_main_camera(_viewer!);
|
||||
}
|
||||
|
||||
@override
|
||||
Future setCamera(FilamentEntity entity, String? name) async {
|
||||
if (_viewer == null) {
|
||||
@@ -1027,11 +1107,12 @@ class FilamentControllerFFI extends FilamentController {
|
||||
}
|
||||
|
||||
@override
|
||||
Future setCameraRotation(double rads, double x, double y, double z) async {
|
||||
Future setCameraRotation(Quaternion quaternion) async {
|
||||
if (_viewer == null) {
|
||||
throw Exception("No viewer available, ignoring");
|
||||
}
|
||||
set_camera_rotation(_viewer!, rads, x, y, z);
|
||||
set_camera_rotation(
|
||||
_viewer!, quaternion.w, quaternion.x, quaternion.y, quaternion.z);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -1158,13 +1239,13 @@ class FilamentControllerFFI extends FilamentController {
|
||||
}
|
||||
|
||||
@override
|
||||
Future reveal(FilamentEntity entity, String meshName) async {
|
||||
Future reveal(FilamentEntity entity, String? meshName) async {
|
||||
if (_viewer == null) {
|
||||
throw Exception("No viewer available, ignoring");
|
||||
}
|
||||
|
||||
final meshNamePtr = meshName.toNativeUtf8().cast<Char>();
|
||||
final result = reveal_mesh(_sceneManager!, entity, meshNamePtr) != 1;
|
||||
final meshNamePtr = meshName?.toNativeUtf8().cast<Char>() ?? nullptr;
|
||||
final result = reveal_mesh(_sceneManager!, entity, meshNamePtr) == 1;
|
||||
allocator.free(meshNamePtr);
|
||||
if (!result) {
|
||||
throw Exception("Failed to reveal mesh $meshName");
|
||||
@@ -1199,7 +1280,8 @@ class FilamentControllerFFI extends FilamentController {
|
||||
}
|
||||
}
|
||||
var entityId = outPtr.value;
|
||||
_pickResultController.add(entityId);
|
||||
_pickResultController
|
||||
.add((entity: entityId, x: x.toDouble(), y: y.toDouble()));
|
||||
allocator.free(outPtr);
|
||||
}
|
||||
|
||||
@@ -1331,28 +1413,6 @@ class FilamentControllerFFI extends FilamentController {
|
||||
return frustum;
|
||||
}
|
||||
|
||||
@override
|
||||
Future setBoneTransform(FilamentEntity entity, String meshName,
|
||||
String boneName, Matrix4 data) async {
|
||||
var ptr = allocator<Float>(16);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
ptr.elementAt(i).value = data.storage[i];
|
||||
}
|
||||
|
||||
var meshNamePtr = meshName.toNativeUtf8(allocator: allocator).cast<Char>();
|
||||
var boneNamePtr = boneName.toNativeUtf8(allocator: allocator).cast<Char>();
|
||||
|
||||
var result = set_bone_transform_ffi(
|
||||
_sceneManager!, entity, meshNamePtr, ptr, boneNamePtr);
|
||||
|
||||
allocator.free(ptr);
|
||||
allocator.free(meshNamePtr);
|
||||
allocator.free(boneNamePtr);
|
||||
if (!result) {
|
||||
throw Exception("Failed to set bone transform. See logs for details");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<FilamentEntity> getChildEntity(
|
||||
FilamentEntity parent, String childName) async {
|
||||
|
||||
@@ -183,11 +183,21 @@ external void clear_lights(
|
||||
|
||||
@ffi.Native<
|
||||
EntityId Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Char>,
|
||||
ffi.Bool)>(symbol: 'load_glb', assetId: 'flutter_filament_plugin')
|
||||
ffi.Int)>(symbol: 'load_glb', assetId: 'flutter_filament_plugin')
|
||||
external int load_glb(
|
||||
ffi.Pointer<ffi.Void> sceneManager,
|
||||
ffi.Pointer<ffi.Char> assetPath,
|
||||
bool unlit,
|
||||
int numInstances,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
EntityId Function(
|
||||
ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>, ffi.Size)>(
|
||||
symbol: 'load_glb_from_buffer', assetId: 'flutter_filament_plugin')
|
||||
external int load_glb_from_buffer(
|
||||
ffi.Pointer<ffi.Void> sceneManager,
|
||||
ffi.Pointer<ffi.Void> data,
|
||||
int length,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
@@ -200,12 +210,42 @@ external int load_gltf(
|
||||
ffi.Pointer<ffi.Char> relativePath,
|
||||
);
|
||||
|
||||
@ffi.Native<EntityId Function(ffi.Pointer<ffi.Void>, EntityId)>(
|
||||
symbol: 'create_instance', assetId: 'flutter_filament_plugin')
|
||||
external int create_instance(
|
||||
ffi.Pointer<ffi.Void> sceneManager,
|
||||
int id,
|
||||
);
|
||||
|
||||
@ffi.Native<ffi.Int Function(ffi.Pointer<ffi.Void>, EntityId)>(
|
||||
symbol: 'get_instance_count', assetId: 'flutter_filament_plugin')
|
||||
external int get_instance_count(
|
||||
ffi.Pointer<ffi.Void> sceneManager,
|
||||
int entityId,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
ffi.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>)>(
|
||||
symbol: 'set_main_camera', assetId: 'flutter_filament_plugin')
|
||||
external void set_main_camera(
|
||||
ffi.Pointer<ffi.Void> viewer,
|
||||
);
|
||||
|
||||
@ffi.Native<EntityId Function(ffi.Pointer<ffi.Void>)>(
|
||||
symbol: 'get_main_camera', assetId: 'flutter_filament_plugin')
|
||||
external int get_main_camera(
|
||||
ffi.Pointer<ffi.Void> viewer,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
ffi.Bool Function(
|
||||
ffi.Pointer<ffi.Void>, EntityId, ffi.Pointer<ffi.Char>)>(
|
||||
@@ -653,7 +693,7 @@ external void get_camera_position(
|
||||
symbol: 'set_camera_rotation', assetId: 'flutter_filament_plugin')
|
||||
external void set_camera_rotation(
|
||||
ffi.Pointer<ffi.Void> viewer,
|
||||
double rads,
|
||||
double w,
|
||||
double x,
|
||||
double y,
|
||||
double z,
|
||||
@@ -1140,11 +1180,22 @@ external void clear_lights_ffi(
|
||||
|
||||
@ffi.Native<
|
||||
EntityId Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Char>,
|
||||
ffi.Bool)>(symbol: 'load_glb_ffi', assetId: 'flutter_filament_plugin')
|
||||
ffi.Int)>(symbol: 'load_glb_ffi', assetId: 'flutter_filament_plugin')
|
||||
external int load_glb_ffi(
|
||||
ffi.Pointer<ffi.Void> assetManager,
|
||||
ffi.Pointer<ffi.Void> sceneManager,
|
||||
ffi.Pointer<ffi.Char> assetPath,
|
||||
bool unlit,
|
||||
int numInstances,
|
||||
);
|
||||
|
||||
@ffi.Native<
|
||||
EntityId Function(
|
||||
ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>, ffi.Size, 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<
|
||||
@@ -1152,11 +1203,18 @@ external int load_glb_ffi(
|
||||
ffi.Pointer<ffi.Char>)>(
|
||||
symbol: 'load_gltf_ffi', assetId: 'flutter_filament_plugin')
|
||||
external int load_gltf_ffi(
|
||||
ffi.Pointer<ffi.Void> assetManager,
|
||||
ffi.Pointer<ffi.Void> sceneManager,
|
||||
ffi.Pointer<ffi.Char> assetPath,
|
||||
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)>(
|
||||
symbol: 'remove_entity_ffi', assetId: 'flutter_filament_plugin')
|
||||
external void remove_entity_ffi(
|
||||
@@ -1185,7 +1243,7 @@ external bool set_camera_ffi(
|
||||
ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Float>, ffi.Int)>(
|
||||
symbol: 'apply_weights_ffi', assetId: 'flutter_filament_plugin')
|
||||
external void apply_weights_ffi(
|
||||
ffi.Pointer<ffi.Void> assetManager,
|
||||
ffi.Pointer<ffi.Void> sceneManager,
|
||||
int asset,
|
||||
ffi.Pointer<ffi.Char> entityName,
|
||||
ffi.Pointer<ffi.Float> weights,
|
||||
@@ -1197,7 +1255,7 @@ external void apply_weights_ffi(
|
||||
ffi.Bool, ffi.Bool, ffi.Float)>(
|
||||
symbol: 'play_animation_ffi', assetId: 'flutter_filament_plugin')
|
||||
external void play_animation_ffi(
|
||||
ffi.Pointer<ffi.Void> assetManager,
|
||||
ffi.Pointer<ffi.Void> sceneManager,
|
||||
int asset,
|
||||
int index,
|
||||
bool loop,
|
||||
@@ -1210,7 +1268,7 @@ external void play_animation_ffi(
|
||||
ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId, ffi.Int, ffi.Int)>(
|
||||
symbol: 'set_animation_frame_ffi', assetId: 'flutter_filament_plugin')
|
||||
external void set_animation_frame_ffi(
|
||||
ffi.Pointer<ffi.Void> assetManager,
|
||||
ffi.Pointer<ffi.Void> sceneManager,
|
||||
int asset,
|
||||
int animationIndex,
|
||||
int animationFrame,
|
||||
@@ -1219,7 +1277,7 @@ external void set_animation_frame_ffi(
|
||||
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId, ffi.Int)>(
|
||||
symbol: 'stop_animation_ffi', assetId: 'flutter_filament_plugin')
|
||||
external void stop_animation_ffi(
|
||||
ffi.Pointer<ffi.Void> assetManager,
|
||||
ffi.Pointer<ffi.Void> sceneManager,
|
||||
int asset,
|
||||
int index,
|
||||
);
|
||||
@@ -1227,7 +1285,7 @@ external void stop_animation_ffi(
|
||||
@ffi.Native<ffi.Int Function(ffi.Pointer<ffi.Void>, EntityId)>(
|
||||
symbol: 'get_animation_count_ffi', assetId: 'flutter_filament_plugin')
|
||||
external int get_animation_count_ffi(
|
||||
ffi.Pointer<ffi.Void> assetManager,
|
||||
ffi.Pointer<ffi.Void> sceneManager,
|
||||
int asset,
|
||||
);
|
||||
|
||||
@@ -1236,7 +1294,7 @@ external int get_animation_count_ffi(
|
||||
ffi.Pointer<ffi.Void>, EntityId, ffi.Pointer<ffi.Char>, ffi.Int)>(
|
||||
symbol: 'get_animation_name_ffi', assetId: 'flutter_filament_plugin')
|
||||
external void get_animation_name_ffi(
|
||||
ffi.Pointer<ffi.Void> assetManager,
|
||||
ffi.Pointer<ffi.Void> sceneManager,
|
||||
int asset,
|
||||
ffi.Pointer<ffi.Char> outPtr,
|
||||
int index,
|
||||
@@ -1247,7 +1305,7 @@ external void get_animation_name_ffi(
|
||||
ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>, ffi.Int)>(
|
||||
symbol: 'get_morph_target_name_ffi', assetId: 'flutter_filament_plugin')
|
||||
external void get_morph_target_name_ffi(
|
||||
ffi.Pointer<ffi.Void> assetManager,
|
||||
ffi.Pointer<ffi.Void> sceneManager,
|
||||
int asset,
|
||||
ffi.Pointer<ffi.Char> meshName,
|
||||
ffi.Pointer<ffi.Char> outPtr,
|
||||
@@ -1260,7 +1318,7 @@ external void get_morph_target_name_ffi(
|
||||
symbol: 'get_morph_target_name_count_ffi',
|
||||
assetId: 'flutter_filament_plugin')
|
||||
external int get_morph_target_name_count_ffi(
|
||||
ffi.Pointer<ffi.Void> assetManager,
|
||||
ffi.Pointer<ffi.Void> sceneManager,
|
||||
int asset,
|
||||
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)>(
|
||||
symbol: 'set_morph_target_weights_ffi', assetId: 'flutter_filament_plugin')
|
||||
external void set_morph_target_weights_ffi(
|
||||
ffi.Pointer<ffi.Void> assetManager,
|
||||
ffi.Pointer<ffi.Void> sceneManager,
|
||||
int asset,
|
||||
ffi.Pointer<ffi.Char> entityName,
|
||||
ffi.Pointer<ffi.Float> morphData,
|
||||
@@ -1289,7 +1347,7 @@ external void set_morph_target_weights_ffi(
|
||||
ffi.Float)>(
|
||||
symbol: 'set_morph_animation_ffi', assetId: 'flutter_filament_plugin')
|
||||
external bool set_morph_animation_ffi(
|
||||
ffi.Pointer<ffi.Void> assetManager,
|
||||
ffi.Pointer<ffi.Void> sceneManager,
|
||||
int asset,
|
||||
ffi.Pointer<ffi.Char> entityName,
|
||||
ffi.Pointer<ffi.Float> morphData,
|
||||
@@ -1308,7 +1366,7 @@ external bool set_morph_animation_ffi(
|
||||
ffi.Pointer<ffi.Char>)>(
|
||||
symbol: 'set_bone_transform_ffi', assetId: 'flutter_filament_plugin')
|
||||
external bool set_bone_transform_ffi(
|
||||
ffi.Pointer<ffi.Void> assetManager,
|
||||
ffi.Pointer<ffi.Void> sceneManager,
|
||||
int asset,
|
||||
ffi.Pointer<ffi.Char> entityName,
|
||||
ffi.Pointer<ffi.Float> transform,
|
||||
@@ -1328,7 +1386,7 @@ external bool set_bone_transform_ffi(
|
||||
ffi.Bool)>(
|
||||
symbol: 'add_bone_animation_ffi', assetId: 'flutter_filament_plugin')
|
||||
external void add_bone_animation_ffi(
|
||||
ffi.Pointer<ffi.Void> assetManager,
|
||||
ffi.Pointer<ffi.Void> sceneManager,
|
||||
int asset,
|
||||
ffi.Pointer<ffi.Float> frameData,
|
||||
int numFrames,
|
||||
@@ -1360,7 +1418,7 @@ external void pick_ffi(
|
||||
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId)>(
|
||||
symbol: 'reset_to_rest_pose_ffi', assetId: 'flutter_filament_plugin')
|
||||
external void reset_to_rest_pose_ffi(
|
||||
ffi.Pointer<ffi.Void> assetManager,
|
||||
ffi.Pointer<ffi.Void> sceneManager,
|
||||
int entityId,
|
||||
);
|
||||
|
||||
|
||||
42
lib/hardware/hardware_keyboard_poll.dart
Normal file
42
lib/hardware/hardware_keyboard_poll.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
29
lib/lights/light_options.dart
Normal file
29
lib/lights/light_options.dart
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,20 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_filament/camera/camera_orientation.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 {
|
||||
final FilamentController controller;
|
||||
final CameraOrientation cameraOrientation;
|
||||
final List<({FilamentEntity entity, String name})> cameras;
|
||||
|
||||
CameraOptionsWidget(
|
||||
{super.key, required this.controller, required this.cameras}) {}
|
||||
{super.key,
|
||||
required this.controller,
|
||||
required this.cameras,
|
||||
required this.cameraOrientation}) {}
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _CameraOptionsWidgetState();
|
||||
@@ -45,6 +49,7 @@ class _CameraOptionsWidgetState extends State<CameraOptionsWidget> {
|
||||
|
||||
@override
|
||||
void didUpdateWidget(CameraOptionsWidget oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.cameras.length != widget.cameras.length) {
|
||||
setState(() {});
|
||||
}
|
||||
@@ -55,9 +60,20 @@ class _CameraOptionsWidgetState extends State<CameraOptionsWidget> {
|
||||
double.parse(_apertureController.text),
|
||||
double.parse(_speedController.text),
|
||||
double.parse(_sensitivityController.text));
|
||||
await widget.controller.setCameraPosition(
|
||||
widget.cameraOrientation.position.x,
|
||||
widget.cameraOrientation.position.y,
|
||||
widget.cameraOrientation.position.z);
|
||||
var rotation = widget.cameraOrientation.compose();
|
||||
await widget.controller.setCameraRotation(rotation);
|
||||
print(
|
||||
"Camera : ${widget.cameraOrientation.position} ${widget.cameraOrientation.rotationX} ${widget.cameraOrientation.rotationY} ${widget.cameraOrientation.rotationZ}");
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
double _bloom = 0.0;
|
||||
|
||||
double _focalLength = 26.0;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Theme(
|
||||
@@ -81,6 +97,118 @@ class _CameraOptionsWidgetState extends State<CameraOptionsWidget> {
|
||||
Expanded(
|
||||
child: TextField(controller: _sensitivityController)),
|
||||
]),
|
||||
Row(children: [
|
||||
Text("Bloom: ${_bloom.toStringAsFixed(2)}"),
|
||||
Slider(
|
||||
value: _bloom,
|
||||
min: 0.0,
|
||||
max: 1.0,
|
||||
onChanged: (v) async {
|
||||
setState(() {
|
||||
_bloom = v;
|
||||
});
|
||||
await widget.controller.setBloom(_bloom);
|
||||
})
|
||||
]),
|
||||
Row(children: [
|
||||
Text("Focal length"),
|
||||
Slider(
|
||||
label: _focalLength.toString(),
|
||||
value: _focalLength,
|
||||
min: 1.0,
|
||||
max: 100.0,
|
||||
onChanged: (v) async {
|
||||
setState(() {
|
||||
_focalLength = v;
|
||||
});
|
||||
await widget.controller
|
||||
.setCameraFocalLength(_focalLength);
|
||||
})
|
||||
]),
|
||||
Row(children: [
|
||||
Text("X"),
|
||||
Slider(
|
||||
label: widget.cameraOrientation.position.x.toString(),
|
||||
value: widget.cameraOrientation.position.x,
|
||||
min: -100.0,
|
||||
max: 100.0,
|
||||
onChanged: (v) async {
|
||||
setState(() {
|
||||
widget.cameraOrientation.position.x = v;
|
||||
});
|
||||
_set();
|
||||
})
|
||||
]),
|
||||
Row(children: [
|
||||
Text("Y"),
|
||||
Slider(
|
||||
label: widget.cameraOrientation.position.y.toString(),
|
||||
value: widget.cameraOrientation.position.y,
|
||||
min: -100.0,
|
||||
max: 100.0,
|
||||
onChanged: (v) async {
|
||||
setState(() {
|
||||
widget.cameraOrientation.position.y = v;
|
||||
});
|
||||
_set();
|
||||
})
|
||||
]),
|
||||
Row(children: [
|
||||
Text("Z"),
|
||||
Slider(
|
||||
label: widget.cameraOrientation.position.z.toString(),
|
||||
value: widget.cameraOrientation.position.z,
|
||||
min: -100.0,
|
||||
max: 100.0,
|
||||
onChanged: (v) async {
|
||||
setState(() {
|
||||
widget.cameraOrientation.position.z = v;
|
||||
});
|
||||
_set();
|
||||
})
|
||||
]),
|
||||
Row(children: [
|
||||
Text("ROTX"),
|
||||
Slider(
|
||||
label: widget.cameraOrientation.rotationX.toString(),
|
||||
value: widget.cameraOrientation.rotationX,
|
||||
min: -pi,
|
||||
max: pi,
|
||||
onChanged: (value) async {
|
||||
setState(() {
|
||||
widget.cameraOrientation.rotationX = value;
|
||||
});
|
||||
_set();
|
||||
})
|
||||
]),
|
||||
Row(children: [
|
||||
Text("ROTY"),
|
||||
Slider(
|
||||
label: widget.cameraOrientation.rotationY.toString(),
|
||||
value: widget.cameraOrientation.rotationY,
|
||||
min: -pi,
|
||||
max: pi,
|
||||
onChanged: (v) async {
|
||||
setState(() {
|
||||
widget.cameraOrientation.rotationY = v;
|
||||
});
|
||||
_set();
|
||||
}),
|
||||
]),
|
||||
Row(children: [
|
||||
Text("ROTZ"),
|
||||
Slider(
|
||||
label: widget.cameraOrientation.rotationZ.toString(),
|
||||
value: widget.cameraOrientation.rotationZ,
|
||||
min: -pi,
|
||||
max: pi,
|
||||
onChanged: (v) async {
|
||||
setState(() {
|
||||
widget.cameraOrientation.rotationZ = v;
|
||||
});
|
||||
_set();
|
||||
})
|
||||
]),
|
||||
Wrap(
|
||||
children: [
|
||||
GestureDetector(
|
||||
|
||||
@@ -32,16 +32,22 @@ class FilamentGestureDetector extends StatelessWidget {
|
||||
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(
|
||||
{Key? key,
|
||||
required this.controller,
|
||||
this.child,
|
||||
this.showControlOverlay = false,
|
||||
this.enabled = true})
|
||||
this.enableCamera = true,
|
||||
this.enablePicking = true})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
@@ -53,14 +59,16 @@ class FilamentGestureDetector extends StatelessWidget {
|
||||
controller: controller,
|
||||
child: child,
|
||||
showControlOverlay: showControlOverlay,
|
||||
listenerEnabled: enabled,
|
||||
enableCamera: enableCamera,
|
||||
enablePicking: enablePicking,
|
||||
);
|
||||
} else {
|
||||
return FilamentGestureDetectorMobile(
|
||||
controller: controller,
|
||||
child: child,
|
||||
showControlOverlay: showControlOverlay,
|
||||
listenerEnabled: enabled,
|
||||
enableCamera: enableCamera,
|
||||
enablePicking: enablePicking,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,16 +28,22 @@ class FilamentGestureDetectorDesktop extends StatefulWidget {
|
||||
final bool showControlOverlay;
|
||||
|
||||
///
|
||||
/// If false, all gestures will be ignored.
|
||||
/// If false, gestures will not manipulate the active camera.
|
||||
///
|
||||
final bool listenerEnabled;
|
||||
final bool enableCamera;
|
||||
|
||||
///
|
||||
/// If false, pointer down events will not trigger hit-testing (picking).
|
||||
///
|
||||
final bool enablePicking;
|
||||
|
||||
const FilamentGestureDetectorDesktop(
|
||||
{Key? key,
|
||||
required this.controller,
|
||||
this.child,
|
||||
this.showControlOverlay = false,
|
||||
this.listenerEnabled = true})
|
||||
this.enableCamera = true,
|
||||
this.enablePicking = true})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
@@ -57,7 +63,8 @@ class _FilamentGestureDetectorDesktopState
|
||||
@override
|
||||
void didUpdateWidget(FilamentGestureDetectorDesktop oldWidget) {
|
||||
if (widget.showControlOverlay != oldWidget.showControlOverlay ||
|
||||
widget.listenerEnabled != oldWidget.listenerEnabled) {
|
||||
widget.enableCamera != oldWidget.enableCamera ||
|
||||
widget.enablePicking != oldWidget.enablePicking) {
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@@ -86,12 +93,9 @@ class _FilamentGestureDetectorDesktopState
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!widget.listenerEnabled) {
|
||||
return widget.child ?? Container();
|
||||
}
|
||||
return Listener(
|
||||
onPointerSignal: (PointerSignalEvent pointerSignal) async {
|
||||
if (pointerSignal is PointerScrollEvent) {
|
||||
if (pointerSignal is PointerScrollEvent && widget.enableCamera) {
|
||||
_zoom(pointerSignal);
|
||||
} else {
|
||||
throw Exception("TODO");
|
||||
@@ -108,20 +112,20 @@ class _FilamentGestureDetectorDesktopState
|
||||
onPointerMove: (PointerMoveEvent d) async {
|
||||
// if this is the first move event, we need to call rotateStart/panStart to set the first coordinates
|
||||
if (!_pointerMoving) {
|
||||
if (d.buttons == kTertiaryButton) {
|
||||
if (d.buttons == kTertiaryButton && widget.enableCamera) {
|
||||
widget.controller
|
||||
.rotateStart(d.localPosition.dx, d.localPosition.dy);
|
||||
} else {
|
||||
} else if (widget.enableCamera) {
|
||||
widget.controller
|
||||
.panStart(d.localPosition.dx, d.localPosition.dy);
|
||||
}
|
||||
}
|
||||
// set the _pointerMoving flag so we don't call rotateStart/panStart on future move events
|
||||
_pointerMoving = true;
|
||||
if (d.buttons == kTertiaryButton) {
|
||||
if (d.buttons == kTertiaryButton && widget.enableCamera) {
|
||||
widget.controller
|
||||
.rotateUpdate(d.localPosition.dx, d.localPosition.dy);
|
||||
} else {
|
||||
} else if (widget.enableCamera) {
|
||||
widget.controller.panUpdate(d.localPosition.dx, d.localPosition.dy);
|
||||
}
|
||||
},
|
||||
@@ -130,12 +134,12 @@ class _FilamentGestureDetectorDesktopState
|
||||
// 2) if _pointerMoving is false, this is interpreted as a pick
|
||||
// same applies to middle mouse button, but this is ignored as a pick
|
||||
onPointerUp: (PointerUpEvent d) async {
|
||||
if (d.buttons == kTertiaryButton) {
|
||||
if (d.buttons == kTertiaryButton && widget.enableCamera) {
|
||||
widget.controller.rotateEnd();
|
||||
} else {
|
||||
if (_pointerMoving) {
|
||||
if (_pointerMoving && widget.enableCamera) {
|
||||
widget.controller.panEnd();
|
||||
} else {
|
||||
} else if (widget.enablePicking) {
|
||||
widget.controller
|
||||
.pick(d.localPosition.dx.toInt(), d.localPosition.dy.toInt());
|
||||
}
|
||||
|
||||
@@ -28,9 +28,14 @@ class FilamentGestureDetectorMobile extends StatefulWidget {
|
||||
final bool showControlOverlay;
|
||||
|
||||
///
|
||||
/// If false, all gestures will be ignored.
|
||||
/// If false, gestures will not manipulate the active camera.
|
||||
///
|
||||
final bool listenerEnabled;
|
||||
final bool enableCamera;
|
||||
|
||||
///
|
||||
/// If false, pointer down events will not trigger hit-testing (picking).
|
||||
///
|
||||
final bool enablePicking;
|
||||
|
||||
final double zoomDelta;
|
||||
|
||||
@@ -39,7 +44,8 @@ class FilamentGestureDetectorMobile extends StatefulWidget {
|
||||
required this.controller,
|
||||
this.child,
|
||||
this.showControlOverlay = false,
|
||||
this.listenerEnabled = true,
|
||||
this.enableCamera = true,
|
||||
this.enablePicking = true,
|
||||
this.zoomDelta = 1})
|
||||
: super(key: key);
|
||||
|
||||
@@ -105,7 +111,8 @@ class _FilamentGestureDetectorMobileState
|
||||
@override
|
||||
void didUpdateWidget(FilamentGestureDetectorMobile oldWidget) {
|
||||
if (widget.showControlOverlay != oldWidget.showControlOverlay ||
|
||||
widget.listenerEnabled != oldWidget.listenerEnabled) {
|
||||
widget.enableCamera != oldWidget.enableCamera ||
|
||||
widget.enablePicking != oldWidget.enablePicking) {
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@@ -122,10 +129,6 @@ class _FilamentGestureDetectorMobileState
|
||||
// - inner is a Listener for all other gestures (including scroll zoom on desktop)
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!widget.listenerEnabled) {
|
||||
return widget.child ?? Container();
|
||||
}
|
||||
|
||||
return Stack(children: [
|
||||
Positioned.fill(
|
||||
child: GestureDetector(
|
||||
@@ -136,10 +139,10 @@ class _FilamentGestureDetectorMobileState
|
||||
});
|
||||
},
|
||||
onScaleStart: (d) async {
|
||||
if (d.pointerCount == 2) {
|
||||
if (d.pointerCount == 2 && widget.enableCamera) {
|
||||
_scaling = true;
|
||||
await widget.controller.zoomBegin();
|
||||
} else if (!_scaling) {
|
||||
} else if (!_scaling && widget.enableCamera) {
|
||||
if (_rotateOnPointerMove) {
|
||||
widget.controller.rotateStart(
|
||||
d.localFocalPoint.dx, d.localFocalPoint.dy);
|
||||
@@ -150,7 +153,7 @@ class _FilamentGestureDetectorMobileState
|
||||
}
|
||||
},
|
||||
onScaleUpdate: (ScaleUpdateDetails d) async {
|
||||
if (d.pointerCount == 2) {
|
||||
if (d.pointerCount == 2 && widget.enableCamera) {
|
||||
if (d.horizontalScale != _lastScale) {
|
||||
widget.controller.zoomUpdate(
|
||||
d.localFocalPoint.dx,
|
||||
@@ -158,7 +161,7 @@ class _FilamentGestureDetectorMobileState
|
||||
d.horizontalScale > _lastScale ? 0.1 : -0.1);
|
||||
_lastScale = d.horizontalScale;
|
||||
}
|
||||
} else if (!_scaling) {
|
||||
} else if (!_scaling && widget.enableCamera) {
|
||||
if (_rotateOnPointerMove) {
|
||||
widget.controller
|
||||
.rotateUpdate(d.focalPoint.dx, d.focalPoint.dy);
|
||||
@@ -169,9 +172,9 @@ class _FilamentGestureDetectorMobileState
|
||||
}
|
||||
},
|
||||
onScaleEnd: (d) async {
|
||||
if (d.pointerCount == 2) {
|
||||
if (d.pointerCount == 2 && widget.enableCamera) {
|
||||
widget.controller.zoomEnd();
|
||||
} else if (!_scaling) {
|
||||
} else if (!_scaling && widget.enableCamera) {
|
||||
if (_rotateOnPointerMove) {
|
||||
widget.controller.rotateEnd();
|
||||
} else {
|
||||
|
||||
@@ -3,72 +3,52 @@ import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.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;
|
||||
|
||||
class LightSliderWidget extends StatefulWidget {
|
||||
final FilamentController controller;
|
||||
|
||||
late final v.Vector3 initialPosition;
|
||||
late final v.Vector3 initialDirection;
|
||||
final int initialType;
|
||||
final double initialColor;
|
||||
final double initialIntensity;
|
||||
final bool initialCastShadows;
|
||||
final LightOptions options;
|
||||
final bool showControls;
|
||||
|
||||
LightSliderWidget(
|
||||
{super.key,
|
||||
required this.controller,
|
||||
this.initialType = 0,
|
||||
this.initialColor = 6500,
|
||||
this.initialIntensity = 100000,
|
||||
this.initialCastShadows = true,
|
||||
this.showControls = false,
|
||||
v.Vector3? initialDirection,
|
||||
v.Vector3? initialPosition}) {
|
||||
this.initialDirection = initialDirection ?? v.Vector3(0, 0.5, -1);
|
||||
this.initialPosition = initialPosition ?? v.Vector3(0, 0.5, 1);
|
||||
}
|
||||
|
||||
required this.options});
|
||||
@override
|
||||
State<StatefulWidget> createState() => _IblRotationSliderWidgetState();
|
||||
State<StatefulWidget> createState() => _LightSliderWidgetState();
|
||||
}
|
||||
|
||||
class _IblRotationSliderWidgetState extends State<LightSliderWidget> {
|
||||
v.Vector3 lightPos = v.Vector3(1, 0.1, 1);
|
||||
v.Vector3 lightDir = v.Vector3(-1, 0.1, 0);
|
||||
bool castShadows = true;
|
||||
int type = 0;
|
||||
double color = 6500;
|
||||
double intensity = 100000;
|
||||
class _LightSliderWidgetState extends State<LightSliderWidget> {
|
||||
FilamentEntity? _light;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
type = widget.initialType;
|
||||
castShadows = widget.initialCastShadows;
|
||||
color = widget.initialColor;
|
||||
lightPos = widget.initialPosition;
|
||||
lightDir = widget.initialDirection;
|
||||
intensity = widget.initialIntensity;
|
||||
_set();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
Future _set() async {
|
||||
if (_light != null) await widget.controller.removeLight(_light!);
|
||||
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(
|
||||
type,
|
||||
color,
|
||||
intensity,
|
||||
lightPos.x,
|
||||
lightPos.y,
|
||||
lightPos.z,
|
||||
lightDir.x,
|
||||
lightDir.y,
|
||||
lightDir.z,
|
||||
castShadows);
|
||||
widget.options.directionalType,
|
||||
widget.options.directionalColor,
|
||||
widget.options.directionalIntensity,
|
||||
widget.options.directionalPosition.x,
|
||||
widget.options.directionalPosition.y,
|
||||
widget.options.directionalPosition.z,
|
||||
widget.options.directionalDirection.x,
|
||||
widget.options.directionalDirection.y,
|
||||
widget.options.directionalDirection.z,
|
||||
widget.options.directionalCastShadows);
|
||||
|
||||
setState(() {});
|
||||
}
|
||||
@@ -87,35 +67,39 @@ class _IblRotationSliderWidgetState extends State<LightSliderWidget> {
|
||||
showValueIndicator: ShowValueIndicator.always,
|
||||
valueIndicatorTextStyle: TextStyle(color: Colors.black)),
|
||||
child: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
Text("Directional"),
|
||||
Row(children: [
|
||||
Expanded(
|
||||
child: Slider(
|
||||
label: "POSX",
|
||||
value: lightPos.x,
|
||||
label:
|
||||
"POSX ${widget.options.directionalPosition.x}",
|
||||
value: widget.options.directionalPosition.x,
|
||||
min: -10.0,
|
||||
max: 10.0,
|
||||
onChanged: (value) {
|
||||
lightPos.x = value;
|
||||
widget.options.directionalPosition.x = value;
|
||||
_set();
|
||||
})),
|
||||
Expanded(
|
||||
child: Slider(
|
||||
label: "POSY",
|
||||
value: lightPos.y,
|
||||
min: -10.0,
|
||||
max: 10.0,
|
||||
label:
|
||||
"POSY ${widget.options.directionalPosition.y}",
|
||||
value: widget.options.directionalPosition.y,
|
||||
min: -100.0,
|
||||
max: 100.0,
|
||||
onChanged: (value) {
|
||||
lightPos.y = value;
|
||||
widget.options.directionalPosition.y = value;
|
||||
_set();
|
||||
})),
|
||||
Expanded(
|
||||
child: Slider(
|
||||
label: "POSZ",
|
||||
value: lightPos.z,
|
||||
min: -10.0,
|
||||
max: 10.0,
|
||||
label:
|
||||
"POSZ ${widget.options.directionalPosition.z}",
|
||||
value: widget.options.directionalPosition.z,
|
||||
min: -100.0,
|
||||
max: 100.0,
|
||||
onChanged: (value) {
|
||||
lightPos.z = value;
|
||||
widget.options.directionalPosition.z = value;
|
||||
_set();
|
||||
}))
|
||||
]),
|
||||
@@ -123,58 +107,58 @@ class _IblRotationSliderWidgetState extends State<LightSliderWidget> {
|
||||
Expanded(
|
||||
child: Slider(
|
||||
label: "DIRX",
|
||||
value: lightDir.x,
|
||||
min: -10.0,
|
||||
max: 10.0,
|
||||
value: widget.options.directionalDirection.x,
|
||||
min: -1.0,
|
||||
max: 1.0,
|
||||
onChanged: (value) {
|
||||
lightDir.x = value;
|
||||
widget.options.directionalDirection.x = value;
|
||||
_set();
|
||||
})),
|
||||
Expanded(
|
||||
child: Slider(
|
||||
label: "DIRY",
|
||||
value: lightDir.y,
|
||||
min: -10.0,
|
||||
max: 10.0,
|
||||
value: widget.options.directionalDirection.y,
|
||||
min: -1.0,
|
||||
max: 1.0,
|
||||
onChanged: (value) {
|
||||
lightDir.y = value;
|
||||
widget.options.directionalDirection.y = value;
|
||||
_set();
|
||||
})),
|
||||
Expanded(
|
||||
child: Slider(
|
||||
label: "DIRZ",
|
||||
value: lightDir.z,
|
||||
min: -10.0,
|
||||
max: 10.0,
|
||||
value: widget.options.directionalDirection.z,
|
||||
min: -1.0,
|
||||
max: 1.0,
|
||||
onChanged: (value) {
|
||||
lightDir.z = value;
|
||||
widget.options.directionalDirection.z = value;
|
||||
_set();
|
||||
}))
|
||||
]),
|
||||
Slider(
|
||||
label: "Color",
|
||||
value: color,
|
||||
value: widget.options.directionalColor,
|
||||
min: 0,
|
||||
max: 16000,
|
||||
onChanged: (value) {
|
||||
color = value;
|
||||
widget.options.directionalColor = value;
|
||||
_set();
|
||||
}),
|
||||
Slider(
|
||||
label: "Intensity",
|
||||
value: intensity,
|
||||
label: "Intensity ${widget.options.directionalIntensity}",
|
||||
value: widget.options.directionalIntensity,
|
||||
min: 0,
|
||||
max: 1000000,
|
||||
onChanged: (value) {
|
||||
intensity = value;
|
||||
widget.options.directionalIntensity = value;
|
||||
_set();
|
||||
}),
|
||||
DropdownButton(
|
||||
onChanged: (v) {
|
||||
this.type = v;
|
||||
this.widget.options.directionalType = v;
|
||||
_set();
|
||||
},
|
||||
value: type,
|
||||
value: this.widget.options.directionalType,
|
||||
items: List<DropdownMenuItem>.generate(
|
||||
5,
|
||||
(idx) => DropdownMenuItem(
|
||||
@@ -182,13 +166,27 @@ class _IblRotationSliderWidgetState extends State<LightSliderWidget> {
|
||||
child: Text("$idx"),
|
||||
))),
|
||||
Row(children: [
|
||||
Text("Shadows: $castShadows"),
|
||||
Text(
|
||||
"Shadows: ${this.widget.options.directionalCastShadows}"),
|
||||
Checkbox(
|
||||
value: castShadows,
|
||||
value: widget.options.directionalCastShadows,
|
||||
onChanged: (v) {
|
||||
this.castShadows = v!;
|
||||
this.widget.options.directionalCastShadows = v!;
|
||||
_set();
|
||||
})
|
||||
]),
|
||||
Text("Indirect"),
|
||||
Row(children: [
|
||||
Expanded(
|
||||
child: Slider(
|
||||
label: "Intensity ${widget.options.iblIntensity}",
|
||||
value: widget.options.iblIntensity,
|
||||
min: 0.0,
|
||||
max: 200000,
|
||||
onChanged: (value) {
|
||||
widget.options.iblIntensity = value;
|
||||
_set();
|
||||
})),
|
||||
])
|
||||
]))));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user