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,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(

View File

@@ -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,
);
}
}

View File

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

View File

@@ -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 {

View File

@@ -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();
})),
])
]))));
}