add setCameraCulling method to set near/far culling plane and return vector_math:Frustum from planes returned by get_camera_frustum

This commit is contained in:
Nick Fisher
2023-11-09 15:08:34 +08:00
parent 76f723c497
commit f2a458b9ca
11 changed files with 227 additions and 104 deletions

View File

@@ -6,8 +6,12 @@ import 'package:flutter_filament/filament_controller.dart';
class CameraMatrixOverlay extends StatefulWidget {
final FilamentController controller;
final bool showProjectionMatrices;
const CameraMatrixOverlay({super.key, required this.controller});
const CameraMatrixOverlay(
{super.key,
required this.controller,
required this.showProjectionMatrices});
@override
State<StatefulWidget> createState() => _CameraMatrixOverlayState();
@@ -18,21 +22,36 @@ class _CameraMatrixOverlayState extends State<CameraMatrixOverlay> {
String? _cameraPosition;
String? _cameraRotation;
String? _cameraProjectionMatrix;
String? _cameraCullingProjectionMatrix;
void _tick(Timer timer) async {
var cameraPosition = await widget.controller.getCameraPosition();
var cameraRotation = await widget.controller.getCameraRotation();
_cameraPosition =
"${cameraPosition.storage.map((v) => v.toStringAsFixed(2))}";
_cameraRotation =
"${cameraRotation.storage.map((v) => v.toStringAsFixed(2))}";
if (widget.showProjectionMatrices) {
var projMatrix = await widget.controller.getCameraProjectionMatrix();
var cullingMatrix =
await widget.controller.getCameraCullingProjectionMatrix();
_cameraProjectionMatrix =
projMatrix.storage.map((v) => v.toStringAsFixed(2)).join(",");
_cameraCullingProjectionMatrix =
cullingMatrix.storage.map((v) => v.toStringAsFixed(2)).join(",");
}
setState(() {});
}
void _updateTimer() {
_cameraTimer?.cancel();
if (widget.controller.hasViewer.value) {
_cameraTimer =
Timer.periodic(const Duration(milliseconds: 50), (timer) async {
var cameraPosition = await widget.controller.getCameraPosition();
var cameraRotation = await widget.controller.getCameraRotation();
_cameraPosition =
"${cameraPosition.storage.map((v) => v.toStringAsFixed(2))}";
_cameraRotation =
"${cameraRotation.storage.map((v) => v.toStringAsFixed(2))}";
setState(() {});
});
_cameraTimer = Timer.periodic(const Duration(milliseconds: 50), _tick);
}
}
@@ -45,6 +64,12 @@ class _CameraMatrixOverlayState extends State<CameraMatrixOverlay> {
widget.controller.hasViewer.addListener(_updateTimer);
}
@override
void didUpdateWidget(CameraMatrixOverlay oldWidget) {
super.didUpdateWidget(oldWidget);
setState(() {});
}
@override
void dispose() {
super.dispose();
@@ -59,7 +84,20 @@ class _CameraMatrixOverlayState extends State<CameraMatrixOverlay> {
color: Colors.black.withOpacity(0.5),
borderRadius: BorderRadius.circular(29)),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
child: Text("Camera position : $_cameraPosition $_cameraRotation",
style: const TextStyle(color: Colors.white, fontSize: 12)));
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text("Camera position : $_cameraPosition $_cameraRotation",
style: const TextStyle(color: Colors.white, fontSize: 12)),
widget.showProjectionMatrices
? Text("Projection matrix : $_cameraProjectionMatrix",
style: const TextStyle(color: Colors.white, fontSize: 12))
: Container(),
widget.showProjectionMatrices
? Text("Culling matrix : $_cameraCullingProjectionMatrix",
style: const TextStyle(color: Colors.white, fontSize: 12))
: Container(),
]));
}
}

View File

@@ -76,6 +76,7 @@ class ExampleWidgetState extends State<ExampleWidget> {
static FilamentEntity? directionalLight;
static bool loop = false;
static final showProjectionMatrices = ValueNotifier<bool>(false);
late StreamSubscription _listener;
@@ -158,7 +159,11 @@ class ExampleWidgetState extends State<ExampleWidget> {
? Container()
: Padding(
padding: const EdgeInsets.only(top: 40, left: 20, right: 20),
child: CameraMatrixOverlay(controller: _filamentController!),
child: ValueListenableBuilder(
valueListenable: showProjectionMatrices,
builder: (ctx, value, child) => CameraMatrixOverlay(
controller: _filamentController!,
showProjectionMatrices: value)),
),
_filamentController == null
? Container()

View File

@@ -15,8 +15,66 @@ class CameraSubmenu extends StatefulWidget {
}
class _CameraSubmenuState extends State<CameraSubmenu> {
double _near = 0.05;
double _far = 1000.0;
final _menuController = MenuController();
List<Widget> _cameraMenu() {
return [
MenuItemButton(
closeOnActivate: false,
onPressed: () async {
ExampleWidgetState.showProjectionMatrices.value =
!ExampleWidgetState.showProjectionMatrices.value;
print("Set to ${ExampleWidgetState.showProjectionMatrices}");
},
child: Text(
'${ExampleWidgetState.showProjectionMatrices.value ? "Hide" : "Display"} camera projection/culling projection matrices',
style: TextStyle(
fontWeight: ExampleWidgetState.showProjectionMatrices.value
? FontWeight.bold
: FontWeight.normal),
),
),
SubmenuButton(
menuChildren: [1.0, 7.0, 14.0, 28.0, 56.0]
.map((v) => MenuItemButton(
onPressed: () {
widget.controller.setCameraFocalLength(v);
},
child: Text(
v.toStringAsFixed(2),
),
))
.toList(),
child: const Text("Set camera focal length")),
SubmenuButton(
menuChildren: [0.05, 0.1, 1.0, 10.0, 100.0]
.map((v) => MenuItemButton(
onPressed: () {
_near = v;
widget.controller.setCameraCulling(_near, _far);
},
child: Text(
v.toStringAsFixed(2),
),
))
.toList(),
child: const Text("Set near")),
SubmenuButton(
menuChildren: [5.0, 50.0, 500.0, 1000.0, 100000.0]
.map((v) => MenuItemButton(
onPressed: () {
_far = v;
widget.controller.setCameraCulling(_near, _far);
},
child: Text(
v.toStringAsFixed(2),
),
))
.toList(),
child: const Text("Set far")),
MenuItemButton(
onPressed: () async {
widget.controller.setCameraPosition(1.0, 1.0, -1.0);
@@ -80,7 +138,21 @@ class _CameraSubmenuState extends State<CameraSubmenu> {
closeOnActivate: false,
onPressed: () async {
var frustum = await widget.controller.getCameraFrustum();
await showDialog(
var normalString = [
frustum.plane0,
frustum.plane1,
frustum.plane2,
frustum.plane3,
frustum.plane4,
frustum.plane5
]
.map((plane) =>
plane.normal.storage
.map((v) => v.toStringAsFixed(2))
.join(",") +
",${plane.constant}")
.join("\n");
showDialog(
context: context,
builder: (ctx) {
return Center(
@@ -88,8 +160,10 @@ class _CameraSubmenuState extends State<CameraSubmenu> {
height: 300,
width: 300,
color: Colors.white,
child: Text(frustum.toString())));
child:
Text("Frustum plane normals : $normalString ")));
});
_menuController.close();
},
child: const Text("Get frustum")),
SubmenuButton(
@@ -164,6 +238,7 @@ class _CameraSubmenuState extends State<CameraSubmenu> {
@override
Widget build(BuildContext context) {
return SubmenuButton(
controller: _menuController,
menuChildren: _cameraMenu(),
child: const Text("Camera"),
);

View File

@@ -105,7 +105,8 @@ namespace polyvox
const filament::Frustum getCameraFrustum();
void setCameraModelMatrix(const float *const matrix);
void setCameraProjectionMatrix(const double *const matrix, double near, double far);
void setCameraFocalLength(float fl);
void setCameraFocalLength(float focalLength);
void setCameraCulling(double near, double far);
void setCameraFocusDistance(float focusDistance);
void setCameraManipulatorOptions(filament::camutils::Mode mode, double orbitSpeedX, double orbitSpeedY, double zoomSpeed);
void grabBegin(float x, float y, bool pan);
@@ -169,6 +170,8 @@ namespace polyvox
math::mat4f _cameraPosition;
math::mat4f _cameraRotation;
void _createManipulator();
double _near = 0.05;
double _far = 1000.0;
ColorGrading *colorGrading = nullptr;

View File

@@ -153,6 +153,7 @@ FLUTTER_PLUGIN_EXPORT const double* const get_camera_model_matrix(const void* co
FLUTTER_PLUGIN_EXPORT const double* const get_camera_view_matrix(const void* const viewer);
FLUTTER_PLUGIN_EXPORT const double* const get_camera_projection_matrix(const void* const viewer);
FLUTTER_PLUGIN_EXPORT void set_camera_projection_matrix(const void* const viewer, const double *const matrix, double near, double far);
FLUTTER_PLUGIN_EXPORT void set_camera_culling(const void* const viewer, double near, double far);
FLUTTER_PLUGIN_EXPORT const double* const get_camera_culling_projection_matrix(const void* const viewer);
FLUTTER_PLUGIN_EXPORT const double* const get_camera_frustum(const void* const viewer);
FLUTTER_PLUGIN_EXPORT void set_camera_focal_length(const void* const viewer, float focalLength);

View File

@@ -96,9 +96,6 @@ namespace filament
namespace polyvox
{
const double kNearPlane = 0.05; // 5 cm
const double kFarPlane = 1000.0; // 1 km
// const float kAperture = 1.0f;
// const float kShutterSpeed = 1.0f;
// const float kSensitivity = 50.0f;
@@ -163,8 +160,8 @@ namespace polyvox
_view->setCamera(_mainCamera);
_cameraFocalLength = 28.0f;
_mainCamera->setLensProjection(_cameraFocalLength, 1.0f, kNearPlane,
kFarPlane);
_mainCamera->setLensProjection(_cameraFocalLength, 1.0f, _near,
_far);
// _mainCamera->setExposure(kAperture, kShutterSpeed, kSensitivity);
Log("View created");
const float aperture = _mainCamera->getAperture();
@@ -743,8 +740,20 @@ namespace polyvox
{
Camera &cam = _view->getCamera();
_cameraFocalLength = focalLength;
cam.setLensProjection(_cameraFocalLength, 1.0f, kNearPlane,
kFarPlane);
cam.setLensProjection(_cameraFocalLength, 1.0f, _near,
_far);
}
///
/// Set the focal length of the active camera.
///
void FilamentViewer::setCameraCulling(double near, double far)
{
Camera &cam = _view->getCamera();
_near = near;
_far = far;
cam.setLensProjection(_cameraFocalLength, 1.0f, _near,
_far);
}
///
@@ -1036,8 +1045,8 @@ namespace polyvox
const double aspect = (double)width / height;
Camera &cam = _view->getCamera();
cam.setLensProjection(_cameraFocalLength, 1.0f, kNearPlane,
kFarPlane);
cam.setLensProjection(_cameraFocalLength, 1.0f, _near,
_far);
cam.setScaling({1.0 / aspect, 1.0});
@@ -1128,7 +1137,7 @@ namespace polyvox
matrix[13],
matrix[14],
matrix[15]);
cam.setCustomProjection(projectionMatrix, near, far);
cam.setCustomProjection(projectionMatrix,projectionMatrix, near, far);
}
const math::mat4 FilamentViewer::getCameraModelMatrix()

View File

@@ -152,6 +152,11 @@ extern "C"
{
((FilamentViewer *)viewer)->setCameraProjectionMatrix(matrix, near, far);
}
void set_camera_culling(const void *const viewer, double near, double far)
{
((FilamentViewer *)viewer)->setCameraCulling(near, far);
}
const double *const get_camera_frustum(const void *const viewer)
{

View File

@@ -335,10 +335,15 @@ abstract class FilamentController {
Future setBloom(double bloom);
///
/// Sets the focal length of the camera.
/// Sets the focal length of the camera. Default value is 28.0.
///
Future setCameraFocalLength(double focalLength);
///
/// Sets the near/far culling planes for the active camera. Default values are 0.05/1000.0. See Camera.h for details.
///
Future setCameraCulling(double near, double far);
///
/// Sets the focus distance for the camera.
///
@@ -370,7 +375,8 @@ abstract class FilamentController {
Future<Matrix4> getCameraCullingProjectionMatrix();
///
/// Get the camera's culling frustum in world space. Returns six Vector4s defining the left, right, bottom, top, far and near planes respectively. See Camera.h and Frustum.h for more details.
/// Get the camera's culling frustum in world space. Returns a (vector_math) [Frustum] instance where plane0-plane6 define the left, right, bottom, top, far and near planes respectively.
/// See Camera.h and (filament) Frustum.h for more details.
///
Future<Frustum> getCameraFrustum();

View File

@@ -846,6 +846,14 @@ class FilamentControllerFFI extends FilamentController {
set_camera_focal_length(_viewer!, focalLength);
}
@override
Future setCameraCulling(double near, double far) async {
if (_viewer == null) {
throw Exception("No viewer available, ignoring");
}
set_camera_culling(_viewer!, near, far);
}
@override
Future setCameraFocusDistance(double focusDistance) async {
if (_viewer == null) {
@@ -1084,11 +1092,20 @@ class FilamentControllerFFI extends FilamentController {
_viewer!, mode.index, orbitSpeedX, orbitSpeedX, zoomSpeed);
}
///
/// I don't think these two methods are accurate - don't rely on them, use the Frustum values instead.
/// I think because we use [setLensProjection] and [setScaling] together, this projection matrix doesn't accurately reflect the field of view (because it's using an additional scaling matrix).
/// Also, the near/far planes never seem to get updated (which is what I would expect to see when calling [getCameraCullingProjectionMatrix])
///
@override
Future<Matrix4> getCameraProjectionMatrix() async {
if (_viewer == null) {
throw Exception("No viewer available");
}
print(
"WARNING: getCameraProjectionMatrix and getCameraCullingProjectionMatrix are not reliable. Consider these broken");
var arrayPtr = get_camera_projection_matrix(_viewer!);
var doubleList = arrayPtr.asTypedList(16);
var projectionMatrix = Matrix4.fromList(doubleList);
@@ -1101,6 +1118,8 @@ class FilamentControllerFFI extends FilamentController {
if (_viewer == null) {
throw Exception("No viewer available");
}
print(
"WARNING: getCameraProjectionMatrix and getCameraCullingProjectionMatrix are not reliable. Consider these broken");
var arrayPtr = get_camera_culling_projection_matrix(_viewer!);
var doubleList = arrayPtr.asTypedList(16);
var projectionMatrix = Matrix4.fromList(doubleList);
@@ -1113,9 +1132,27 @@ class FilamentControllerFFI extends FilamentController {
if (_viewer == null) {
throw Exception("No viewer available");
}
var arrayPtr = get_camera_frustum(_viewer!);
var doubleList = arrayPtr.asTypedList(24);
var planeNormals = [];
for (int i = 0; i < 6; i++) {
planeNormals.add(Vector3.array(doubleList.sublist(i * 3, (i + 1) * 3)));
}
var projectionMatrix = await getCameraProjectionMatrix();
var frustum = Frustum();
frustum.plane0.setFromComponents(
doubleList[0], doubleList[1], doubleList[2], doubleList[3]);
frustum.plane1.setFromComponents(
doubleList[4], doubleList[5], doubleList[6], doubleList[7]);
frustum.plane2.setFromComponents(
doubleList[8], doubleList[9], doubleList[10], doubleList[11]);
frustum.plane3.setFromComponents(
doubleList[12], doubleList[13], doubleList[14], doubleList[15]);
frustum.plane4.setFromComponents(
doubleList[16], doubleList[17], doubleList[18], doubleList[19]);
frustum.plane5.setFromComponents(
doubleList[20], doubleList[21], doubleList[22], doubleList[23]);
return Frustum.matrix(projectionMatrix);
return frustum;
}
}

View File

@@ -629,6 +629,14 @@ external void set_camera_projection_matrix(
double far,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, ffi.Double, ffi.Double)>(
symbol: 'set_camera_culling', assetId: 'flutter_filament_plugin')
external void set_camera_culling(
ffi.Pointer<ffi.Void> viewer,
double near,
double far,
);
@ffi.Native<ffi.Pointer<ffi.Double> Function(ffi.Pointer<ffi.Void>)>(
symbol: 'get_camera_culling_projection_matrix',
assetId: 'flutter_filament_plugin')

View File

@@ -57,14 +57,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.18.0"
crypto:
dependency: transitive
description:
name: crypto
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev"
source: hosted
version: "3.0.3"
fake_async:
dependency: transitive
description:
@@ -128,38 +120,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.2"
intl:
dependency: transitive
description:
name: intl
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
url: "https://pub.dev"
source: hosted
version: "0.18.1"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: f38a2c91c12f31726ca13015fbab3d2e9440edcb7c17b8b36ed9b85ed6eee6a2
url: "https://pub.dev"
source: hosted
version: "9.0.11"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "23770c69594f5260a79fe9d84e29f8b175d1b05d128e751c904b3cdf910e5dfc"
url: "https://pub.dev"
source: hosted
version: "1.0.9"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: b06739349ec2477e943055aea30172c5c7000225f79dad4702e2ec0eda79a6ff
url: "https://pub.dev"
source: hosted
version: "1.0.5"
lints:
dependency: transitive
description:
@@ -188,18 +148,18 @@ packages:
dependency: transitive
description:
name: material_color_utilities
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
url: "https://pub.dev"
source: hosted
version: "0.8.0"
version: "0.5.0"
meta:
dependency: transitive
description:
name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
url: "https://pub.dev"
source: hosted
version: "1.11.0"
version: "1.10.0"
package_config:
dependency: transitive
description:
@@ -293,14 +253,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.1"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
url: "https://pub.dev"
source: hosted
version: "1.3.2"
vector_math:
dependency: "direct main"
description:
@@ -309,30 +261,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: a13d5503b4facefc515c8c587ce3cf69577a7b064a9f1220e005449cf1f64aad
url: "https://pub.dev"
source: hosted
version: "12.0.0"
web:
dependency: transitive
description:
name: web
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
sha256: "14f1f70c51119012600c5f1f60ca68efda5a9b6077748163c6af2893ec5df8fc"
url: "https://pub.dev"
source: hosted
version: "0.3.0"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b
url: "https://pub.dev"
source: hosted
version: "2.4.0"
version: "0.2.1-beta"
yaml:
dependency: transitive
description:
@@ -350,5 +286,5 @@ packages:
source: hosted
version: "2.1.0"
sdks:
dart: ">=3.2.0-194.0.dev <4.0.0"
dart: ">=3.2.0-157.0.dev <4.0.0"
flutter: ">=3.16.0-0.2.pre"