diff --git a/thermion_dart/lib/src/filament/src/implementation/ffi_camera.dart b/thermion_dart/lib/src/filament/src/implementation/ffi_camera.dart index 5a712d5d..6941d0e2 100644 --- a/thermion_dart/lib/src/filament/src/implementation/ffi_camera.dart +++ b/thermion_dart/lib/src/filament/src/implementation/ffi_camera.dart @@ -195,7 +195,7 @@ class FFICamera extends Camera { Engine_destroyCamera(app.engine, camera); } - Future setCameraExposure( + Future setExposure( double aperture, double shutterSpeed, double sensitivity) async { Camera_setExposure(camera, aperture, shutterSpeed, sensitivity); } diff --git a/thermion_dart/lib/src/filament/src/interface/camera.dart b/thermion_dart/lib/src/filament/src/interface/camera.dart index adf73a3d..aa7dfd49 100644 --- a/thermion_dart/lib/src/filament/src/interface/camera.dart +++ b/thermion_dart/lib/src/filament/src/interface/camera.dart @@ -4,12 +4,17 @@ import 'package:thermion_dart/thermion_dart.dart'; enum Projection { Perspective, Orthographic } abstract class Camera { - + /// + /// + /// Future getPosition() async { final modelMatrix = await getModelMatrix(); return modelMatrix.getTranslation(); } + /// + /// + /// Future lookAt(Vector3 position, {Vector3? focus, Vector3? up}) async { focus ??= Vector3.zero(); up ??= Vector3(0, 1, 0); @@ -19,9 +24,30 @@ abstract class Camera { } /// - /// Sets the camera exposure. + /// From Camera.h: /// - Future setCameraExposure( + /// Sets this camera's exposure (default is f/16, 1/125s, 100 ISO) + /// + /// The exposure ultimately controls the scene's brightness, just like with a real camera. + /// The default values provide adequate exposure for a camera placed outdoors on a sunny day + /// with the sun at the zenith. + /// + /// @param aperture Aperture in f-stops, clamped between 0.5 and 64. + /// A lower \p aperture value ///increases/// the exposure, leading to + /// a brighter scene. Realistic values are between 0.95 and 32. + /// + /// @param shutterSpeed Shutter speed in seconds, clamped between 1/25,000 and 60. + /// A lower shutter speed increases the exposure. Realistic values are + /// between 1/8000 and 30. + /// + /// @param sensitivity Sensitivity in ISO, clamped between 10 and 204,800. + /// A higher \p sensitivity increases the exposure. Realistic values are + /// between 50 and 25600. + /// + /// @note + /// With the default parameters, the scene must contain at least one Light of intensity + /// similar to the sun (e.g.: a 100,000 lux directional light). + Future setExposure( double aperture, double shutterSpeed, double sensitivity); Future setProjection(Projection projection, double left, double right, @@ -52,5 +78,7 @@ abstract class Camera { Future setFocusDistance(double focusDistance); Future getHorizontalFieldOfView(); Future getVerticalFieldOfView(); + Future getFrustum(); + Future destroy(); } diff --git a/thermion_dart/native/src/c_api/TCamera.cpp b/thermion_dart/native/src/c_api/TCamera.cpp index 914ecdb0..30c79f64 100644 --- a/thermion_dart/native/src/c_api/TCamera.cpp +++ b/thermion_dart/native/src/c_api/TCamera.cpp @@ -52,6 +52,12 @@ namespace thermion camera->setLensProjection(focalLength, aspect, near, far); } + EMSCRIPTEN_KEEPALIVE void Camera_setExposure(TCamera *tCamera, float aperture, float shutterSpeed, float sensitivity) { + TRACE("Setting exposure %f %f %f", aperture, shutterSpeed, sensitivity); + auto *camera = reinterpret_cast(tCamera); + camera->setExposure(aperture, shutterSpeed, sensitivity); + } + EMSCRIPTEN_KEEPALIVE void Camera_setModelMatrix(TCamera *tCamera, double *tModelMatrix) { auto *camera = reinterpret_cast(tCamera); auto modelMatrix = convert_double_to_mat4f(tModelMatrix); diff --git a/thermion_dart/test/camera_tests.dart b/thermion_dart/test/camera_tests.dart index fef5e853..26c908eb 100644 --- a/thermion_dart/test/camera_tests.dart +++ b/thermion_dart/test/camera_tests.dart @@ -1,6 +1,5 @@ // ignore_for_file: unused_local_variable -import 'dart:math'; import 'package:thermion_dart/thermion_dart.dart'; import 'package:test/test.dart'; import 'helpers.dart'; @@ -8,7 +7,7 @@ import 'helpers.dart'; void main() async { final testHelper = TestHelper("camera"); await testHelper.setup(); - group('camera', () { + test('create/destroy camera', () async { await testHelper.withViewer((viewer) async { final camera = await viewer.createCamera(); @@ -16,7 +15,7 @@ void main() async { }); }); - test('model matrix', () async { + test('set model matrix', () async { await testHelper.withViewer((viewer) async { final camera = await viewer.getActiveCamera(); await camera.setModelMatrix(Matrix4.translation(Vector3.all(4.0))); @@ -31,6 +30,15 @@ void main() async { }); }); + test('set exposure', () async { + await testHelper.withViewer((viewer) async { + final camera = await viewer.getActiveCamera(); + await testHelper.capture(viewer.view, "camera_default_exposure"); + await camera.setExposure(16.0, 1.0 / 125.0, 200.0); + await testHelper.capture(viewer.view, "camera_iso_200"); + }, addSkybox: true); + }); + // test('getCameraViewMatrix', () async { // await testHelper.withViewer((viewer) async { // await viewer.setCameraModelMatrix4(Matrix4.identity()); @@ -55,48 +63,49 @@ void main() async { // }); // }); - // test('getCameraProjectionMatrix', () async { - // await testHelper.withViewer((viewer) async { - // var projectionMatrix = await viewer.getCameraProjectionMatrix(); - // print(projectionMatrix); - // }); - // }); + test('getCameraProjectionMatrix', () async { + await testHelper.withViewer((viewer) async { + var camera = await viewer.getActiveCamera(); + var projectionMatrix = await camera.getProjectionMatrix(); + print(projectionMatrix); + }); + }); - // test('getCameraCullingProjectionMatrix', () async { - // await testHelper.withViewer((viewer) async { - // // ignore: dead_code - // var viewer = await testHelper.createViewer(); - // var matrix = await viewer.getCameraCullingProjectionMatrix(); - // print(matrix); - // }); - // }); + test('getCameraCullingProjectionMatrix', () async { + await testHelper.withViewer((viewer) async { + var camera = await viewer.getActiveCamera(); + var matrix = await camera.getCullingProjectionMatrix(); + print(matrix); + }); + }); - // test('getCameraFrustum', () async { - // await testHelper.withViewer((viewer) async { - // var frustum = await viewer.getCameraFrustum(); - // print(frustum.plane5.normal); - // print(frustum.plane5.constant); + test('getCameraFrustum', () async { + await testHelper.withViewer((viewer) async { + var camera = await viewer.getActiveCamera(); + var frustum = await camera.getFrustum(); - // var camera = await viewer.getMainCamera(); + print(frustum.plane5.normal); + print(frustum.plane5.constant); - // await camera.setLensProjection( - // near: 10.0, far: 1000.0, aspect: 1.0, focalLength: 28.0); - // frustum = await viewer.getCameraFrustum(); - // print(frustum.plane5.normal); - // print(frustum.plane5.constant); - // }); - // }); + await camera.setLensProjection( + near: 10.0, far: 1000.0, aspect: 1.0, focalLength: 28.0); + frustum = await camera.getFrustum(); + print(frustum.plane5.normal); + print(frustum.plane5.constant); + }); + }); - // test('set orthographic projection', () async { - // await testHelper.withViewer((viewer) async { - // var camera = await viewer.getMainCamera(); - // await viewer.createGeometry(GeometryHelper.cube()); + test('set orthographic projection', () async { + await testHelper.withViewer((viewer) async { + var camera = await viewer.getActiveCamera(); - // await camera.setProjection( - // Projection.Orthographic, -0.05, 0.05, -0.05, 0.05, 0.05, 10000); - // await testHelper.capture(viewer, "camera_set_orthographic_projection"); - // }); - // }); + await viewer.createGeometry(GeometryHelper.cube()); + + await camera.setProjection( + Projection.Orthographic, -0.05, 0.05, -0.05, 0.05, 0.05, 10000); + await testHelper.capture(viewer.view, "camera_set_orthographic_projection"); + }); + }); // test('set perspective projection/culling matrix', () async { // await testHelper.withViewer((viewer) async { @@ -237,5 +246,5 @@ void main() async { // () => viewer.getCameraAt(2), throwsA(isA())); // }); // }); - }); + }