From 1177a71f7369b2dd6420710520094ec9f85e2438 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Thu, 20 Mar 2025 18:56:10 +0800 Subject: [PATCH] test updates --- thermion_dart/test/asset_tests.dart | 94 ++--- thermion_dart/test/assets/cube.bin | Bin 0 -> 840 bytes thermion_dart/test/assets/cube.gltf | 121 +++++++ thermion_dart/test/background_tests.dart | 24 ++ thermion_dart/test/camera_tests.dart | 361 +++++++++---------- thermion_dart/test/geometry_tests.dart | 61 ++-- thermion_dart/test/gltf_tests.dart | 262 +++++++------- thermion_dart/test/helpers.dart | 259 ++++++------- thermion_dart/test/input_handlers.mocks.dart | 2 +- thermion_dart/test/material_tests.dart | 285 ++++++--------- thermion_dart/test/render_thread_tests.dart | 44 +++ thermion_dart/test/texture_tests.dart | 276 +++++++++----- thermion_dart/test/transform_tests.dart | 3 - thermion_dart/test/view_tests.dart | 250 ++++++------- 14 files changed, 1142 insertions(+), 900 deletions(-) create mode 100644 thermion_dart/test/assets/cube.bin create mode 100644 thermion_dart/test/assets/cube.gltf create mode 100644 thermion_dart/test/background_tests.dart create mode 100644 thermion_dart/test/render_thread_tests.dart diff --git a/thermion_dart/test/asset_tests.dart b/thermion_dart/test/asset_tests.dart index 3dfb57d5..270bf541 100644 --- a/thermion_dart/test/asset_tests.dart +++ b/thermion_dart/test/asset_tests.dart @@ -7,21 +7,21 @@ void main() async { final testHelper = TestHelper("assets"); await testHelper.setup(); group("assets", () { - // test('load/clear skybox', () async { - // await testHelper.withViewer((viewer) async { - // final camera = await viewer.getActiveCamera(); - // print(await camera.getModelMatrix()); - // print(await camera.getViewMatrix()); - // print(await camera.getProjectionMatrix()); - // await camera.lookAt(Vector3(0, 0, 10), - // focus: Vector3.zero(), up: Vector3(0, 1, 0)); - // await viewer.loadSkybox( - // "file://${testHelper.testDir}/assets/default_env_skybox.ktx"); - // await testHelper.capture(viewer.view, "load_skybox"); - // await viewer.removeSkybox(); - // await testHelper.capture(viewer.view, "remove_skybox"); - // }); - // }); + test('load/clear skybox', () async { + await testHelper.withViewer((viewer) async { + final camera = await viewer.getActiveCamera(); + print(await camera.getModelMatrix()); + print(await camera.getViewMatrix()); + print(await camera.getProjectionMatrix()); + await camera.lookAt(Vector3(0, 0, 10), + focus: Vector3.zero(), up: Vector3(0, 1, 0)); + await viewer.loadSkybox( + "file://${testHelper.testDir}/assets/default_env_skybox.ktx"); + await testHelper.capture(viewer.view, "load_skybox"); + await viewer.removeSkybox(); + await testHelper.capture(viewer.view, "remove_skybox"); + }); + }); test('load/remove ibl', () async { await testHelper.withViewer((viewer) async { @@ -35,39 +35,41 @@ void main() async { }, cameraPosition: Vector3(0, 0, 5)); }); - // test('add/remove asset from scene ', () async { - // await testHelper.withViewer((viewer) async { - // var asset = await viewer - // .loadGlb("file://${testHelper.testDir}/assets/cube.glb"); - // await viewer - // .loadIbl("file://${testHelper.testDir}/assets/default_env_ibl.ktx"); - // await testHelper.capture(viewer.view, "asset_added"); - // await viewer.removeFromScene(asset); - // await testHelper.capture(viewer.view, "asset_removed"); - // }, cameraPosition: Vector3(0, 0, 5)); - // }); + test('load/remove gltf', () async { + await testHelper.withViewer((viewer) async { + var asset = await viewer + .loadGlb("file://${testHelper.testDir}/assets/cube.gltf", relativeResourcePath: "${testHelper.testDir}/assets"); + await viewer + .loadIbl("file://${testHelper.testDir}/assets/default_env_ibl.ktx"); + await testHelper.capture(viewer.view, "gltf_loaded"); + await viewer.removeFromScene(asset); + await testHelper.capture(viewer.view, "gltf_removed"); + }, cameraPosition: Vector3(0, 0, 5)); + }); - // test('destroy assets', () async { - // await testHelper.withViewer((viewer) async { - // var asset = await viewer - // .loadGlb("file://${testHelper.testDir}/assets/cube.glb"); - // await viewer - // .loadIbl("file://${testHelper.testDir}/assets/default_env_ibl.ktx"); - // await testHelper.capture(viewer.view, "assets_present"); - // await viewer.destroyAssets(); - // await testHelper.capture(viewer.view, "assets_destroyed"); - // }, cameraPosition: Vector3(0, 0, 5)); - // }); + test('add/remove asset from scene ', () async { + await testHelper.withViewer((viewer) async { + var asset = await viewer + .loadGlb("file://${testHelper.testDir}/assets/cube.glb"); + await viewer + .loadIbl("file://${testHelper.testDir}/assets/default_env_ibl.ktx"); + await testHelper.capture(viewer.view, "asset_added"); + await viewer.removeFromScene(asset); + await testHelper.capture(viewer.view, "asset_removed"); + }, cameraPosition: Vector3(0, 0, 5)); + }); + test('destroy assets', () async { + await testHelper.withViewer((viewer) async { + var asset = await viewer + .loadGlb("file://${testHelper.testDir}/assets/cube.glb"); + await viewer + .loadIbl("file://${testHelper.testDir}/assets/default_env_ibl.ktx"); + await testHelper.capture(viewer.view, "assets_present"); + await viewer.destroyAssets(); + await testHelper.capture(viewer.view, "assets_destroyed"); + }, cameraPosition: Vector3(0, 0, 5)); + }); - - - // await viewer.destroyLights(); - // await viewer.removeSkybox(); - // await viewer.removeIbl(); - - // asset = await viewer - // .loadGlb("file://${testHelper.testDir}/assets/cube.glb"); - // await testHelper.capture(viewer.view, "asset_reloaded"); }); } diff --git a/thermion_dart/test/assets/cube.bin b/thermion_dart/test/assets/cube.bin new file mode 100644 index 0000000000000000000000000000000000000000..29df9d975689d89810aab5225917fb635df0774c GIT binary patch literal 840 zcmaJ-TMmLS5FC6WDk#2wJ&E-w9HlqYqd6np1U7{x+u3q^hF3jvmN1l%U*ARP_G|L#K>?q^Dx4vv&`I9u$nLCd?v4G&yV^R98m2`f~*E8MZh0wwPfdo(z(u;DE61Gt!b{Qv*} literal 0 HcmV?d00001 diff --git a/thermion_dart/test/assets/cube.gltf b/thermion_dart/test/assets/cube.gltf new file mode 100644 index 00000000..b0e0d43c --- /dev/null +++ b/thermion_dart/test/assets/cube.gltf @@ -0,0 +1,121 @@ +{ + "asset":{ + "generator":"Khronos glTF Blender I/O v4.2.60", + "version":"2.0" + }, + "scene":0, + "scenes":[ + { + "name":"Scene", + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0, + "name":"Cube" + } + ], + "materials":[ + { + "doubleSided":true, + "name":"Material", + "pbrMetallicRoughness":{ + "baseColorFactor":[ + 0.800000011920929, + 0.800000011920929, + 0.800000011920929, + 1 + ], + "metallicFactor":0, + "roughnessFactor":0.5 + } + } + ], + "meshes":[ + { + "name":"Cube", + "primitives":[ + { + "attributes":{ + "POSITION":0, + "NORMAL":1, + "TEXCOORD_0":2 + }, + "indices":3, + "material":0 + } + ] + } + ], + "accessors":[ + { + "bufferView":0, + "componentType":5126, + "count":24, + "max":[ + 1, + 1, + 1 + ], + "min":[ + -1, + -1, + -1 + ], + "type":"VEC3" + }, + { + "bufferView":1, + "componentType":5126, + "count":24, + "type":"VEC3" + }, + { + "bufferView":2, + "componentType":5126, + "count":24, + "type":"VEC2" + }, + { + "bufferView":3, + "componentType":5123, + "count":36, + "type":"SCALAR" + } + ], + "bufferViews":[ + { + "buffer":0, + "byteLength":288, + "byteOffset":0, + "target":34962 + }, + { + "buffer":0, + "byteLength":288, + "byteOffset":288, + "target":34962 + }, + { + "buffer":0, + "byteLength":192, + "byteOffset":576, + "target":34962 + }, + { + "buffer":0, + "byteLength":72, + "byteOffset":768, + "target":34963 + } + ], + "buffers":[ + { + "byteLength":840, + "uri":"cube.bin" + } + ] +} diff --git a/thermion_dart/test/background_tests.dart b/thermion_dart/test/background_tests.dart new file mode 100644 index 00000000..d884c0e6 --- /dev/null +++ b/thermion_dart/test/background_tests.dart @@ -0,0 +1,24 @@ +import 'package:test/test.dart'; +import 'helpers.dart'; + +void main() async { + final testHelper = TestHelper("assets"); + await testHelper.setup(); + group("background", () { + test('set background color', () async { + await testHelper.withViewer((viewer) async { + await viewer.setBackgroundColor(0, 1, 0, 1); + await testHelper.capture(viewer.view, "background_green"); + await viewer.setBackgroundColor(1, 0, 0, 1); + await testHelper.capture(viewer.view, "background_red"); + }); + }); + + test('set background image', () async { + await testHelper.withViewer((viewer) async { + await viewer.setBackgroundImage("file://${testHelper.testDir}/assets/cube_texture2_512x512.png"); + await testHelper.capture(viewer.view, "background_image"); + }); + }); + }); +} diff --git a/thermion_dart/test/camera_tests.dart b/thermion_dart/test/camera_tests.dart index 04db60f7..5e41a41f 100644 --- a/thermion_dart/test/camera_tests.dart +++ b/thermion_dart/test/camera_tests.dart @@ -1,240 +1,235 @@ // ignore_for_file: unused_local_variable import 'dart:math'; - import 'package:thermion_dart/thermion_dart.dart'; import 'package:test/test.dart'; -import 'package:vector_math/vector_math_64.dart'; import 'helpers.dart'; void main() async { - final testHelper = TestHelper("integration"); - + final testHelper = TestHelper("camera"); + await testHelper.setup(); group('camera', () { test('model matrix', () async { await testHelper.withViewer((viewer) async { - var matrix = await viewer.getCameraModelMatrix(); - expect(matrix.trace(), 4); - - await viewer.setCameraPosition(2.0, 2.0, 2.0); - matrix = await viewer.getCameraModelMatrix(); - var position = matrix.getColumn(3).xyz; + final camera = await viewer.getActiveCamera(); + await camera.setModelMatrix(Matrix4.translation(Vector3.all(4.0))); + var matrix = await camera.getModelMatrix(); + + await camera.lookAt(Vector3(2.0, 2.0, 2.0)); + matrix = await camera.getModelMatrix(); + var position = await camera.getPosition(); expect(position.x, 2.0); expect(position.y, 2.0); expect(position.z, 2.0); - position = await viewer.getCameraPosition(); - expect(position.x, 2.0); - expect(position.y, 2.0); - expect(position.z, 2.0); }); }); - test('getCameraViewMatrix', () async { - await testHelper.withViewer((viewer) async { - await viewer.setCameraModelMatrix4(Matrix4.identity()); - var modelMatrix = await viewer.getCameraModelMatrix(); - var viewMatrix = await viewer.getCameraViewMatrix(); + // test('getCameraViewMatrix', () async { + // await testHelper.withViewer((viewer) async { + // await viewer.setCameraModelMatrix4(Matrix4.identity()); + // var modelMatrix = await camera.getModelMatrix(); + // var viewMatrix = await viewer.getCameraViewMatrix(); - // The view matrix should be the inverse of the model matrix - var identity = modelMatrix * viewMatrix; - expect(identity.isIdentity(), isTrue); - var camera = await viewer.getMainCamera(); - identity = modelMatrix * (await camera.getViewMatrix()); - expect(identity.isIdentity(), isTrue); + // // The view matrix should be the inverse of the model matrix + // var identity = modelMatrix * viewMatrix; + // expect(identity.isIdentity(), isTrue); + // var camera = await viewer.getMainCamera(); + // identity = modelMatrix * (await camera.getViewMatrix()); + // expect(identity.isIdentity(), isTrue); - // Check that moving the camera affects the view matrix - await viewer.setCameraPosition(3.0, 4.0, 5.0); - viewMatrix = await viewer.getCameraViewMatrix(); - var invertedView = viewMatrix.clone()..invert(); - var position = invertedView.getColumn(3).xyz; - expect(position.x, closeTo(3.0, 1e-6)); - expect(position.y, closeTo(4.0, 1e-6)); - expect(position.z, closeTo(5.0, 1e-6)); - }); - }); + // // Check that moving the camera affects the view matrix + // await viewer.setCameraPosition(3.0, 4.0, 5.0); + // viewMatrix = await viewer.getCameraViewMatrix(); + // var invertedView = viewMatrix.clone()..invert(); + // var position = invertedView.getColumn(3).xyz; + // expect(position.x, closeTo(3.0, 1e-6)); + // expect(position.y, closeTo(4.0, 1e-6)); + // expect(position.z, closeTo(5.0, 1e-6)); + // }); + // }); - test('getCameraProjectionMatrix', () async { - await testHelper.withViewer((viewer) async { - var projectionMatrix = await viewer.getCameraProjectionMatrix(); - print(projectionMatrix); - }); - }); + // test('getCameraProjectionMatrix', () async { + // await testHelper.withViewer((viewer) async { + // var projectionMatrix = await viewer.getCameraProjectionMatrix(); + // 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 { + // // ignore: dead_code + // var viewer = await testHelper.createViewer(); + // var matrix = await viewer.getCameraCullingProjectionMatrix(); + // 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 frustum = await viewer.getCameraFrustum(); + // print(frustum.plane5.normal); + // print(frustum.plane5.constant); - var camera = await viewer.getMainCamera(); + // var camera = await viewer.getMainCamera(); - 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 viewer.getCameraFrustum(); + // 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.getMainCamera(); + // 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, "camera_set_orthographic_projection"); - }); - }); + // await camera.setProjection( + // Projection.Orthographic, -0.05, 0.05, -0.05, 0.05, 0.05, 10000); + // await testHelper.capture(viewer, "camera_set_orthographic_projection"); + // }); + // }); - test('set perspective projection/culling matrix', () async { - await testHelper.withViewer((viewer) async { - var camera = await viewer.getMainCamera(); - final cube = await viewer.createGeometry(GeometryHelper.cube()); + // test('set perspective projection/culling matrix', () async { + // await testHelper.withViewer((viewer) async { + // var camera = await viewer.getMainCamera(); + // final cube = await viewer.createGeometry(GeometryHelper.cube()); - var fovY = pi / 2; - await camera.setProjectionMatrixWithCulling( - makePerspectiveMatrix(fovY, 1.0, 0.05, 10000), 0.05, 10000); + // var fovY = pi / 2; + // await camera.setProjectionMatrixWithCulling( + // makePerspectiveMatrix(fovY, 1.0, 0.05, 10000), 0.05, 10000); - await testHelper.capture(viewer, - "camera_set_perspective_projection_culling_matrix_object_fov90"); + // await testHelper.capture(viewer, + // "camera_set_perspective_projection_culling_matrix_object_fov90"); - // cube no longer visible when the far plane is moved closer to camera so cube is outside - fovY = 2 * (pi / 3); - await camera.setProjectionMatrixWithCulling( - makePerspectiveMatrix(fovY, 1.0, 0.05, 10000), 0.05, 10000); + // // cube no longer visible when the far plane is moved closer to camera so cube is outside + // fovY = 2 * (pi / 3); + // await camera.setProjectionMatrixWithCulling( + // makePerspectiveMatrix(fovY, 1.0, 0.05, 10000), 0.05, 10000); - await testHelper.capture(viewer, - "camera_set_perspective_projection_culling_matrix_object_fov120"); - }); - }); + // await testHelper.capture(viewer, + // "camera_set_perspective_projection_culling_matrix_object_fov120"); + // }); + // }); - test('set custom projection/culling matrix (orthographic)', () async { - await testHelper.withViewer((viewer) async { - var camera = await viewer.getMainCamera(); - final cube = await viewer.createGeometry(GeometryHelper.cube()); + // test('set custom projection/culling matrix (orthographic)', () async { + // await testHelper.withViewer((viewer) async { + // var camera = await viewer.getMainCamera(); + // final cube = await viewer.createGeometry(GeometryHelper.cube()); - // cube is visible when inside the frustum, cube is visible - var projectionMatrix = - makeOrthographicMatrix(-10.0, 10.0, -10.0, 10.0, 0.05, 10000); - await camera.setProjectionMatrixWithCulling( - projectionMatrix, 0.05, 10000); + // // cube is visible when inside the frustum, cube is visible + // var projectionMatrix = + // makeOrthographicMatrix(-10.0, 10.0, -10.0, 10.0, 0.05, 10000); + // await camera.setProjectionMatrixWithCulling( + // projectionMatrix, 0.05, 10000); - await testHelper.capture( - viewer, "camera_projection_culling_matrix_object_in_frustum"); + // await testHelper.capture( + // viewer, "camera_projection_culling_matrix_object_in_frustum"); - // cube no longer visible when the far plane is moved closer to camera so cube is outside - projectionMatrix = - makeOrthographicMatrix(-10.0, 10.0, -10.0, 10.0, 0.05, 1); - await camera.setProjectionMatrixWithCulling(projectionMatrix, 0.05, 1); - await testHelper.capture( - viewer, "camera_projection_culling_matrix_object_outside_frustum"); - }); - }); + // // cube no longer visible when the far plane is moved closer to camera so cube is outside + // projectionMatrix = + // makeOrthographicMatrix(-10.0, 10.0, -10.0, 10.0, 0.05, 1); + // await camera.setProjectionMatrixWithCulling(projectionMatrix, 0.05, 1); + // await testHelper.capture( + // viewer, "camera_projection_culling_matrix_object_outside_frustum"); + // }); + // }); - test('setting transform on camera updates model matrix (no parent)', - () async { - await testHelper.withViewer((viewer) async { - var cameraEntity = await viewer.getMainCameraEntity(); - var camera = await viewer.getMainCamera(); + // test('setting transform on camera updates model matrix (no parent)', + // () async { + // await testHelper.withViewer((viewer) async { + // var cameraEntity = await viewer.getMainCameraEntity(); + // var camera = await viewer.getMainCamera(); - await viewer.setTransform( - cameraEntity, Matrix4.translation(Vector3.all(1))); + // await viewer.setTransform( + // cameraEntity, Matrix4.translation(Vector3.all(1))); - var modelMatrix = await viewer.getCameraModelMatrix(); - expect(modelMatrix.getColumn(3).x, 1.0); - expect(modelMatrix.getColumn(3).y, 1.0); - expect(modelMatrix.getColumn(3).z, 1.0); - expect(modelMatrix.getColumn(3).w, 1.0); - }); - }); + // var modelMatrix = await camera.getModelMatrix(); + // expect(modelMatrix.getColumn(3).x, 1.0); + // expect(modelMatrix.getColumn(3).y, 1.0); + // expect(modelMatrix.getColumn(3).z, 1.0); + // expect(modelMatrix.getColumn(3).w, 1.0); + // }); + // }); - test('setting transform on camera updates model matrix (with parent)', - () async { - await testHelper.withViewer((viewer) async { - var cameraEntity = await viewer.getMainCameraEntity(); - var camera = await viewer.getMainCamera(); + // test('setting transform on camera updates model matrix (with parent)', + // () async { + // await testHelper.withViewer((viewer) async { + // var cameraEntity = await viewer.getMainCameraEntity(); + // var camera = await viewer.getMainCamera(); - var parent = await viewer.createGeometry(GeometryHelper.cube()); + // var parent = await viewer.createGeometry(GeometryHelper.cube()); - await viewer.setParent(camera.getEntity(), parent.entity); - await viewer.setTransform( - cameraEntity, Matrix4.translation(Vector3(1, 0, 0))); + // await viewer.setParent(camera.getEntity(), parent.entity); + // await viewer.setTransform( + // cameraEntity, Matrix4.translation(Vector3(1, 0, 0))); - var modelMatrix = await viewer.getCameraModelMatrix(); - expect(modelMatrix.getColumn(3).x, 1.0); - expect(modelMatrix.getColumn(3).y, 0.0); - expect(modelMatrix.getColumn(3).z, 0.0); - expect(modelMatrix.getColumn(3).w, 1.0); + // var modelMatrix = await camera.getModelMatrix(); + // expect(modelMatrix.getColumn(3).x, 1.0); + // expect(modelMatrix.getColumn(3).y, 0.0); + // expect(modelMatrix.getColumn(3).z, 0.0); + // expect(modelMatrix.getColumn(3).w, 1.0); - await viewer.setTransform( - parent.entity, Matrix4.translation(Vector3(0, 1, 0))); - modelMatrix = await viewer.getCameraModelMatrix(); - expect(modelMatrix.getColumn(3).x, 1.0); - expect(modelMatrix.getColumn(3).y, 1.0); - expect(modelMatrix.getColumn(3).z, 0.0); - expect(modelMatrix.getColumn(3).w, 1.0); - }); - }); + // await viewer.setTransform( + // parent.entity, Matrix4.translation(Vector3(0, 1, 0))); + // modelMatrix = await camera.getModelMatrix(); + // expect(modelMatrix.getColumn(3).x, 1.0); + // expect(modelMatrix.getColumn(3).y, 1.0); + // expect(modelMatrix.getColumn(3).z, 0.0); + // expect(modelMatrix.getColumn(3).w, 1.0); + // }); + // }); - test( - 'when a camera is the parent of another entity, setting the model matrix updates the parent transform ', - () async { - await testHelper.withViewer((viewer) async { - var camera = await viewer.createCamera(); + // test( + // 'when a camera is the parent of another entity, setting the model matrix updates the parent transform ', + // () async { + // await testHelper.withViewer((viewer) async { + // var camera = await viewer.createCamera(); - var child = await viewer - .createGeometry(GeometryHelper.cube(normals: false, uvs: false)); - await viewer.setParent(child.entity, camera.getEntity()); + // var child = await viewer + // .createGeometry(GeometryHelper.cube(normals: false, uvs: false)); + // await viewer.setParent(child.entity, camera.getEntity()); - await testHelper.capture(viewer, "camera_as_parent1"); + // await testHelper.capture(viewer, "camera_as_parent1"); - await camera.setModelMatrix(Matrix4.translation(Vector3(1, 0, 0))); + // await camera.setModelMatrix(Matrix4.translation(Vector3(1, 0, 0))); - await testHelper.capture(viewer, "camera_as_parent2"); - }, bg: kRed, cameraPosition: Vector3(0, 0, 10)); - }); + // await testHelper.capture(viewer, "camera_as_parent2"); + // }, bg: kRed, cameraPosition: Vector3(0, 0, 10)); + // }); - test('create camera', () async { - await testHelper.withViewer((viewer) async { - await viewer.setCameraPosition(0, 0, 5); - await viewer.setBackgroundColor(1.0, 0.0, 1.0, 1.0); - await viewer - .createGeometry(GeometryHelper.cube(normals: false, uvs: false)); - await testHelper.capture(viewer, "create_camera_main_camera"); + // test('create camera', () async { + // await testHelper.withViewer((viewer) async { + // await viewer.setCameraPosition(0, 0, 5); + // await viewer.setBackgroundColor(1.0, 0.0, 1.0, 1.0); + // await viewer + // .createGeometry(GeometryHelper.cube(normals: false, uvs: false)); + // await testHelper.capture(viewer, "create_camera_main_camera"); - expect(viewer.getCameraCount(), 1); - var newCamera = await viewer.createCamera(); - expect(viewer.getCameraCount(), 2); - await newCamera.setTransform(Matrix4.translation(Vector3(0, 0, 4))); - newCamera.setLensProjection(); - await viewer.setActiveCamera(newCamera); + // expect(viewer.getCameraCount(), 1); + // var newCamera = await viewer.createCamera(); + // expect(viewer.getCameraCount(), 2); + // await newCamera.setTransform(Matrix4.translation(Vector3(0, 0, 4))); + // newCamera.setLensProjection(); + // await viewer.setActiveCamera(newCamera); - expect(await viewer.getActiveCamera(), newCamera); + // expect(await viewer.getActiveCamera(), newCamera); - await testHelper.capture(viewer, "create_camera_new_camera"); + // await testHelper.capture(viewer, "create_camera_new_camera"); - final mainCamera = await viewer.getMainCamera(); - await viewer.setActiveCamera(mainCamera); - expect(await viewer.getActiveCamera(), mainCamera); - await testHelper.capture(viewer, "create_camera_back_to_main"); + // final mainCamera = await viewer.getMainCamera(); + // await viewer.setActiveCamera(mainCamera); + // expect(await viewer.getActiveCamera(), mainCamera); + // await testHelper.capture(viewer, "create_camera_back_to_main"); - expect(viewer.getCameraCount(), 2); - expect(viewer.getCameraAt(0), await viewer.getMainCamera()); - expect(viewer.getCameraAt(1), newCamera); - await expectLater( - () => viewer.getCameraAt(2), throwsA(isA())); - }); - }); + // expect(viewer.getCameraCount(), 2); + // expect(viewer.getCameraAt(0), await viewer.getMainCamera()); + // expect(viewer.getCameraAt(1), newCamera); + // await expectLater( + // () => viewer.getCameraAt(2), throwsA(isA())); + // }); + // }); }); } diff --git a/thermion_dart/test/geometry_tests.dart b/thermion_dart/test/geometry_tests.dart index 7bfdbfb9..4dbf44b6 100644 --- a/thermion_dart/test/geometry_tests.dart +++ b/thermion_dart/test/geometry_tests.dart @@ -1,18 +1,21 @@ // ignore_for_file: unused_local_variable - import 'dart:io'; import 'dart:math'; -import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_viewer_ffi.dart'; import 'package:thermion_dart/thermion_dart.dart'; import 'package:test/test.dart'; - import 'package:vector_math/vector_math_64.dart'; - import 'helpers.dart'; void main() async { final testHelper = TestHelper("geometry"); + await testHelper.setup(); group("custom geometry", () { + test('create cube (uvs only)', () async { + var viewer = await testHelper.createViewer(); + await viewer + .createGeometry(GeometryHelper.cube(normals: false, uvs: true)); + await testHelper.capture(viewer, "geometry_cube_with_uvs"); + }); test('create cube with normals & uvs', () async { var viewer = await testHelper.createViewer(); await viewer @@ -185,15 +188,18 @@ void main() async { final cube = await viewer.createGeometry( GeometryHelper.cube(uvs: true, normals: true), materialInstances: [materialInstance]); - var textureData = + final image = await viewer.decodeImage( File("${testHelper.testDir}/assets/cube_texture_512x512.png") - .readAsBytesSync(); - var texture = await viewer.createTexture(textureData); - await viewer.applyTexture(texture as ThermionFFITexture, cube.entity); + .readAsBytesSync()); + var texture = await viewer.createTexture( + await image.getWidth(), await image.getHeight()); + await texture.setLinearImage( + image, PixelDataFormat.RGBA, PixelDataType.FLOAT); await testHelper.capture( viewer, "geometry_cube_with_custom_material_ubershader_texture"); await viewer.destroyAsset(cube); - await viewer.destroyTexture(texture); + await texture.dispose(); + await image.destroy(); }); test('unlit material with color only', () async { @@ -216,11 +222,14 @@ void main() async { materialInstances: [materialInstance]); await materialInstance.setParameterInt("baseColorIndex", 0); - var textureData = + + final image = await viewer.decodeImage( File("${testHelper.testDir}/assets/cube_texture_512x512.png") - .readAsBytesSync(); - var texture = await viewer.createTexture(textureData); - await viewer.applyTexture(texture, cube.entity); + .readAsBytesSync()); + var texture = await viewer.createTexture( + await image.getWidth(), await image.getHeight()); + await texture.setLinearImage( + image, PixelDataFormat.RGBA, PixelDataType.FLOAT); await testHelper.capture(viewer, "unlit_material_texture_only"); await viewer.destroyAsset(cube); }); @@ -237,24 +246,26 @@ void main() async { cube2.entity, Matrix4.translation(Vector3(1, 1, 1))); await materialInstance.setParameterInt("baseColorIndex", 0); - var textureData = + final image = await viewer.decodeImage( File("${testHelper.testDir}/assets/cube_texture_512x512.png") - .readAsBytesSync(); - var texture = await viewer.createTexture(textureData); - await viewer.applyTexture(texture, cube1.entity); - await viewer.applyTexture(texture, cube2.entity); + .readAsBytesSync()); + var texture = await viewer.createTexture( + await image.getWidth(), await image.getHeight()); + await texture.setLinearImage( + image, PixelDataFormat.RGBA, PixelDataType.FLOAT); + await testHelper.capture(viewer, "unlit_material_shared"); - await viewer.destroyTexture(texture); + await texture.dispose(); + await image.destroy(); }); }); test('create sphere (no normals)', () async { - var viewer = await testHelper.createViewer(); - await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0); - await viewer.setCameraPosition(0, 0, 6); - await viewer - .createGeometry(GeometryHelper.sphere(normals: false, uvs: false)); - await testHelper.capture(viewer, "geometry_sphere_no_normals"); + await testHelper.withViewer((viewer) async { + await viewer + .createGeometry(GeometryHelper.sphere(normals: false, uvs: false)); + await testHelper.capture(viewer, "geometry_sphere_no_normals"); + }, bg: kBlue, cameraPosition: Vector3(0, 0, 6)); }); test('create multiple (non-instanced) geometry', () async { diff --git a/thermion_dart/test/gltf_tests.dart b/thermion_dart/test/gltf_tests.dart index e3566b57..f1002420 100644 --- a/thermion_dart/test/gltf_tests.dart +++ b/thermion_dart/test/gltf_tests.dart @@ -1,153 +1,153 @@ -// ignore_for_file: unused_local_variable +// // ignore_for_file: unused_local_variable -import 'dart:io'; -import 'package:thermion_dart/thermion_dart.dart'; -import 'package:test/test.dart'; +// import 'dart:io'; +// import 'package:thermion_dart/thermion_dart.dart'; +// import 'package:test/test.dart'; -import 'package:vector_math/vector_math_64.dart'; +// import 'package:vector_math/vector_math_64.dart'; -import 'helpers.dart'; +// import 'helpers.dart'; -void main() async { - final testHelper = TestHelper("gltf"); - group("gltf", () { - test('load glb from file', () async { - await testHelper.withViewer((viewer) async { - var model = await viewer - .loadGlb("file://${testHelper.testDir}/assets/cube.glb"); - await testHelper.capture(viewer, "load_glb_from_file"); - await viewer.destroyAsset(model); - }); - }); +// void main() async { +// final testHelper = TestHelper("gltf"); +// group("gltf", () { +// test('load glb from file', () async { +// await testHelper.withViewer((viewer) async { +// var model = await viewer +// .loadGlb("file://${testHelper.testDir}/assets/cube.glb"); +// await testHelper.capture(viewer, "load_glb_from_file"); +// await viewer.destroyAsset(model); +// }); +// }); - test('load glb from buffer', () async { - await testHelper.withViewer((viewer) async { - var buffer = - File("${testHelper.testDir}/assets/cube.glb").readAsBytesSync(); - var model = await viewer.loadGlbFromBuffer(buffer); - await testHelper.capture(viewer, "load_glb_from_buffer"); - }); - }); +// test('load glb from buffer', () async { +// await testHelper.withViewer((viewer) async { +// var buffer = +// File("${testHelper.testDir}/assets/cube.glb").readAsBytesSync(); +// var model = await viewer.loadGlbFromBuffer(buffer); +// await testHelper.capture(viewer, "load_glb_from_buffer"); +// }); +// }); - test('load glb from buffer with instances', () async { - await testHelper.withViewer((viewer) async { - var buffer = - File("${testHelper.testDir}/assets/cube.glb").readAsBytesSync(); - var model = await viewer.loadGlbFromBuffer(buffer, numInstances: 2); - var instance = await model.createInstance(); - await instance.addToScene(); - await viewer.setTransform( - instance.entity, Matrix4.translation(Vector3(1, 0, 0))); +// test('load glb from buffer with instances', () async { +// await testHelper.withViewer((viewer) async { +// var buffer = +// File("${testHelper.testDir}/assets/cube.glb").readAsBytesSync(); +// var model = await viewer.loadGlbFromBuffer(buffer, numInstances: 2); +// var instance = await model.createInstance(); +// await instance.addToScene(); +// await viewer.setTransform( +// instance.entity, Matrix4.translation(Vector3(1, 0, 0))); - await testHelper.capture(viewer, "load_glb_from_buffer_with_instances"); +// await testHelper.capture(viewer, "load_glb_from_buffer_with_instances"); - await viewer.destroyAsset(instance); +// await viewer.destroyAsset(instance); - await testHelper.capture( - viewer, "load_glb_from_buffer_instance_removed"); +// await testHelper.capture( +// viewer, "load_glb_from_buffer_instance_removed"); - await viewer.destroyAsset(model); +// await viewer.destroyAsset(model); - await testHelper.capture( - viewer, "load_glb_from_buffer_original_removed"); - }, bg: kRed); - }); +// await testHelper.capture( +// viewer, "load_glb_from_buffer_original_removed"); +// }, bg: kRed); +// }); - test('load glb from buffer with priority', () async { - await testHelper.withViewer((viewer) async { - viewer.addDirectLight(DirectLight.sun()); - var buffer = - File("${testHelper.testDir}/assets/cube.glb").readAsBytesSync(); +// test('load glb from buffer with priority', () async { +// await testHelper.withViewer((viewer) async { +// viewer.addDirectLight(DirectLight.sun()); +// var buffer = +// File("${testHelper.testDir}/assets/cube.glb").readAsBytesSync(); - // priority 0 gets drawn first - var greenModel = await viewer.loadGlbFromBuffer(buffer, priority: 0); - for (final entity in await viewer.getChildEntities(greenModel)) { - final material = await viewer.getMaterialInstanceAt(entity, 0); - await material!.setParameterFloat4("baseColorFactor", 0, 1, 0.0, 1.0); - } +// // priority 0 gets drawn first +// var greenModel = await viewer.loadGlbFromBuffer(buffer, priority: 0); +// for (final entity in await viewer.getChildEntities(greenModel)) { +// final material = await viewer.getMaterialInstanceAt(entity, 0); +// await material!.setParameterFloat4("baseColorFactor", 0, 1, 0.0, 1.0); +// } - // priority 7 gets drawn last - var blueModel = await viewer.loadGlbFromBuffer(buffer, priority: 7); - for (final entity in await viewer.getChildEntities(blueModel)) { - final material = await viewer.getMaterialInstanceAt(entity, 0); - await material!.setParameterFloat4("baseColorFactor", 0, 0, 1.0, 1.0); - } +// // priority 7 gets drawn last +// var blueModel = await viewer.loadGlbFromBuffer(buffer, priority: 7); +// for (final entity in await viewer.getChildEntities(blueModel)) { +// final material = await viewer.getMaterialInstanceAt(entity, 0); +// await material!.setParameterFloat4("baseColorFactor", 0, 0, 1.0, 1.0); +// } - // blue model rendered in front - await testHelper.capture(viewer, "load_glb_from_buffer_with_priority"); - }); - }); +// // blue model rendered in front +// await testHelper.capture(viewer, "load_glb_from_buffer_with_priority"); +// }); +// }); - test('create instance from gltf', () async { - await testHelper.withViewer((viewer) async { - var model = await viewer.loadGlb( - "file://${testHelper.testDir}/assets/cube.glb", - numInstances: 32); - await testHelper.capture(viewer, "gltf_create_instance_0"); - var instance = await model.createInstance(); - await instance.addToScene(); - await viewer.setRendering(true); +// test('create instance from gltf', () async { +// await testHelper.withViewer((viewer) async { +// var model = await viewer.loadGlb( +// "file://${testHelper.testDir}/assets/cube.glb", +// numInstances: 32); +// await testHelper.capture(viewer, "gltf_create_instance_0"); +// var instance = await model.createInstance(); +// await instance.addToScene(); +// await viewer.setRendering(true); - await viewer.setTransform( - instance.entity, Matrix4.translation(Vector3.all(1))); - await testHelper.capture(viewer, "gltf_create_instance_1"); - }); - }); +// await viewer.setTransform( +// instance.entity, Matrix4.translation(Vector3.all(1))); +// await testHelper.capture(viewer, "gltf_create_instance_1"); +// }); +// }); - test('create instance from gltf with new material', () async { - await testHelper.withViewer((viewer) async { - var model = await viewer.loadGlb( - "file://${testHelper.testDir}/assets/cube.glb", - numInstances: 2); - await testHelper.capture( - viewer, "gltf_create_instance_with_material_0"); +// test('create instance from gltf with new material', () async { +// await testHelper.withViewer((viewer) async { +// var model = await viewer.loadGlb( +// "file://${testHelper.testDir}/assets/cube.glb", +// numInstances: 2); +// await testHelper.capture( +// viewer, "gltf_create_instance_with_material_0"); - final materialInstance = await viewer.createUnlitMaterialInstance(); - await materialInstance.setParameterFloat4( - "baseColorFactor", 1.0, 0.0, 0.0, 1.0); - var instance = - await model.createInstance(materialInstances: [materialInstance]); - await instance.addToScene(); +// final materialInstance = await viewer.createUnlitMaterialInstance(); +// await materialInstance.setParameterFloat4( +// "baseColorFactor", 1.0, 0.0, 0.0, 1.0); +// var instance = +// await model.createInstance(materialInstances: [materialInstance]); +// await instance.addToScene(); - await viewer.setTransform( - instance.entity, Matrix4.translation(Vector3.all(1))); - await testHelper.capture( - viewer, "gltf_create_instance_with_material_1"); - await viewer.destroyMaterialInstance(materialInstance); - }); - }); +// await viewer.setTransform( +// instance.entity, Matrix4.translation(Vector3.all(1))); +// await testHelper.capture( +// viewer, "gltf_create_instance_with_material_1"); +// await viewer.destroyMaterialInstance(materialInstance); +// }); +// }); - test('replace material instance with unlit material', () async { - await testHelper.withViewer((viewer) async { - var model = await viewer - .loadGlb("file://${testHelper.testDir}/assets/cube.glb"); - await testHelper.capture(viewer, "gltf_default_material_instance"); - var materialInstance = await viewer.createUnlitMaterialInstance(); - await materialInstance.setParameterFloat4( - "baseColorFactor", 1.0, 1.0, 0.0, 1.0); - await model.setMaterialInstanceAt(materialInstance); - await testHelper.capture( - viewer, "gltf_replace_material_instance_unlit"); - await viewer.destroyAsset(model); - await viewer.destroyMaterialInstance(materialInstance); - }); - }); +// test('replace material instance with unlit material', () async { +// await testHelper.withViewer((viewer) async { +// var model = await viewer +// .loadGlb("file://${testHelper.testDir}/assets/cube.glb"); +// await testHelper.capture(viewer, "gltf_default_material_instance"); +// var materialInstance = await viewer.createUnlitMaterialInstance(); +// await materialInstance.setParameterFloat4( +// "baseColorFactor", 1.0, 1.0, 0.0, 1.0); +// await model.setMaterialInstanceAt(materialInstance); +// await testHelper.capture( +// viewer, "gltf_replace_material_instance_unlit"); +// await viewer.destroyAsset(model); +// await viewer.destroyMaterialInstance(materialInstance); +// }); +// }); - test('replace material instance with ubershader material', () async { - await testHelper.withViewer((viewer) async { - var model = await viewer - .loadGlb("file://${testHelper.testDir}/assets/cube.glb"); - await viewer - .loadIbl("file://${testHelper.testDir}/assets/default_env_ibl.ktx"); - var materialInstance = await viewer.createUbershaderMaterialInstance(); - await materialInstance.setParameterFloat4( - "baseColorFactor", 1.0, 1.0, 0.0, 1.0); - await model.setMaterialInstanceAt(materialInstance); - await testHelper.capture( - viewer, "gltf_replace_material_instance_ubershader"); - await viewer.destroyAsset(model); - await viewer.destroyMaterialInstance(materialInstance); - }, bg: kRed); - }); - }); -} +// test('replace material instance with ubershader material', () async { +// await testHelper.withViewer((viewer) async { +// var model = await viewer +// .loadGlb("file://${testHelper.testDir}/assets/cube.glb"); +// await viewer +// .loadIbl("file://${testHelper.testDir}/assets/default_env_ibl.ktx"); +// var materialInstance = await viewer.createUbershaderMaterialInstance(); +// await materialInstance.setParameterFloat4( +// "baseColorFactor", 1.0, 1.0, 0.0, 1.0); +// await model.setMaterialInstanceAt(materialInstance); +// await testHelper.capture( +// viewer, "gltf_replace_material_instance_ubershader"); +// await viewer.destroyAsset(model); +// await viewer.destroyMaterialInstance(materialInstance); +// }, bg: kRed); +// }); +// }); +// } diff --git a/thermion_dart/test/helpers.dart b/thermion_dart/test/helpers.dart index 84935444..1e43e1ec 100644 --- a/thermion_dart/test/helpers.dart +++ b/thermion_dart/test/helpers.dart @@ -1,15 +1,17 @@ // ignore_for_file: unused_local_variable - import 'dart:ffi'; import 'dart:io'; -import 'dart:math'; - import 'package:image/image.dart' as img; import 'dart:typed_data'; import 'package:ffi/ffi.dart'; import 'package:image/image.dart'; -import 'swift/swift_bindings.g.dart'; +import 'package:thermion_dart/src/filament/src/layers.dart'; +import 'package:thermion_dart/src/swift/swift_bindings.g.dart'; import 'package:thermion_dart/src/utils/src/dart_resources.dart'; +import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart'; +import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart'; +import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_render_target.dart'; +import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_swapchain.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_dart.g.dart'; import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_viewer_ffi.dart'; import 'package:thermion_dart/src/viewer/src/ffi/thermion_viewer_ffi.dart'; @@ -32,7 +34,7 @@ Color kBlue = ColorFloat32(4)..setRgba(0.0, 0.0, 1.0, 1.0); Uri findPackageRoot(String packageName) { final script = Platform.script; final fileName = script.name; - if (fileName.endsWith('_test.dart')) { + if (fileName.contains('_test')) { // We're likely running from source. var directory = script.resolve('.'); while (true) { @@ -69,73 +71,88 @@ Future savePixelBufferToBmp( return data; } +Future savePixelBufferToPng( + Uint8List pixelBuffer, int width, int height, String outputPath) async { + var data = await pixelBufferToPng(pixelBuffer, width, height); + File(outputPath).writeAsBytesSync(data); + print("Wrote bitmap to ${outputPath}"); + return data; +} + class TestHelper { - late SwapChain swapChain; + late FFISwapChain swapChain; late Directory outDir; late String testDir; TestHelper(String dir) { - final packageUri = findPackageRoot('thermion_dart'); - print("Package URIL $packageUri"); - testDir = Directory("${packageUri.toFilePath()}/test").path; - print("Test dir : $packageUri"); - + final packageUri = findPackageRoot('thermion_dart').toFilePath(); + testDir = Directory("${packageUri}test").path; outDir = Directory("$testDir/output/${dir}"); - print("Out dir : $packageUri"); outDir.createSync(recursive: true); - print("Created out dir : $packageUri"); if (Platform.isMacOS) { DynamicLibrary.open('${testDir}/libThermionTextureSwift.dylib'); } } - Future capture(ThermionViewer viewer, String outputFilename, - {View? view, SwapChain? swapChain, RenderTarget? renderTarget}) async { - await Future.delayed(Duration(milliseconds: 10)); - var outPath = p.join(outDir.path, "$outputFilename.bmp"); - - var pixelBuffer = await viewer.capture( - view: view, - swapChain: swapChain ?? this.swapChain, - renderTarget: renderTarget); - await viewer.render(); - - view ??= await viewer.getViewAt(0); + /// + /// + /// + Future capture(View view, String? outputFilename) async { + final rt = await view.getRenderTarget(); + var pixelBuffer = await FilamentApp.instance! + .capture(view, captureRenderTarget: rt != null); var vp = await view.getViewport(); - await savePixelBufferToBmp(pixelBuffer, vp.width, vp.height, outPath); + + if (outputFilename != null) { + var outPath = p.join(outDir.path, "$outputFilename.png"); + await savePixelBufferToPng(pixelBuffer, vp.width, vp.height, outPath); + } + return pixelBuffer; } - Future createTexture(int width, int height) async { + /// + /// + /// + Future> captureMultiple( + ThermionViewer viewer, + String? outputFilename, { + View? view, + SwapChain? swapChain, + RenderTarget? renderTarget, + }) async { + throw UnimplementedError(); + + // view ??= await viewer.view; + // final targets = [ + // (view: view!, swapChain: swapChain, renderTarget: renderTarget) + // ]; + // var pixelBuffers = await viewer.capture(targets); + + // for (final entry in targets) { + // var vp = await entry.view.getViewport(); + // if (outputFilename != null) { + // var outPath = p.join(outDir.path, "$outputFilename.png"); + // await savePixelBufferToPng( + // pixelBuffers[targets.indexOf(entry)], vp.width, vp.height, outPath); + // } + // } + // return pixelBuffers; + } + + /// + /// + /// + Future createTexture(int width, int height, + {bool depth = false}) async { final object = ThermionTextureSwift.new1(); - object.initWithWidth_height_(width, height); + object.initWithWidth_height_isDepth_(width, height, depth); return object; } - Future withViewer(Future Function(ThermionViewer viewer) fn, - {img.Color? bg, - Vector3? cameraPosition, - viewportDimensions = (width: 500, height: 500), - bool postProcessing = false}) async { - var viewer = await createViewer( - bg: bg, - cameraPosition: cameraPosition, - viewportDimensions: viewportDimensions, - postProcessing: postProcessing); - - await fn.call(viewer); - await viewer.dispose(); - } - - Future createViewer( - {img.Color? bg, - Vector3? cameraPosition, - bool postProcessing = false, - viewportDimensions = (width: 500, height: 500)}) async { + Future setup() async { final resourceLoader = calloc(1); - cameraPosition ??= Vector3(0, 2, 6); - var loadToOut = NativeCallable< Void Function(Pointer, Pointer)>.listener(DartResourceLoader.loadResource); @@ -146,96 +163,88 @@ class TestHelper { DartResourceLoader.freeResource); resourceLoader.ref.freeResource = freeResource.nativeFunction; + await FFIFilamentApp.create(); - var viewer = ThermionViewerFFI(resourceLoader: resourceLoader.cast()); + await FilamentApp.instance!.setClearColor(0, 0, 0, 1); + } + + /// + /// + /// + Future withViewer(Future Function(ThermionViewer viewer) fn, + {img.Color? bg, + Vector3? cameraPosition, + ({int width, int height}) viewportDimensions = (width: 500, height: 500), + bool postProcessing = false, + bool createRenderTarget = false}) async { + cameraPosition ??= Vector3(0, 2, 6); + + var swapChain = await FilamentApp.instance!.createHeadlessSwapChain( + viewportDimensions.width, viewportDimensions.height) as FFISwapChain; + + FFIRenderTarget? renderTarget; + if (createRenderTarget) { + var metalColorTexture = await createTexture( + viewportDimensions.width, viewportDimensions.height); + var metalDepthTexture = await createTexture( + viewportDimensions.width, viewportDimensions.height, + depth: true); + var color = await FilamentApp.instance! + .createTexture(viewportDimensions.width, viewportDimensions.height, + flags: { + TextureUsage.TEXTURE_USAGE_BLIT_SRC, + TextureUsage.TEXTURE_USAGE_COLOR_ATTACHMENT, + TextureUsage.TEXTURE_USAGE_SAMPLEABLE + }, + textureFormat: TextureFormat.RGB32F, + importedTextureHandle: metalColorTexture.metalTextureAddress); + + var depth = await FilamentApp.instance! + .createTexture(viewportDimensions.width, viewportDimensions.height, + flags: { + TextureUsage.TEXTURE_USAGE_BLIT_SRC, + TextureUsage.TEXTURE_USAGE_DEPTH_ATTACHMENT, + TextureUsage.TEXTURE_USAGE_SAMPLEABLE, + }, + textureFormat: TextureFormat.DEPTH32F, + importedTextureHandle: metalDepthTexture.metalTextureAddress); + + renderTarget = await FilamentApp.instance!.createRenderTarget( + viewportDimensions.width, viewportDimensions.height, + color: color, depth: depth) as FFIRenderTarget; + } + + var viewer = ThermionViewerFFI( + loadAssetFromUri: (path) async => + File(path.replaceAll("file://", "")).readAsBytesSync(), + renderTarget: renderTarget); await viewer.initialized; - swapChain = await viewer.createHeadlessSwapChain( - viewportDimensions.width, viewportDimensions.height); + await FilamentApp.instance!.register(swapChain, viewer.view); - await viewer.updateViewportAndCameraProjection( - viewportDimensions.width.toDouble(), - viewportDimensions.height.toDouble()); + await viewer.view + .setViewport(viewportDimensions.width, viewportDimensions.height); + + await viewer.view.setBloom(false, 0); if (bg != null) { await viewer.setBackgroundColor( bg.r.toDouble(), bg.g.toDouble(), bg.b.toDouble(), bg.a.toDouble()); } - await viewer.setCameraPosition( - cameraPosition.x, cameraPosition.y, cameraPosition.z); + final camera = await viewer.getActiveCamera(); + + await camera.setLensProjection( + near: kNear, far: kFar, aspect: 1.0, focalLength: kFocalLength); + + await camera.lookAt(cameraPosition); await viewer.setPostProcessing(postProcessing); await viewer.setToneMapping(ToneMapper.LINEAR); - return viewer; - } -} - -Future pixelsToPng(Uint8List pixelBuffer, int width, int height, - {bool linearToSrgb = false, bool invertAces = false}) async { - final image = img.Image(width: width, height: height); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - final int pixelIndex = (y * width + x) * 4; - double r = pixelBuffer[pixelIndex] / 255.0; - double g = pixelBuffer[pixelIndex + 1] / 255.0; - double b = pixelBuffer[pixelIndex + 2] / 255.0; - int a = pixelBuffer[pixelIndex + 3]; - - // Apply inverse ACES tone mapping - if (invertAces) { - r = _inverseACESToneMapping(r); - g = _inverseACESToneMapping(g); - b = _inverseACESToneMapping(b); - } - - if (linearToSrgb) { - // Convert from linear to sRGB - - image.setPixel( - x, - y, - img.ColorUint8(4) - ..setRgba( - _linearToSRGB(r), _linearToSRGB(g), _linearToSRGB(b), 1.0)); - } else { - image.setPixel( - x, - y, - img.ColorUint8(4) - ..setRgba((r * 255).toInt(), (g * 255).toInt(), (b * 255).toInt(), - 1.0)); - } - } - } - - return img.encodePng(image); -} - -double _inverseACESToneMapping(double x) { - const double a = 2.51; - const double b = 0.03; - const double c = 2.43; - const double d = 0.59; - const double e = 0.14; - - // Ensure x is in the valid range [0, 1] - x = x.clamp(0.0, 1.0); - - // Inverse ACES filmic tone mapping function - return (x * (x * a + b)) / (x * (x * c + d) + e); -} - -int _linearToSRGB(double linearValue) { - if (linearValue <= 0.0031308) { - return (linearValue * 12.92 * 255.0).round().clamp(0, 255); - } else { - return ((1.055 * pow(linearValue, 1.0 / 2.4) - 0.055) * 255.0) - .round() - .clamp(0, 255); + await fn.call(viewer); + await viewer.dispose(); } } diff --git a/thermion_dart/test/input_handlers.mocks.dart b/thermion_dart/test/input_handlers.mocks.dart index ce0cd1f4..e8c81563 100644 --- a/thermion_dart/test/input_handlers.mocks.dart +++ b/thermion_dart/test/input_handlers.mocks.dart @@ -10,7 +10,7 @@ import 'package:animation_tools_dart/animation_tools_dart.dart' as _i9; import 'package:mockito/mockito.dart' as _i1; import 'package:thermion_dart/src/utils/src/gizmo.dart' as _i4; import 'package:thermion_dart/src/viewer/src/events.dart' as _i7; -import 'package:thermion_dart/src/viewer/src/shared_types/shared_types.dart' +import 'package:thermion_dart/src/filament/src/shared_types.dart' as _i2; import 'package:thermion_dart/src/viewer/src/thermion_viewer_base.dart' as _i5; import 'package:vector_math/vector_math_64.dart' as _i3; diff --git a/thermion_dart/test/material_tests.dart b/thermion_dart/test/material_tests.dart index 5b6b57e0..306dfd3d 100644 --- a/thermion_dart/test/material_tests.dart +++ b/thermion_dart/test/material_tests.dart @@ -1,12 +1,9 @@ import 'dart:io'; import 'dart:math'; import 'dart:typed_data'; -import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_viewer_ffi.dart'; import 'package:thermion_dart/thermion_dart.dart'; import 'package:test/test.dart'; - import 'package:vector_math/vector_math_64.dart'; - import 'helpers.dart'; Future< @@ -43,8 +40,8 @@ Future< void main() async { final testHelper = TestHelper("material"); - group("unlit material tests", () { - test('unlit material with color only', () async { + group("unlit material", () { + test('unlit + baseColorFactor', () async { await testHelper.withViewer((viewer) async { await viewer.setPostProcessing(true); await viewer.setToneMapping(ToneMapper.LINEAR); @@ -62,6 +59,116 @@ void main() async { }, bg: kRed); }); + test('unlit + baseColorMap', () async { + await testHelper.withViewer((viewer) async { + var materialInstance = await viewer.createUnlitMaterialInstance(); + var cube = await viewer.createGeometry(GeometryHelper.cube(), + materialInstances: [materialInstance]); + + await materialInstance.setParameterFloat4( + "baseColorFactor", 1.0, 1.0, 1.0, 1.0); + await materialInstance.setParameterFloat2("uvScale", 1.0, 1.0); + await materialInstance.setParameterInt("baseColorIndex", 0); + + var data = + File("${testHelper.testDir}/assets/cube_texture2_512x512.png") + .readAsBytesSync(); + final image = await viewer.decodeImage(data); + + final texture = await viewer.createTexture( + await image.getWidth(), await image.getHeight(), + textureFormat: TextureFormat.RGBA32F); + await texture.setLinearImage( + image, PixelDataFormat.RGBA, PixelDataType.FLOAT); + final sampler = await viewer.createTextureSampler(); + + await materialInstance.setParameterTexture( + "baseColorMap", texture, sampler); + + await testHelper.capture(viewer, "unlit_baseColorMap"); + + await image.destroy(); + await texture.dispose(); + await sampler.dispose(); + + await viewer.destroyMaterialInstance(materialInstance); + }); + }); + + test('unlit + baseColorMap (apply material after creation)', () async { + await testHelper.withViewer((viewer) async { + var cube = await viewer + .createGeometry(GeometryHelper.cube(), materialInstances: []); + var materialInstance = await viewer.createUnlitMaterialInstance(); + await materialInstance.setParameterFloat4( + "baseColorFactor", 1.0, 1.0, 1.0, 1.0); + await materialInstance.setParameterFloat2("uvScale", 1.0, 1.0); + await materialInstance.setParameterInt("baseColorIndex", 0); + + var data = + File("${testHelper.testDir}/assets/cube_texture2_512x512.png") + .readAsBytesSync(); + final image = await viewer.decodeImage(data); + + final texture = await viewer.createTexture( + await image.getWidth(), await image.getHeight(), + textureFormat: TextureFormat.RGBA32F); + await texture.setLinearImage( + image, PixelDataFormat.RGBA, PixelDataType.FLOAT); + final sampler = await viewer.createTextureSampler(); + + await materialInstance.setParameterTexture( + "baseColorMap", texture, sampler); + await cube.setMaterialInstanceAt(materialInstance); + await testHelper.capture( + viewer, "unlit_baseColorMap_material_created_after"); + + await image.destroy(); + await texture.dispose(); + await sampler.dispose(); + + await viewer.destroyMaterialInstance(materialInstance); + }); + }); + + test('unlit + baseColorMap (fetch material after creation)', () async { + await testHelper.withViewer((viewer) async { + var materialInstance = await viewer.createUnlitMaterialInstance(); + var cube = await viewer.createGeometry(GeometryHelper.cube(), + materialInstances: [materialInstance]); + + materialInstance = await viewer.getMaterialInstanceAt(cube.entity, 0); + + await materialInstance.setParameterFloat4( + "baseColorFactor", 1.0, 1.0, 1.0, 1.0); + await materialInstance.setParameterInt("baseColorIndex", 0); + + var data = + File("${testHelper.testDir}/assets/cube_texture2_512x512.png") + .readAsBytesSync(); + final image = await viewer.decodeImage(data); + + final texture = await viewer.createTexture( + await image.getWidth(), await image.getHeight(), + textureFormat: TextureFormat.RGBA32F); + await texture.setLinearImage( + image, PixelDataFormat.RGBA, PixelDataType.FLOAT); + final sampler = await viewer.createTextureSampler(); + + await materialInstance.setParameterTexture( + "baseColorMap", texture, sampler); + await cube.setMaterialInstanceAt(materialInstance); + await testHelper.capture( + viewer, "unlit_baseColorMap_fetch_material"); + + await image.destroy(); + await texture.dispose(); + await sampler.dispose(); + + await viewer.destroyMaterialInstance(materialInstance); + }); + }); + test('unlit material with color + alpha', () async { await testHelper.withViewer((viewer) async { await viewer.setPostProcessing(true); @@ -123,18 +230,13 @@ void main() async { }, bg: kRed, postProcessing: true); }); - test('set ubershader texture', () async { + test('ubershader + baseColorMap texture', () async { await testHelper.withViewer((viewer) async { - await viewer.addLight(LightType.SUN, 6500, 1000000, 0, 0, 0, 0, 0, -1); - await viewer.setCameraPosition(0, 2, 6); - await viewer - .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 8)); - - var materialInstance = await viewer.createUbershaderMaterialInstance(); + var materialInstance = await viewer.createUbershaderMaterialInstance(unlit: true); final cube = await viewer.createGeometry( - GeometryHelper.cube(uvs: true, normals: true), + GeometryHelper.cube(), materialInstances: [materialInstance]); - var data = File("${testHelper.testDir}/assets/cube_texture_512x512.png") + var data = File("${testHelper.testDir}/assets/cube_texture2_512x512_flipped.png") .readAsBytesSync(); final image = await viewer.decodeImage(data); final texture = await viewer.createTexture( @@ -153,7 +255,7 @@ void main() async { viewer, "geometry_cube_with_custom_material_ubershader_texture"); await viewer.destroyAsset(cube); await viewer.destroyMaterialInstance(materialInstance); - await viewer.destroyTexture(texture); + await texture.dispose(); }); }); @@ -340,159 +442,6 @@ void main() async { }, postProcessing: true); }); }); - - group('projection', () { - test('apply projection material', () async { - await testHelper.withViewer((viewer) async { - await viewer - .setCameraModelMatrix4(Matrix4.translation(Vector3(0, 0, 5))); - // // Rotate the camera in 30-degree increments and capture at each position - // for (int i = 0; i <= 180; i += 30) { - // int i = 60; - // final angle = i * (pi / 180); // Convert to radians - - // // Calculate camera position - // // Start at (0, 1, 5) (facing the sphere from +z) and rotate around to (-5, 1, 0) - // final radius = 5.0; - // final x = -radius * sin(angle); - // final z = radius * cos(angle); - - // // Create view matrix for this camera position - // final matrix = makeViewMatrix( - // Vector3(x, 1, z), - // Vector3.zero(), // Looking at origin - // Vector3(0, 1, 0) // Up vector - // ) - // ..invert(); - - // await viewer.setCameraModelMatrix4(matrix); - - // // Take a snapshot at this position - // await testHelper.capture(viewer, "projection_${i}deg"); - // } - - final view = await viewer.getViewAt(0); - final rtTextureHandle = await testHelper.createTexture(512, 512); - final rt = await viewer.createRenderTarget( - 512, 512, rtTextureHandle.metalTextureAddress); - await view.setRenderTarget(rt); - final rtTexture = await rt.getColorTexture(); - - final ppuvMaterial = await viewer.createMaterial(File( - "/Users/nickfisher/Documents/thermion/materials/postprocess_uv.filamat") - .readAsBytesSync()); - final ppuvInstance = await ppuvMaterial.createInstance(); - - // Setup the scene (lighting, materials, etc.) - await viewer.loadIbl( - "file://${testHelper.testDir}/assets/default_env_ibl.ktx", - intensity: 1000); - - await viewer.addDirectLight(DirectLight.sun( - intensity: 500000, - castShadows: true, - direction: Vector3(1, -0.5, 0).normalized())); - - // input texture - var inputTextureData = - File("${testHelper.testDir}/assets/cube_texture_512x512.png") - .readAsBytesSync(); - var inputImage = await viewer.decodeImage(inputTextureData); - var inputTexture = await viewer.createTexture( - await inputImage.getWidth(), await inputImage.getHeight(), - textureFormat: TextureFormat.RGBA32F); - await inputTexture.setLinearImage( - inputImage, PixelDataFormat.RGBA, PixelDataType.FLOAT); - - var captureUvMaterial = await viewer.createMaterial(File( - "/Users/nickfisher/Documents/thermion/materials/capture_uv.filamat") - .readAsBytesSync()); - var captureUvMaterialInstance = - await captureUvMaterial.createInstance(); - var sampler = await viewer.createTextureSampler(); - - // Create sphere and plane - final sphere = await viewer.createGeometry( - GeometryHelper.sphere(normals: true, uvs: true), - materialInstances: []); - // await viewer.setTransform( - // sphere.entity, Matrix4.compose(Vector3(2, 1, -1), Quaternion.identity(), Vector3(1.0,1.0,1.0))); - await sphere.addToScene(); - - await testHelper.capture(viewer, "color", renderTarget: rt); - - await captureUvMaterialInstance.setParameterTexture( - "color", rtTexture, sampler); - await sphere.setMaterialInstanceAt(captureUvMaterialInstance); - - await testHelper.capture(viewer, "uv_capture", renderTarget: rt); - - // await ppuvInstance.setParameterTexture("uvTexture", rtTexture, sampler); - - await sphere.setMaterialInstanceAt(ppuvInstance); - - await testHelper.capture(viewer, "ppuv", renderTarget: rt); - - // final quad = await viewer.createGeometry( - // GeometryHelper.plane(width: 100.0, height: 100.0), - // materialInstances: [materialInstance]); - // await viewer.setTransform( - // quad.entity, Matrix4.translation(Vector3(0, -1, 0))); - // await quad.addToScene(); - - var bytePixelBuffer = await viewer.capture(renderTarget: rt); - - var floatPixelBuffer = Float32List.fromList( - bytePixelBuffer.map((p) => p.toDouble() / 255.0).toList()); - - print("pixelBuffer ${floatPixelBuffer.lengthInBytes} bytes"); - - var output = unprojectTexture( - renderTarget: await inputImage.getData(), - uvCoordinates: floatPixelBuffer, - renderTargetWidth: 512, - renderTargetHeight: 512, - renderTargetChannels: 4, - uvWidth: 512, - uvHeight: 512, - uvChannels: 4, - outputWidth: 512, - outputHeight: 512); - var byteOutput = - Uint8List.fromList(output.map((o) => (o * 255.0).toInt()).toList()); - await savePixelBufferToBmp(byteOutput, 512, 512, "/tmp/foo.bmp"); - - final reappliedImage = await viewer.createImage(512, 512, 4); - final data = await reappliedImage.getData(); - data.setRange(0, data.length, output); - final reappliedTexture = await viewer.createTexture(512, 512, - textureFormat: TextureFormat.RGBA32F); - await reappliedTexture.setLinearImage( - reappliedImage, PixelDataFormat.RGBA, PixelDataType.FLOAT); - var newMaterialInstance = - await viewer.createUbershaderMaterialInstance(unlit: true); - await newMaterialInstance.setParameterInt("baseColorIndex", 0); - - await newMaterialInstance.setParameterTexture( - "baseColorMap", - reappliedTexture, - // inputTexture, - - sampler); - await newMaterialInstance.setParameterFloat4( - "baseColorFactor", 1.0, 1.0, 1.0, 1.0); - await sphere.setMaterialInstanceAt(newMaterialInstance); - await testHelper.capture(viewer, "capture2"); - - // final view = await viewer.getViewAt(0); - // final renderTargetTexture = await testHelper.createTexture(512, 512); - // final rt = await viewer.createRenderTarget( - // 512, 512, renderTargetTexture.metalTextureAddress); - // await view.setRenderTarget(rt); - // pixelBuffer = await viewer.capture(renderTarget: rt); - }, viewportDimensions: (width: 512, height: 512)); - }); - }); } Float32List unprojectTexture({ diff --git a/thermion_dart/test/render_thread_tests.dart b/thermion_dart/test/render_thread_tests.dart new file mode 100644 index 00000000..12740e28 --- /dev/null +++ b/thermion_dart/test/render_thread_tests.dart @@ -0,0 +1,44 @@ +import 'dart:async'; +import 'dart:ffi'; +import 'package:test/test.dart'; +import 'package:thermion_dart/thermion_dart.dart'; + +import 'helpers.dart'; + +void main() async { + final testHelper = TestHelper("render_thread"); + + await testHelper.setup(); + + group("render thread/capture", () { + test("request frame on render thread", () async { + await testHelper.withViewer((viewer) async { + var metalTexture = await testHelper.createTexture(500, 500); + var texture = await FilamentApp.instance!.createTexture( + 500, 500, + importedTextureHandle: metalTexture.metalTextureAddress, + flags: { + TextureUsage.TEXTURE_USAGE_BLIT_DST, + TextureUsage.TEXTURE_USAGE_SAMPLEABLE, + TextureUsage.TEXTURE_USAGE_COLOR_ATTACHMENT + }); + + var renderTarget = await FilamentApp.instance! + .createRenderTarget(500, 500, color: texture); + + await viewer.view.setRenderTarget(renderTarget); + + await viewer.render(); + + await Future.delayed(Duration(milliseconds: 1)); + + var data = metalTexture.getTextureBytes()!; + var pixels = data.bytes.cast().asTypedList(data.length); + + savePixelBufferToBmp( + pixels, 500, 500, "${testHelper.testDir}/request_frame.bmp"); + await viewer.dispose(); + }); + }); + }); +} diff --git a/thermion_dart/test/texture_tests.dart b/thermion_dart/test/texture_tests.dart index d182ec20..d42581ea 100644 --- a/thermion_dart/test/texture_tests.dart +++ b/thermion_dart/test/texture_tests.dart @@ -316,7 +316,13 @@ void main() async { test('depth visualization', () async { RenderLoop_create(); final engine = await withPointerCallback( - (cb) => Engine_createRenderThread(TBackend.BACKEND_METAL.index, cb)); + (cb) => Engine_createRenderThread(TBackend.BACKEND_METAL.index, nullptr, nullptr, 1, false, cb)); + + final gltfResourceLoader = await withPointerCallback( + (cb) => GltfResourceLoader_createRenderThread(engine, cb)); + final gltfAssetLoader = await withPointerCallback( + (cb) => GltfAssetLoader_createRenderThread(engine, nullptr, cb)); + final renderer = await withPointerCallback( (cb) => Engine_createRendererRenderThread(engine, cb)); final swapchain = await withPointerCallback((cb) => @@ -328,20 +334,63 @@ void main() async { cb)); final camera = await withPointerCallback( (cb) => Engine_createCameraRenderThread(engine, cb)); + + final offscreenView = await withPointerCallback( + (cb) => Engine_createViewRenderThread(engine, cb)); final view = await withPointerCallback( (cb) => Engine_createViewRenderThread(engine, cb)); + + final colorTexture = await withPointerCallback((cb) => + Texture_buildRenderThread( + engine, + 500, + 500, + 1, + 1, + TTextureUsage.TEXTURE_USAGE_COLOR_ATTACHMENT.value | + TTextureUsage.TEXTURE_USAGE_SAMPLEABLE.value | + TTextureUsage.TEXTURE_USAGE_BLIT_SRC.value, + 0, + TTextureSamplerType.SAMPLER_2D, + TTextureFormat.TEXTUREFORMAT_RGBA8, + cb)); + + final depthTexture = await withPointerCallback((cb) => + Texture_buildRenderThread( + engine, + 500, + 500, + 1, + 1, + TTextureUsage.TEXTURE_USAGE_DEPTH_ATTACHMENT.value | + TTextureUsage.TEXTURE_USAGE_SAMPLEABLE.value, + 0, + TTextureSamplerType.SAMPLER_2D, + TTextureFormat.TEXTUREFORMAT_DEPTH32F, + cb)); + + final renderTarget = await withPointerCallback((cb) => + RenderTarget_createRenderThread( + engine, 500, 500, colorTexture, depthTexture, cb)); + View_setRenderTarget(offscreenView, renderTarget); + final offscreenScene = Engine_createScene(engine); final scene = Engine_createScene(engine); + await withVoidCallback((cb) { Renderer_setClearOptionsRenderThread( renderer, 1.0, 0.0, 1.0, 1.0, 0, true, true, cb); }); + View_setFrustumCullingEnabled(offscreenView, false); View_setFrustumCullingEnabled(view, false); + View_setScene(offscreenView, offscreenScene); View_setScene(view, scene); + View_setCamera(offscreenView, camera); View_setCamera(view, camera); + View_setViewport(offscreenView, 500, 500); View_setViewport(view, 500, 500); final eye = Struct.create() - ..x = 0.0 - ..y = 0.0 + ..x = 5.0 + ..y = 1.0 ..z = 5.0; Camera_lookAt( camera, @@ -354,11 +403,21 @@ void main() async { ..x = 0.0 ..y = 1.0 ..z = 0.0); - View_setBloomRenderThread(view, false, 0.0); + View_setBloomRenderThread(offscreenView, false, 0.0); Camera_setLensProjection(camera, 0.05, 100000, 1.0, kFocalLength); + View_setPostProcessing(offscreenView, false); View_setPostProcessing(view, false); + final iblData = File("${testHelper.testDir}/assets/default_env_ibl.ktx") + .readAsBytesSync(); + final ibl = await withPointerCallback((cb) => + Engine_buildIndirectLightRenderThread( + engine, iblData.address, iblData.length, 30000, cb, nullptr)); + + Scene_setIndirectLight(offscreenScene, ibl); + Scene_setIndirectLight(scene, ibl); + final skyboxData = File("${testHelper.testDir}/assets/default_env_skybox.ktx") .readAsBytesSync(); @@ -367,25 +426,98 @@ void main() async { Engine_buildSkyboxRenderThread( engine, skyboxData.address, skyboxData.length, cb, nullptr)); + Scene_setSkybox(offscreenScene, skybox); Scene_setSkybox(scene, skybox); - final cubeData = GeometryHelper.cube(); - final cube = await withPointerCallback((cb) => - SceneAsset_createGeometryRenderThread( + // final cubeData = GeometryHelper.cube(); + // final cube = await withPointerCallback((cb) => + // SceneAsset_createGeometryRenderThread( + // engine, + // cubeData.vertices.address, + // cubeData.vertices.length, + // cubeData.normals.address, + // cubeData.normals.length, + // cubeData.uvs.address, + // cubeData.uvs.length, + // cubeData.indices.address, + // cubeData.indices.length, + // TPrimitiveType.PRIMITIVETYPE_POINTS, + // nullptr, + // 0, + // cb)); + // Scene_addEntity(offscreenScene, SceneAsset_getEntity(cube)); + var cube = + File("${testHelper.testDir}/assets/cube.glb").readAsBytesSync(); + final filamentAsset = await withPointerCallback((cb) => + GltfAssetLoader_loadRenderThread(gltfAssetLoader, + cube.address, cube.length, 1, cb)); + var entities = Int32List(FilamentAsset_getEntityCount(filamentAsset)); + FilamentAsset_getEntities(filamentAsset, entities.address); + + final unlitMaData = + File("/Users/nickfisher/Documents/thermion/materials/unlit.filamat") + .readAsBytesSync(); + final unlitMa = + Engine_buildMaterial(engine, unlitMaData.address, unlitMaData.length); + final unlitMi = await withPointerCallback( + (cb) => Material_createInstanceRenderThread(unlitMa, cb)); + MaterialInstance_setParameterFloat2( + unlitMi, "uvScale".toNativeUtf8().cast(), 1.0, 1.0); + MaterialInstance_setParameterFloat4(unlitMi, + "baseColorFactor".toNativeUtf8().cast(), 1.0, 1.0, 0.0, 1.0); + MaterialInstance_setParameterInt( + unlitMi, "baseColorIndex".toNativeUtf8().cast(), -1); + for (int i = 0; i < entities.length; i++) { + RenderableManager_setMaterialInstanceAt( + Engine_getRenderableManager(engine), entities[i], 0, unlitMi); + } + + // final materialInstance = GltfAssetLoader_getMaterialInstance( + // Engine_getRenderableManager(engine), filamentAsset); + // MaterialInstance_setParameterFloat4(materialInstance, + // "baseColorFactor".toNativeUtf8().cast(), 1.0, 0, 0, 1); + + final imageData = + File("${testHelper.testDir}/assets/cube_texture2_512x512.png") + .readAsBytesSync(); + final image = await Image_decode(imageData.address, imageData.length, + "unused".toNativeUtf8().cast()); + final texture = await withPointerCallback((cb) => + Texture_buildRenderThread( engine, - cubeData.vertices.address, - cubeData.vertices.length, - cubeData.normals.address, - cubeData.normals.length, - cubeData.uvs.address, - cubeData.uvs.length, - cubeData.indices.address, - cubeData.indices.length, - TPrimitiveType.PRIMITIVETYPE_POINTS, - nullptr, + Image_getWidth(image), + Image_getHeight(image), + 1, + 1, + TTextureUsage.TEXTURE_USAGE_SAMPLEABLE.index, 0, + TTextureSamplerType.SAMPLER_2D, + TTextureFormat.TEXTUREFORMAT_RGBA32F, cb)); - Scene_addEntity(scene, SceneAsset_getEntity(cube)); + + await withBoolCallback((cb) => Texture_loadImageRenderThread( + engine, + texture, + image, + TPixelDataFormat.PIXELDATAFORMAT_RGBA, + TPixelDataType.PIXELDATATYPE_FLOAT, + cb)); + MaterialInstance_setParameterInt( + unlitMi, "baseColorIndex".toNativeUtf8().cast(), 0); + MaterialInstance_setParameterTexture( + unlitMi, + "baseColorMap".toNativeUtf8().cast(), + RenderTarget_getDepthTexture(renderTarget), + // texture, + TextureSampler_create()); + + await withVoidCallback((cb) { + Scene_addFilamentAssetRenderThread(offscreenScene, filamentAsset, cb); + }); + + await withVoidCallback((cb) { + Scene_addFilamentAssetRenderThread(scene, filamentAsset, cb); + }); await withVoidCallback((cb) { Engine_flushAndWaitRenderThead(engine, cb); @@ -400,13 +532,29 @@ void main() async { ); }); - // for (int i = 0; i < 10; i++) { - await withVoidCallback((cb) { - Renderer_renderRenderThread(renderer, view, cb); - }); - // } + await withVoidCallback((cb) { + Renderer_renderRenderThread(renderer, offscreenView, cb); + }); - var view1Out = Uint8List(500 * 500 * 4); + await withVoidCallback((cb) { + Renderer_renderRenderThread(renderer, view, cb); + }); + + var offscreenViewOut = Uint8List(500 * 500 * 4); + + await withVoidCallback((cb) { + Renderer_readPixelsRenderThread( + renderer, + offscreenView, + renderTarget, + TPixelDataFormat.PIXELDATAFORMAT_RGBA, + TPixelDataType.PIXELDATATYPE_UBYTE, + offscreenViewOut.address, + cb, + ); + }); + + var swapchainOut = Uint8List(500 * 500 * 4); await withVoidCallback((cb) { Renderer_readPixelsRenderThread( @@ -415,7 +563,7 @@ void main() async { nullptr, TPixelDataFormat.PIXELDATAFORMAT_RGBA, TPixelDataType.PIXELDATATYPE_UBYTE, - view1Out.address, + swapchainOut.address, cb, ); }); @@ -425,72 +573,23 @@ void main() async { }); await savePixelBufferToPng( - view1Out, + offscreenViewOut, 500, 500, "/tmp/view1.png", ); + await savePixelBufferToPng( + swapchainOut, + 500, + 500, + "/tmp/sc1.png", + ); + + await withVoidCallback((cb) => Engine_destroyIndirectLightRenderThread(engine, ibl, cb)); + await withVoidCallback((cb) => Engine_destroySkyboxRenderThread(engine, skybox, cb)); RenderLoop_destroy(); - // await camera.lookAt(Vector3(100, 1500, 1500)); - - // // first view just renders a normal unlit cube, but into a render target - // final vp = await (await viewer.getViewAt(0)).getViewport(); - // FFIView view = await viewer.createView() - // as FFIView; // await viewer.getViewAt(0) as FFIView; - // await view.setViewport(vp.width, vp.height); - // await view.setCamera(camera); - // await view.setFrustumCullingEnabled(false); - - // var scene1 = Engine_createScene(engine); - // View_setScene(view.view, scene1); - // Scene_setSkybox(scene1, skybox); - // await view.setPostProcessing(false); - - // await viewer.setClearOptions(Vector4(0, 0, 1, 0), 1, false, false); - // final colorTextureHandle = await testHelper.createTexture( - // vp.width, - // vp.height, - // ); - - // final rt = await viewer.createRenderTarget( - // vp.width, - // vp.height, - // // colorTextureHandle.metalTextureAddress, - // ) as FFIRenderTarget; - // await view.setRenderTarget(rt); - - // final unlit = await viewer.createUnlitMaterialInstance(); - // await unlit.setParameterInt("baseColorIndex", -1); - // await unlit.setParameterFloat4("baseColorFactor", 1.0, 0, 0, 1); - // await unlit.setDepthWriteEnabled(true); - - // final cube = await viewer - // .createGeometry(GeometryHelper.cube(), materialInstances: [unlit]); - // Scene_addEntity(scene1, cube.entity); - - // final cube2 = await viewer - // .createGeometry(GeometryHelper.cube(), materialInstances: [unlit]); - // Scene_addEntity(scene1, cube2.entity); - - // await viewer.setTransform( - // cube.entity, - // Matrix4.compose( - // Vector3.zero(), - // Quaternion.identity(), - // Vector3(950, 950, 500), - // ), - // ); - // await viewer.setTransform( - // cube2.entity, - // Matrix4.compose( - // Vector3(-500, -500, -2000), - // Quaternion.identity(), - // Vector3(500, 500, 500), - // ), - // ); - // final mirrorMaterial = await viewer.createMaterial( // File( // "/Users/nickfisher/Documents/thermion/materials/mirror.filamat", @@ -524,15 +623,6 @@ void main() async { // await view2.setCamera(camera); // await view2.setFrustumCullingEnabled(false); - // final image = await viewer.decodeImage( - // File("${testHelper.testDir}/assets/cube_texture2_512x512.png") - // .readAsBytesSync()); - // final texture = await viewer.createTexture( - // await image.getWidth(), await image.getHeight(), - // textureFormat: TextureFormat.RGBA32F); - // await texture.setLinearImage( - // image, PixelDataFormat.RGBA, PixelDataType.FLOAT); - // await mirrorMi.setParameterTexture( // "albedo", // // texture as FFITexture, diff --git a/thermion_dart/test/transform_tests.dart b/thermion_dart/test/transform_tests.dart index c8016985..b5593b62 100644 --- a/thermion_dart/test/transform_tests.dart +++ b/thermion_dart/test/transform_tests.dart @@ -2,9 +2,6 @@ import 'package:thermion_dart/thermion_dart.dart'; import 'package:test/test.dart'; - -import 'package:vector_math/vector_math_64.dart'; - import 'helpers.dart'; void main() async { diff --git a/thermion_dart/test/view_tests.dart b/thermion_dart/test/view_tests.dart index 4d794749..e9e4cb92 100644 --- a/thermion_dart/test/view_tests.dart +++ b/thermion_dart/test/view_tests.dart @@ -1,157 +1,157 @@ -// ignore_for_file: unused_local_variable +// // ignore_for_file: unused_local_variable -import 'dart:async'; +// import 'dart:async'; -import 'package:test/test.dart'; -import 'package:thermion_dart/thermion_dart.dart'; -import 'package:vector_math/vector_math_64.dart'; -import 'helpers.dart'; +// import 'package:test/test.dart'; +// import 'package:thermion_dart/thermion_dart.dart'; +// import 'package:vector_math/vector_math_64.dart'; +// import 'helpers.dart'; -void main() async { - final testHelper = TestHelper("view"); +// void main() async { +// final testHelper = TestHelper("view"); - group('view tests', () { - test('get camera from view', () async { - await testHelper.withViewer((viewer) async { - var view = await viewer.getViewAt(0); - expect(await view.getCamera(), isNotNull); - }); - }); +// group('view tests', () { +// test('get camera from view', () async { +// await testHelper.withViewer((viewer) async { +// var view = await viewer.getViewAt(0); +// expect(await view.getCamera(), isNotNull); +// }); +// }); - test('one swapchain, render view to render target', () async { - await testHelper.withViewer((viewer) async { - final texture = await testHelper.createTexture(500, 500); - final renderTarget = await viewer.createRenderTarget( - 500, 500, texture.metalTextureAddress); - final view = await viewer.getViewAt(0); - await view.setRenderTarget(renderTarget); +// test('one swapchain, render view to render target', () async { +// await testHelper.withViewer((viewer) async { +// final texture = await testHelper.createTexture(500, 500); +// final renderTarget = await viewer.createRenderTarget( +// 500, 500, texture.metalTextureAddress); +// final view = await viewer.getViewAt(0); +// await view.setRenderTarget(renderTarget); - await viewer.setBackgroundColor(1.0, 0, 0, 1); - final cube = await viewer - .createGeometry(GeometryHelper.cube(normals: false, uvs: false)); +// await viewer.setBackgroundColor(1.0, 0, 0, 1); +// final cube = await viewer +// .createGeometry(GeometryHelper.cube(normals: false, uvs: false)); - var mainCamera = await viewer.getMainCamera(); - mainCamera.setTransform(Matrix4.translation(Vector3(0, 0, 5))); - await testHelper.capture( - viewer, - renderTarget: renderTarget, - "default_swapchain_default_view_render_target"); - }); - }); +// var mainCamera = await viewer.getMainCamera(); +// mainCamera.setTransform(Matrix4.translation(Vector3(0, 0, 5))); +// await testHelper.capture( +// viewer, +// renderTarget: renderTarget, +// "default_swapchain_default_view_render_target"); +// }); +// }); - test('create secondary view, default swapchain', () async { - await testHelper.withViewer((viewer) async { - final cube = await viewer - .createGeometry(GeometryHelper.cube(normals: false, uvs: false)); +// test('create secondary view, default swapchain', () async { +// await testHelper.withViewer((viewer) async { +// final cube = await viewer +// .createGeometry(GeometryHelper.cube(normals: false, uvs: false)); - var mainCamera = await viewer.getMainCamera(); - mainCamera.setTransform(Matrix4.translation(Vector3(0, 0, 5))); - await testHelper.capture(viewer, "default_swapchain_default_view"); +// var mainCamera = await viewer.getMainCamera(); +// mainCamera.setTransform(Matrix4.translation(Vector3(0, 0, 5))); +// await testHelper.capture(viewer, "default_swapchain_default_view"); - final view = await viewer.createView(); - view.updateViewport(500, 500); - view.setCamera(mainCamera); - await testHelper.capture( - viewer, - "default_swapchain_new_view_with_main_camera", - view: view, - ); +// final view = await viewer.createView(); +// view.setViewport(500, 500); +// view.setCamera(mainCamera); +// await testHelper.capture( +// viewer, +// "default_swapchain_new_view_with_main_camera", +// view: view, +// ); - var newCamera = await viewer.createCamera(); - newCamera.setTransform(Matrix4.translation(Vector3(0.0, 0.0, 10.0))); - newCamera.setLensProjection(); - view.setCamera(newCamera); +// var newCamera = await viewer.createCamera(); +// newCamera.setTransform(Matrix4.translation(Vector3(0.0, 0.0, 10.0))); +// newCamera.setLensProjection(); +// view.setCamera(newCamera); - await testHelper.capture( - viewer, - "default_swapchain_new_view_new_camera", - view: view, - ); +// await testHelper.capture( +// viewer, +// "default_swapchain_new_view_new_camera", +// view: view, +// ); - await testHelper.capture( - viewer, - "default_swapchain_default_view_main_camera_no_change", - ); - }); - }); +// await testHelper.capture( +// viewer, +// "default_swapchain_default_view_main_camera_no_change", +// ); +// }); +// }); - test('create secondary view, different swapchain', () async { - await testHelper.withViewer((viewer) async { - final cube = await viewer.createGeometry(GeometryHelper.cube()); +// test('create secondary view, different swapchain', () async { +// await testHelper.withViewer((viewer) async { +// final cube = await viewer.createGeometry(GeometryHelper.cube()); - var mainCamera = await viewer.getMainCamera(); - mainCamera.setTransform(Matrix4.translation(Vector3(0, 0, 5))); - final swapChain = await viewer.createHeadlessSwapChain(1, 1); - await testHelper.capture( - viewer, "create_swapchain_default_view_default_swapchain"); +// var mainCamera = await viewer.getMainCamera(); +// mainCamera.setTransform(Matrix4.translation(Vector3(0, 0, 5))); +// final swapChain = await viewer.createHeadlessSwapChain(1, 1); +// await testHelper.capture( +// viewer, "create_swapchain_default_view_default_swapchain"); - final view = await viewer.createView(); +// final view = await viewer.createView(); - final texture = await testHelper.createTexture(200, 400); - final renderTarget = await viewer.createRenderTarget( - 200, 400, texture.metalTextureAddress); - await view.setRenderTarget(renderTarget); +// final texture = await testHelper.createTexture(200, 400); +// final renderTarget = await viewer.createRenderTarget( +// 200, 400, texture.metalTextureAddress); +// await view.setRenderTarget(renderTarget); - await view.updateViewport(200, 400); - view.setCamera(mainCamera); - mainCamera.setLensProjection(aspect: 0.5); +// await view.setViewport(200, 400); +// view.setCamera(mainCamera); +// mainCamera.setLensProjection(aspect: 0.5); - await testHelper.capture( - viewer, - view: view, - swapChain: swapChain, - renderTarget: renderTarget, - "create_swapchain_secondary_view_new_swapchain", - ); - }); - }); +// await testHelper.capture( +// viewer, +// view: view, +// swapChain: swapChain, +// renderTarget: renderTarget, +// "create_swapchain_secondary_view_new_swapchain", +// ); +// }); +// }); - test('pick', () async { - await testHelper.withViewer((viewer) async { - final view = await viewer.getViewAt(0); +// test('pick', () async { +// await testHelper.withViewer((viewer) async { +// final view = await viewer.getViewAt(0); - await view.setRenderable(true, testHelper.swapChain); +// await view.setRenderable(true, testHelper.swapChain); - final cube = await viewer - .createGeometry(GeometryHelper.cube(normals: false, uvs: false)); +// final cube = await viewer +// .createGeometry(GeometryHelper.cube(normals: false, uvs: false)); - await testHelper.capture(viewer, "view_pick"); +// await testHelper.capture(viewer, "view_pick"); - final completer = Completer(); +// final completer = Completer(); - await viewer.pick(250, 250, (result) { - completer.complete(result.entity); - print( - "Pick result : ${result.fragX} ${result.fragY} ${result.fragZ}"); - }); +// await viewer.pick(250, 250, (result) { +// completer.complete(result.entity); +// print( +// "Pick result : ${result.fragX} ${result.fragY} ${result.fragZ}"); +// }); - for (int i = 0; i < 10; i++) { - await testHelper.capture(viewer, "view_pick"); - if (completer.isCompleted) { - break; - } - } +// for (int i = 0; i < 10; i++) { +// await testHelper.capture(viewer, "view_pick"); +// if (completer.isCompleted) { +// break; +// } +// } - expect(completer.isCompleted, true); - expect(await completer.future, cube.entity); - }, cameraPosition: Vector3(0, 0, 3)); - }); +// expect(completer.isCompleted, true); +// expect(await completer.future, cube.entity); +// }, cameraPosition: Vector3(0, 0, 3)); +// }); - test('dithering', () async { - await testHelper.withViewer((viewer) async { - final view = await viewer.getViewAt(0); +// test('dithering', () async { +// await testHelper.withViewer((viewer) async { +// final view = await viewer.getViewAt(0); - expect(await view.isDitheringEnabled(), true); +// expect(await view.isDitheringEnabled(), true); - final cube = await viewer - .createGeometry(GeometryHelper.cube(normals: false, uvs: false)); +// final cube = await viewer +// .createGeometry(GeometryHelper.cube(normals: false, uvs: false)); - await testHelper.capture(viewer, "dithering_enabled"); +// await testHelper.capture(viewer, "dithering_enabled"); - await view.setDithering(false); - expect(await view.isDitheringEnabled(), false); - await testHelper.capture(viewer, "dithering_disabled"); - }, cameraPosition: Vector3(0, 0, 3)); - }); - }); -} +// await view.setDithering(false); +// expect(await view.isDitheringEnabled(), false); +// await testHelper.capture(viewer, "dithering_disabled"); +// }, cameraPosition: Vector3(0, 0, 3)); +// }); +// }); +// }