diff --git a/thermion_dart/test/assets/default_env_ibl.ktx b/thermion_dart/test/assets/default_env_ibl.ktx new file mode 100644 index 00000000..0765d521 Binary files /dev/null and b/thermion_dart/test/assets/default_env_ibl.ktx differ diff --git a/thermion_dart/test/assets/default_env_skybox.ktx b/thermion_dart/test/assets/default_env_skybox.ktx new file mode 100644 index 00000000..9d72e5a0 Binary files /dev/null and b/thermion_dart/test/assets/default_env_skybox.ktx differ diff --git a/thermion_dart/test/helpers.dart b/thermion_dart/test/helpers.dart index 056d34b4..e078a95f 100644 --- a/thermion_dart/test/helpers.dart +++ b/thermion_dart/test/helpers.dart @@ -55,7 +55,6 @@ extension on Uri { String get name => pathSegments.where((e) => e != '').last; } -late String testDir; Future savePixelBufferToBmp( Uint8List pixelBuffer, int width, int height, String outputPath) async { @@ -66,7 +65,9 @@ Future savePixelBufferToBmp( } class TestHelper { + late Directory outDir; + late String testDir; TestHelper(String dir) { final packageUri = findPackageRoot('thermion_dart'); diff --git a/thermion_dart/test/integration_test.dart b/thermion_dart/test/integration_test.dart index 4b312b51..7daa522b 100644 --- a/thermion_dart/test/integration_test.dart +++ b/thermion_dart/test/integration_test.dart @@ -24,22 +24,17 @@ void main() async { test('set background color to full transparency', () async { var viewer = await createViewer(); await viewer.setBackgroundColor(0.0, 1.0, 0.0, 0.0); - await testHelper.capture(viewer, "set_background_color_to_transparent_green"); + await testHelper.capture( + viewer, "set_background_color_to_transparent_green"); await viewer.dispose(); }); - test('load skybox', () async { - var viewer = await createViewer(); - await viewer.loadSkybox( - "file:///$testDir/../../examples/assets/default_env/default_env_skybox.ktx"); - await Future.delayed(Duration(seconds: 1)); - await testHelper.capture(viewer, "skybox"); - }); + test('set background image', () async { var viewer = await createViewer(); - await viewer - .setBackgroundImage("file:///$testDir/cube_texture_512x512.png"); + await viewer.setBackgroundImage( + "file:///${testHelper.testDir}/assets/cube_texture_512x512.png"); await viewer.setPostProcessing(true); await viewer.setToneMapping(ToneMapper.LINEAR); await testHelper.capture(viewer, "set_background_image"); @@ -50,7 +45,7 @@ void main() async { group("gltf", () { test('load glb from file', () async { var viewer = await createViewer(); - var model = await viewer.loadGlb("file://$testDir/cube.glb"); + var model = await viewer.loadGlb("file://${testHelper.testDir}/cube.glb"); await viewer.transformToUnitCube(model); await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0); await viewer.setCameraPosition(0, 1, 5); @@ -61,7 +56,7 @@ void main() async { test('load glb from buffer', () async { var viewer = await createViewer(); - var buffer = File("$testDir/cube.glb").readAsBytesSync(); + var buffer = File("${testHelper.testDir}/cube.glb").readAsBytesSync(); var model = await viewer.loadGlbFromBuffer(buffer); await viewer.transformToUnitCube(model); await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0); @@ -79,7 +74,7 @@ void main() async { await viewer .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); - var buffer = File("$testDir/cube.glb").readAsBytesSync(); + var buffer = File("${testHelper.testDir}/cube.glb").readAsBytesSync(); var model1 = await viewer.loadGlbFromBuffer(buffer, priority: 7); var model2 = await viewer.loadGlbFromBuffer(buffer, priority: 0); @@ -182,7 +177,7 @@ void main() async { late StreamSubscription listener; - final uri = "$testDir/cube.glb"; + final uri = "${testHelper.testDir}/cube.glb"; listener = viewer.sceneUpdated.listen((updateEvent) { var wasSuccess = updateEvent.eventType == EventType.EntityAdded && @@ -197,7 +192,7 @@ void main() async { test('remove glb fires SceneUpdateEvent', () async { var viewer = await createViewer(); - final uri = "$testDir/cube.glb"; + final uri = "${testHelper.testDir}/cube.glb"; var entity = await viewer.loadGlb(uri, keepData: false); final success = Completer(); @@ -273,7 +268,8 @@ void main() async { materialInstance: materialInstance); await viewer.setMaterialPropertyFloat4( cube, "baseColorFactor", 0, 0.0, 1.0, 0.0, 0.0); - await testHelper.capture(viewer, "geometry_cube_with_custom_material_ubershader"); + await testHelper.capture( + viewer, "geometry_cube_with_custom_material_ubershader"); await viewer.removeEntity(cube); await viewer.destroyMaterialInstance(materialInstance); }); @@ -292,7 +288,7 @@ void main() async { GeometryHelper.cube(uvs: true, normals: true), materialInstance: materialInstance); var textureData = - File("$testDir/cube_texture_512x512.png").readAsBytesSync(); + File("${testHelper.testDir}/assets/cube_texture_512x512.png").readAsBytesSync(); var texture = await viewer.createTexture(textureData); await viewer.applyTexture(texture as ThermionFFITexture, cube); await testHelper.capture( @@ -302,6 +298,25 @@ void main() async { await viewer.destroyTexture(texture); }); + test('unlit material with color only', () async { + var viewer = await createViewer(); + await viewer.setCameraPosition(0, 0, 6); + await viewer.setBackgroundColor(1.0, 0.0, 0.0, 1.0); + await viewer.setPostProcessing(true); + await viewer.setToneMapping(ToneMapper.LINEAR); + + var materialInstance = await viewer.createUnlitMaterialInstance(); + var cube = await viewer.createGeometry(GeometryHelper.cube(), + materialInstance: materialInstance); + + await viewer.setMaterialPropertyFloat4( + cube, "baseColorFactor", 0, 0.0, 1.0, 0.0, 1.0); + + await testHelper.capture(viewer, "unlit_material_base_color"); + + await viewer.dispose(); + }); + test('create cube with custom material instance (unlit)', () async { var viewer = await createViewer(); await viewer.setCameraPosition(0, 2, 6); @@ -316,7 +331,7 @@ void main() async { materialInstance: materialInstance); var textureData = - File("$testDir/cube_texture_512x512.png").readAsBytesSync(); + File("${testHelper.testDir}/assets/cube_texture_512x512.png").readAsBytesSync(); var texture = await viewer.createTexture(textureData); await viewer.applyTexture(texture, cube); await testHelper.capture( @@ -385,7 +400,8 @@ void main() async { // if we disable depth write on cube1, then cube2 will always appear in front // (relying on insertion order) materialInstance!.setDepthWriteEnabled(false); - await testHelper.capture(viewer, "material_instance_depth_write_disabled"); + await testHelper.capture( + viewer, "material_instance_depth_write_disabled"); // set priority for the cube1 cube to 7 (render) last, cube1 renders in front await viewer.setPriority(cube1, 7); @@ -395,7 +411,7 @@ void main() async { }); // test('create instance from glb when keepData is true', () async { - // var model = await viewer.loadGlb("$testDir/cube.glb", keepData: true); + // var model = await viewer.loadGlb("${testHelper.testDir}/cube.glb", keepData: true); // await viewer.transformToUnitCube(model); // var instance = await viewer.createInstance(model); // await viewer.setPosition(instance, 0.5, 0.5, -0.5); @@ -409,7 +425,7 @@ void main() async { // }); // test('create instance from glb fails when keepData is false', () async { - // var model = await viewer.loadGlb("$testDir/cube.glb", keepData: false); + // var model = await viewer.loadGlb("${testHelper.testDir}/cube.glb", keepData: false); // bool thrown = false; // try { // await viewer.createInstance(model); @@ -422,27 +438,27 @@ void main() async { // group('Skinning & animations', () { // test('get bone names', () async { - // var model = await viewer.loadGlb("$testDir/assets/shapes.glb"); + // var model = await viewer.loadGlb("${testHelper.testDir}/assets/shapes.glb"); // var names = await viewer.getBoneNames(model); // expect(names.first, "Bone"); // }); // test('reset bones', () async { - // var model = await viewer.loadGlb("$testDir/assets/shapes.glb"); + // var model = await viewer.loadGlb("${testHelper.testDir}/assets/shapes.glb"); // await viewer.resetBones(model); // }); // test('set from BVH', () async { - // var model = await viewer.loadGlb("$testDir/assets/shapes.glb"); + // var model = await viewer.loadGlb("${testHelper.testDir}/assets/shapes.glb"); // var animation = BVHParser.parse( - // File("$testDir/assets/animation.bvh").readAsStringSync(), + // File("${testHelper.testDir}/assets/animation.bvh").readAsStringSync(), // boneRegex: RegExp(r"Bone$")); // await viewer.addBoneAnimation(model, animation); // }); // test('fade in/out', () async { - // var model = await viewer.loadGlb("$testDir/assets/shapes.glb"); + // var model = await viewer.loadGlb("${testHelper.testDir}/assets/shapes.glb"); // var animation = BVHParser.parse( - // File("$testDir/assets/animation.bvh").readAsStringSync(), + // File("${testHelper.testDir}/assets/animation.bvh").readAsStringSync(), // boneRegex: RegExp(r"Bone$")); // await viewer.addBoneAnimation(model, animation, // fadeInInSecs: 0.5, fadeOutInSecs: 0.5); @@ -504,6 +520,25 @@ void main() async { }); group("transforms & parenting", () { + test('set multiple transforms simultaneously with setTransforms', () async { + var viewer = + await createViewer(bg: kRed, cameraPosition: Vector3(0, 0, 5)); + final cube1 = await viewer.createGeometry(GeometryHelper.cube()); + final cube2 = await viewer.createGeometry(GeometryHelper.cube()); + + await viewer.queueTransformUpdates([ + cube1, + cube2 + ], [ + Matrix4.translation(Vector3(-1, 0, 0)), + Matrix4.translation(Vector3(1, 0, 0)) + ]); + + await viewer.render(); + + await testHelper.capture(viewer, "set_multiple_transforms"); + }); + test('getParent and getAncestor both return null when entity has no parent', () async { var viewer = await createViewer(); @@ -584,11 +619,13 @@ void main() async { await viewer .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); - var buffer = File("$testDir/cube.glb").readAsBytesSync(); + var buffer = File("${testHelper.testDir}/cube.glb").readAsBytesSync(); var model = await viewer.loadGlbFromBuffer(buffer, layer: 1); - await testHelper.capture(viewer, "load_glb_from_buffer_with_layer_disabled"); + await testHelper.capture( + viewer, "load_glb_from_buffer_with_layer_disabled"); await viewer.setLayerVisibility(1, true); - await testHelper.capture(viewer, "load_glb_from_buffer_with_layer_enabled"); + await testHelper.capture( + viewer, "load_glb_from_buffer_with_layer_enabled"); }); test('change layer visibility at runtime', () async { @@ -600,7 +637,8 @@ void main() async { .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); var cube = await viewer.createGeometry(GeometryHelper.cube()); - await testHelper.capture(viewer, "change_layer_visibility_at_runtime_default"); + await testHelper.capture( + viewer, "change_layer_visibility_at_runtime_default"); // all entities set to layer 0 by default, so this should now be invisible await viewer.setLayerVisibility(0, false); @@ -620,7 +658,7 @@ void main() async { }); // test('point light', () async { - // var model = await viewer.loadGlb("$testDir/cube.glb"); + // var model = await viewer.loadGlb("${testHelper.testDir}/cube.glb"); // await viewer.transformToUnitCube(model); // var light = await viewer.addLight( // LightType.POINT, 6500, 1000000, 0, 2, 0, 0, -1, 0, @@ -635,7 +673,7 @@ void main() async { // }); // test('set point light position', () async { - // var model = await viewer.loadGlb("$testDir/cube.glb"); + // var model = await viewer.loadGlb("${testHelper.testDir}/cube.glb"); // await viewer.transformToUnitCube(model); // var light = await viewer.addLight( // LightType.POINT, 6500, 1000000, 0, 2, 0, 0, -1, 0, @@ -651,7 +689,7 @@ void main() async { // }); // test('directional light', () async { - // var model = await viewer.loadGlb("$testDir/cube.glb"); + // var model = await viewer.loadGlb("${testHelper.testDir}/cube.glb"); // await viewer.transformToUnitCube(model); // var light = await viewer.addLight( // LightType.SUN, 6500, 1000000, 0, 0, 0, 0, -1, 0); @@ -665,7 +703,7 @@ void main() async { // }); // test('set directional light direction', () async { - // var model = await viewer.loadGlb("$testDir/cube.glb"); + // var model = await viewer.loadGlb("${testHelper.testDir}/cube.glb"); // await viewer.transformToUnitCube(model); // var light = await viewer.addLight( // LightType.SUN, 6500, 1000000, 0, 0, 0, 0, -1, 0); @@ -682,7 +720,7 @@ void main() async { group("stencil", () { test('set stencil highlight for glb', () async { final viewer = await createViewer(); - var model = await viewer.loadGlb("$testDir/cube.glb", keepData: true); + var model = await viewer.loadGlb("${testHelper.testDir}/cube.glb", keepData: true); await viewer.setPostProcessing(true); var light = await viewer.addLight( @@ -723,7 +761,7 @@ void main() async { await viewer .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); - var cube1 = await viewer.loadGlb("$testDir/cube.glb", keepData: true); + var cube1 = await viewer.loadGlb("${testHelper.testDir}/cube.glb", keepData: true); await viewer.transformToUnitCube(cube1); await viewer.setStencilHighlight(cube1); @@ -754,7 +792,8 @@ void main() async { await viewer.removeStencilHighlight(cube1); await viewer.removeStencilHighlight(cube2); - await testHelper.capture(viewer, "stencil_highlight_multiple_geometry_removed"); + await testHelper.capture( + viewer, "stencil_highlight_multiple_geometry_removed"); }); test('set stencil highlight for multiple gltf assets ', () async { @@ -765,9 +804,9 @@ void main() async { await viewer .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); - var cube1 = await viewer.loadGlb("$testDir/cube.glb", keepData: true); + var cube1 = await viewer.loadGlb("${testHelper.testDir}/cube.glb", keepData: true); await viewer.transformToUnitCube(cube1); - var cube2 = await viewer.loadGlb("$testDir/cube.glb", keepData: true); + var cube2 = await viewer.loadGlb("${testHelper.testDir}/cube.glb", keepData: true); await viewer.transformToUnitCube(cube2); await viewer.setPosition(cube2, 0.5, 0.5, 0); await viewer.setStencilHighlight(cube1); @@ -778,7 +817,8 @@ void main() async { await viewer.removeStencilHighlight(cube1); await viewer.removeStencilHighlight(cube2); - await testHelper.capture(viewer, "stencil_highlight_multiple_geometry_removed"); + await testHelper.capture( + viewer, "stencil_highlight_multiple_geometry_removed"); }); }); @@ -787,7 +827,7 @@ void main() async { var viewer = await createViewer(); var textureData = - File("$testDir/cube_texture_512x512.png").readAsBytesSync(); + File("${testHelper.testDir}/assets/cube_texture_512x512.png").readAsBytesSync(); var texture = await viewer.createTexture(textureData); await viewer.setBackgroundColor(0.0, 0.0, 0.0, 1.0); @@ -853,7 +893,7 @@ void main() async { // await viewer.setMaterialPropertyFloat4( // cube, "baseColorFactor", 0, 1.0, 1.0, 1.0, 1.0); // var textureData = - // File("$testDir/cube_texture_512x512.png").readAsBytesSync(); + // File("${testHelper.testDir}/assets/cube_texture_512x512.png").readAsBytesSync(); // var texture = await viewer.createTexture(textureData); // await viewer.applyTexture(texture, cube, // materialIndex: 0, parameterName: "baseColorMap"); diff --git a/thermion_dart/test/material_tests.dart b/thermion_dart/test/material_tests.dart new file mode 100644 index 00000000..2a50bc09 --- /dev/null +++ b/thermion_dart/test/material_tests.dart @@ -0,0 +1,461 @@ +import 'dart:async'; +import 'dart:io'; +import 'dart:math'; +import 'package:thermion_dart/src/viewer/src/events.dart'; +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("integration"); + + group("texture tests", () { + test('apply texture to custom ubershader material instance', () async { + var viewer = await createViewer(); + 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)); + await viewer.setBackgroundColor(1.0, 0.0, 0.0, 1.0); + + var materialInstance = await viewer.createUbershaderMaterialInstance(); + final cube = await viewer.createGeometry( + GeometryHelper.cube(uvs: true, normals: true), + materialInstance: materialInstance); + var textureData = + File("${testHelper.testDir}/assets/cube_texture_512x512.png").readAsBytesSync(); + var texture = await viewer.createTexture(textureData); + await viewer.applyTexture(texture as ThermionFFITexture, cube); + await testHelper.capture( + viewer, "geometry_cube_with_custom_material_ubershader_texture"); + await viewer.removeEntity(cube); + await viewer.destroyMaterialInstance(materialInstance); + await viewer.destroyTexture(texture); + }); + + test('unlit material with color only', () async { + var viewer = await createViewer(); + await viewer.setCameraPosition(0, 0, 6); + await viewer.setBackgroundColor(1.0, 0.0, 0.0, 1.0); + await viewer.setPostProcessing(true); + await viewer.setToneMapping(ToneMapper.LINEAR); + + var materialInstance = await viewer.createUnlitMaterialInstance(); + var cube = await viewer.createGeometry(GeometryHelper.cube(), + materialInstance: materialInstance); + + await viewer.setMaterialPropertyFloat4( + cube, "baseColorFactor", 0, 0.0, 1.0, 0.0, 1.0); + + await testHelper.capture(viewer, "unlit_material_base_color"); + + await viewer.dispose(); + }); + + test('create cube with custom material instance (unlit)', () async { + var viewer = await createViewer(); + await viewer.setCameraPosition(0, 2, 6); + await viewer + .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 8)); + await viewer.setBackgroundColor(1.0, 0.0, 0.0, 1.0); + await viewer.setPostProcessing(true); + await viewer.setToneMapping(ToneMapper.LINEAR); + + var materialInstance = await viewer.createUnlitMaterialInstance(); + var cube = await viewer.createGeometry(GeometryHelper.cube(), + materialInstance: materialInstance); + + var textureData = + File("${testHelper.testDir}/assets/cube_texture_512x512.png").readAsBytesSync(); + var texture = await viewer.createTexture(textureData); + await viewer.applyTexture(texture, cube); + await testHelper.capture( + viewer, "geometry_cube_with_custom_material_unlit_texture_only"); + await viewer.removeEntity(cube); + + cube = await viewer.createGeometry(GeometryHelper.cube(), + materialInstance: materialInstance); + // reusing same material instance, so set baseColorIndex to -1 to disable the texture + await viewer.setMaterialPropertyInt(cube, "baseColorIndex", 0, -1); + await viewer.setMaterialPropertyFloat4( + cube, "baseColorFactor", 0, 0.0, 1.0, 0.0, 1.0); + await testHelper.capture( + viewer, "geometry_cube_with_custom_material_unlit_color_only"); + await viewer.removeEntity(cube); + + cube = await viewer.createGeometry(GeometryHelper.cube(), + materialInstance: materialInstance); + // now set baseColorIndex to 0 to enable the texture and the base color + await viewer.setMaterialPropertyInt(cube, "baseColorIndex", 0, 0); + await viewer.setMaterialPropertyFloat4( + cube, "baseColorFactor", 0, 0.0, 1.0, 0.0, 0.5); + await viewer.applyTexture(texture, cube); + + await testHelper.capture( + viewer, "geometry_cube_with_custom_material_unlit_color_and_texture"); + + await viewer.removeEntity(cube); + + await viewer.destroyTexture(texture); + await viewer.destroyMaterialInstance(materialInstance); + await viewer.dispose(); + }); + + test('create sphere (no normals)', () async { + var viewer = await 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"); + }); + }); + + group("MaterialInstance", () { + test('disable depth write', () async { + var viewer = await createViewer(); + await viewer.setBackgroundColor(1.0, 0.0, 0.0, 1.0); + await viewer.setCameraPosition(0, 0, 6); + await viewer.addDirectLight( + DirectLight.sun(direction: Vector3(0, 0, -1)..normalize())); + + final cube1 = await viewer.createGeometry(GeometryHelper.cube()); + var materialInstance = await viewer.getMaterialInstanceAt(cube1, 0); + + final cube2 = await viewer.createGeometry(GeometryHelper.cube()); + await viewer.setMaterialPropertyFloat4( + cube2, "baseColorFactor", 0, 0, 1, 0, 1); + await viewer.setPosition(cube2, 1.0, 0.0, -1.0); + + expect(materialInstance, isNotNull); + + // with depth write enabled on both materials, cube2 renders behind the white cube + await testHelper.capture(viewer, "material_instance_depth_write_enabled"); + + // if we disable depth write on cube1, then cube2 will always appear in front + // (relying on insertion order) + materialInstance!.setDepthWriteEnabled(false); + await testHelper.capture( + viewer, "material_instance_depth_write_disabled"); + + // set priority for the cube1 cube to 7 (render) last, cube1 renders in front + await viewer.setPriority(cube1, 7); + await testHelper.capture( + viewer, "material_instance_depth_write_disabled_with_priority"); + }); + + test('set uv scaling (unlit)', () async { + var viewer = await createViewer(); + await viewer.setBackgroundColor(1.0, 0.0, 0.0, 1.0); + await viewer.setCameraPosition(0, 0, 6); + await viewer.addDirectLight( + DirectLight.sun(direction: Vector3(0, 0, -1)..normalize())); + + final unlitMaterialInstance = await viewer.createUnlitMaterialInstance(); + final cube = await viewer.createGeometry(GeometryHelper.cube(), + materialInstance: unlitMaterialInstance); + await viewer.setMaterialPropertyFloat4( + cube, 'baseColorFactor', 0, 1, 1, 1, 1); + await viewer.setMaterialPropertyInt( + cube, 'baseColorIndex', 0, 1); + unlitMaterialInstance.setParameterFloat2("uvScale", 2.0, 4.0); + + var textureData = + File("${testHelper.testDir}/assets/cube_texture_512x512.png").readAsBytesSync(); + var texture = await viewer.createTexture(textureData); + await viewer.applyTexture(texture, cube); + await testHelper.capture(viewer, "set_uv_scaling"); + }); + }); + + group("stencil", () { + test('set stencil highlight for glb', () async { + final viewer = await createViewer(); + var model = await viewer.loadGlb("${testHelper.testDir}/cube.glb", keepData: true); + await viewer.setPostProcessing(true); + + var light = await viewer.addLight( + LightType.SUN, 6500, 1000000, 0, 0, 0, 0, -1, 0); + await viewer.setLightDirection(light, Vector3(0, 1, -1)); + + await viewer.setBackgroundColor(0.0, 1.0, 0.0, 1.0); + await viewer.setCameraPosition(0, -1, 5); + await viewer + .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), pi / 8)); + await viewer.setStencilHighlight(model); + await testHelper.capture(viewer, "stencil_highlight_glb"); + }); + + test('set stencil highlight for geometry', () async { + var viewer = await createViewer(); + await viewer.setPostProcessing(true); + await viewer.setBackgroundColor(0.0, 1.0, 0.0, 1.0); + await viewer.setCameraPosition(0, 2, 5); + await viewer + .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); + + var cube = await viewer.createGeometry(GeometryHelper.cube()); + await viewer.setStencilHighlight(cube); + + await testHelper.capture(viewer, "stencil_highlight_geometry"); + + await viewer.removeStencilHighlight(cube); + + await testHelper.capture(viewer, "stencil_highlight_geometry_remove"); + }); + + test('set stencil highlight for gltf asset', () async { + var viewer = await createViewer(); + await viewer.setPostProcessing(true); + await viewer.setBackgroundColor(0.0, 1.0, 0.0, 1.0); + await viewer.setCameraPosition(0, 1, 5); + await viewer + .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); + + var cube1 = await viewer.loadGlb("${testHelper.testDir}/cube.glb", keepData: true); + await viewer.transformToUnitCube(cube1); + + await viewer.setStencilHighlight(cube1); + + await testHelper.capture(viewer, "stencil_highlight_gltf"); + + await viewer.removeStencilHighlight(cube1); + + await testHelper.capture(viewer, "stencil_highlight_gltf_removed"); + }); + + test('set stencil highlight for multiple geometry ', () async { + var viewer = await createViewer(); + await viewer.setPostProcessing(true); + await viewer.setBackgroundColor(0.0, 1.0, 0.0, 1.0); + await viewer.setCameraPosition(0, 1, 5); + await viewer + .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); + + var cube1 = await viewer.createGeometry(GeometryHelper.cube()); + var cube2 = await viewer.createGeometry(GeometryHelper.cube()); + await viewer.setPosition(cube2, 0.5, 0.5, 0); + await viewer.setStencilHighlight(cube1); + await viewer.setStencilHighlight(cube2, r: 0.0, g: 0.0, b: 1.0); + + await testHelper.capture(viewer, "stencil_highlight_multiple_geometry"); + + await viewer.removeStencilHighlight(cube1); + await viewer.removeStencilHighlight(cube2); + + await testHelper.capture( + viewer, "stencil_highlight_multiple_geometry_removed"); + }); + + test('set stencil highlight for multiple gltf assets ', () async { + var viewer = await createViewer(); + await viewer.setPostProcessing(true); + await viewer.setBackgroundColor(0.0, 1.0, 0.0, 1.0); + await viewer.setCameraPosition(0, 1, 5); + await viewer + .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); + + var cube1 = await viewer.loadGlb("${testHelper.testDir}/cube.glb", keepData: true); + await viewer.transformToUnitCube(cube1); + var cube2 = await viewer.loadGlb("${testHelper.testDir}/cube.glb", keepData: true); + await viewer.transformToUnitCube(cube2); + await viewer.setPosition(cube2, 0.5, 0.5, 0); + await viewer.setStencilHighlight(cube1); + await viewer.setStencilHighlight(cube2, r: 0.0, g: 0.0, b: 1.0); + + await testHelper.capture(viewer, "stencil_highlight_multiple_geometry"); + + await viewer.removeStencilHighlight(cube1); + await viewer.removeStencilHighlight(cube2); + + await testHelper.capture( + viewer, "stencil_highlight_multiple_geometry_removed"); + }); + }); + + group("texture", () { + test("create/apply/dispose texture", () async { + var viewer = await createViewer(); + + var textureData = + File("${testHelper.testDir}/assets/cube_texture_512x512.png").readAsBytesSync(); + + var texture = await viewer.createTexture(textureData); + await viewer.setBackgroundColor(0.0, 0.0, 0.0, 1.0); + await viewer.addDirectLight( + DirectLight.sun(direction: Vector3(0, -10, -1)..normalize())); + await viewer.addDirectLight(DirectLight.spot( + intensity: 1000000, + position: Vector3(0, 0, 1.5), + direction: Vector3(0, 0, -1)..normalize(), + falloffRadius: 10, + spotLightConeInner: 1, + spotLightConeOuter: 1)); + await viewer.setCameraPosition(0, 2, 6); + await viewer + .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 8)); + var materialInstance = + await viewer.createUbershaderMaterialInstance(unlit: true); + var cube = await viewer.createGeometry(GeometryHelper.cube(), + materialInstance: materialInstance); + + await viewer.setPostProcessing(true); + await viewer.setToneMapping(ToneMapper.LINEAR); + + await viewer.applyTexture(texture, cube, + materialIndex: 0, parameterName: "baseColorMap"); + + await testHelper.capture(viewer, "texture_applied_to_geometry"); + + await viewer.removeEntity(cube); + await viewer.destroyTexture(texture); + }); + }); + + group("render thread", () { + test("request frame on render thread", () async { + var viewer = await createViewer(); + viewer.requestFrame(); + + await Future.delayed(Duration(milliseconds: 20)); + await viewer.dispose(); + }); + }); + + // group("unproject", () { + // test("unproject", () async { + // final dimensions = (width: 1280, height: 768); + + // var viewer = await createViewer(viewportDimensions: dimensions); + // await viewer.setPostProcessing(false); + // // await viewer.setToneMapping(ToneMapper.LINEAR); + // await viewer.setBackgroundColor(1.0, 1.0, 1.0, 1.0); + // // await viewer.createIbl(1.0, 1.0, 1.0, 100000); + // await viewer.addLight(LightType.SUN, 6500, 100000, -2, 0, 0, 1, -1, 0); + // await viewer.addLight(LightType.SPOT, 6500, 500000, 0, 0, 2, 0, 0, -1, + // falloffRadius: 10, spotLightConeInner: 1.0, spotLightConeOuter: 2.0); + + // await viewer.setCameraPosition(-3, 4, 6); + // await viewer.setCameraRotation( + // Quaternion.axisAngle(Vector3(0, 1, 0), -pi / 8) * + // Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 6)); + // var cube = + // await viewer.createGeometry(GeometryHelper.cube(), keepData: true); + // await viewer.setMaterialPropertyFloat4( + // cube, "baseColorFactor", 0, 1.0, 1.0, 1.0, 1.0); + // var textureData = + // File("${testHelper.testDir}/assets/cube_texture_512x512.png").readAsBytesSync(); + // var texture = await viewer.createTexture(textureData); + // await viewer.applyTexture(texture, cube, + // materialIndex: 0, parameterName: "baseColorMap"); + + // var numFrames = 60; + + // // first do the render + // for (int i = 0; i < numFrames; i++) { + // await viewer.setCameraPosition(-3 + (i / numFrames * 2), 4, 6); + + // await viewer.setCameraRotation( + // Quaternion.axisAngle(Vector3(0, 1, 0), -pi / 8) * + // Quaternion.axisAngle( + // Vector3(1, 0, 0), -pi / 6 - (i / numFrames * pi / 6))); + + // var rendered = await testHelper.capture(viewer, "unproject_render$i"); + // var renderPng = + // await pixelsToPng(rendered, dimensions.width, dimensions.height); + + // File("${outDir.path}/unproject_render${i}.png") + // .writeAsBytesSync(renderPng); + // } + + // // then go off and convert the video + + // // now unproject the render back onto the geometry + // final textureSize = (width: 1280, height: 768); + // var pixels = []; + // // note we skip the first frame + // for (int i = 0; i < numFrames; i++) { + // await viewer.setCameraPosition(-3 + (i / numFrames * 2), 4, 6); + + // await viewer.setCameraRotation( + // Quaternion.axisAngle(Vector3(0, 1, 0), -pi / 8) * + // Quaternion.axisAngle( + // Vector3(1, 0, 0), -pi / 6 - (i / numFrames * pi / 6))); + + // var input = pngToPixelBuffer(File( + // "${outDir.path}/a8c317af-6081-4848-8a06-f6b69bc57664_${i + 1}.png") + // .readAsBytesSync()); + // var pixelBuffer = await (await viewer as ThermionViewerFFI).unproject( + // cube, + // input, + // dimensions.width, + // dimensions.height, + // textureSize.width, + // textureSize.height); + + // // var png = await pixelsToPng(Uint8List.fromList(pixelBuffer), + // // dimensions.width, dimensions.height); + + // await savePixelBufferToBmp( + // pixelBuffer, + // textureSize.width, + // textureSize.height, + // p.join(outDir.path, "unprojected_texture${i}.bmp")); + + // pixels.add(pixelBuffer); + + // if (i > 10) { + // break; + // } + // } + + // // } + + // final aggregatePixelBuffer = medianImages(pixels); + // await savePixelBufferToBmp(aggregatePixelBuffer, textureSize.width, + // textureSize.height, "unproject_texture.bmp"); + // var pixelBufferPng = await pixelsToPng( + // Uint8List.fromList(aggregatePixelBuffer), + // dimensions.width, + // dimensions.height); + // File("${outDir.path}/unproject_texture.png") + // .writeAsBytesSync(pixelBufferPng); + + // await viewer.setPostProcessing(true); + // await viewer.setToneMapping(ToneMapper.LINEAR); + + // final unlit = await viewer.createUnlitMaterialInstance(); + // await viewer.removeEntity(cube); + // cube = await viewer.createGeometry(GeometryHelper.cube(), + // materialInstance: unlit); + // var reconstructedTexture = await viewer.createTexture(pixelBufferPng); + // await viewer.applyTexture(reconstructedTexture, cube); + + // await viewer.setCameraRotation( + // Quaternion.axisAngle(Vector3(0, 1, 0), -pi / 8) * + // Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 6)); + // await testHelper.capture(viewer, "unproject_reconstruct"); + + // // now re-render + // for (int i = 0; i < numFrames; i++) { + // await viewer.setCameraPosition(-3 + (i / numFrames * 2), 4, 6); + + // await viewer.setCameraRotation( + // Quaternion.axisAngle(Vector3(0, 1, 0), -pi / 8) * + // Quaternion.axisAngle( + // Vector3(1, 0, 0), -pi / 6 - (i / numFrames * pi / 6))); + + // var rendered = await testHelper.capture(viewer, "unproject_rerender$i"); + // var renderPng = + // await pixelsToPng(rendered, dimensions.width, dimensions.height); + + // File("${outDir.path}/unproject_rerender${i}.png") + // .writeAsBytesSync(renderPng); + // } + // }, timeout: Timeout(Duration(minutes: 2))); + // }); +} diff --git a/thermion_dart/test/skybox_tests.dart b/thermion_dart/test/skybox_tests.dart new file mode 100644 index 00000000..723ac172 --- /dev/null +++ b/thermion_dart/test/skybox_tests.dart @@ -0,0 +1,19 @@ +import 'dart:async'; +import 'package:test/test.dart'; + +import 'helpers.dart'; + +void main() async { + final testHelper = TestHelper("integration"); + + group("skybox", () { + test('load skybox', () async { + var viewer = await createViewer(); + await viewer.loadSkybox( + "file:///${testHelper.testDir}/assets/default_env_skybox.ktx"); + await testHelper.capture(viewer, "load_skybox"); + await viewer.removeSkybox(); + await testHelper.capture(viewer, "remove_skybox"); + }); + }); +}