diff --git a/thermion_dart/test/unlit_material_tests.dart b/thermion_dart/test/unlit_material_tests.dart index 69bc3f41..8c2bbf97 100644 --- a/thermion_dart/test/unlit_material_tests.dart +++ b/thermion_dart/test/unlit_material_tests.dart @@ -4,6 +4,39 @@ import 'package:thermion_dart/thermion_dart.dart'; import 'package:test/test.dart'; import 'helpers.dart'; +Future< + ({ + ThermionAsset blueCube, + MaterialInstance blueMaterialInstance, + ThermionAsset greenCube, + MaterialInstance greenMaterialInstance + })> setup(ThermionViewer viewer) async { + var blueMaterialInstance = + await FilamentApp.instance!.createUnlitMaterialInstance(); + final blueCube = await viewer.createGeometry(GeometryHelper.cube(), + materialInstances: [blueMaterialInstance]); + await blueMaterialInstance.setParameterFloat4( + "baseColorFactor", 0.0, 0.0, 1.0, 1.0); + + // Position blue cube slightly behind and to the right + await blueCube.setTransform(Matrix4.translation(Vector3(1.0, 0.0, -1.0))); + + var greenMaterialInstance = + await FilamentApp.instance!.createUnlitMaterialInstance(); + final greenCube = await viewer.createGeometry(GeometryHelper.cube(), + materialInstances: [greenMaterialInstance]); + await greenMaterialInstance.setParameterFloat4( + "baseColorFactor", 0.0, 1.0, 0.0, 1.0); + + return ( + blueCube: blueCube, + blueMaterialInstance: blueMaterialInstance, + greenCube: greenCube, + greenMaterialInstance: greenMaterialInstance + ); +} + + void main() async { final testHelper = TestHelper("material"); @@ -179,4 +212,115 @@ void main() async { await viewer.dispose(); }); + + test('disable depth write', () async { + await testHelper.withViewer((viewer) async { + final ( + :blueCube, + :blueMaterialInstance, + :greenCube, + :greenMaterialInstance + ) = await setup(viewer); + + // With depth write enabled on both materials, green cube renders behind the blue cube + await testHelper.capture( + viewer.view, "material_instance_depth_write_enabled"); + + // Disable depth write on green cube, blue cube will always appear in front (green cube renders behind everything, including the image material, so not it's not visible at all) + await greenMaterialInstance.setDepthWriteEnabled(false); + await testHelper.capture( + viewer.view, "material_instance_depth_write_disabled"); + + // Set priority for greenCube to render last, making it appear in front + await viewer.setPriority(greenCube.entity, 7); + await testHelper.capture(viewer.view, + "material_instance_depth_write_disabled_with_priority"); + }); + }); + + test('enable stencil write', () async { + await testHelper.withViewer((viewer) async { + final ( + :blueCube, + :blueMaterialInstance, + :greenCube, + :greenMaterialInstance + ) = await setup(viewer); + + // force depth to always pass so we're just comparing stencil test + await greenMaterialInstance.setDepthFunc(SamplerCompareFunction.A); + await blueMaterialInstance.setDepthFunc(SamplerCompareFunction.A); + + await testHelper.capture( + viewer.view, "material_instance_depth_pass_stencil_disabled"); + + assert(await greenMaterialInstance.isStencilWriteEnabled() == false); + assert(await blueMaterialInstance.isStencilWriteEnabled() == false); + + await greenMaterialInstance.setStencilWriteEnabled(true); + await blueMaterialInstance.setStencilWriteEnabled(true); + + assert(await greenMaterialInstance.isStencilWriteEnabled() == true); + assert(await blueMaterialInstance.isStencilWriteEnabled() == true); + + // just a sanity check, no difference from the last + await testHelper.capture( + viewer.view, "material_instance_depth_pass_stencil_enabled"); + }, postProcessing: true, bg: null); + }); + + test('stencil always fail', () async { + await testHelper.withViewer((viewer) async { + final ( + :blueCube, + :blueMaterialInstance, + :greenCube, + :greenMaterialInstance + ) = await setup(viewer); + + // force depth to always pass so we're just comparing stencil test + await greenMaterialInstance.setDepthFunc(SamplerCompareFunction.A); + await blueMaterialInstance.setDepthFunc(SamplerCompareFunction.A); + + await greenMaterialInstance.setStencilWriteEnabled(true); + + assert(await greenMaterialInstance.isStencilWriteEnabled() == true); + + await greenMaterialInstance + .setStencilCompareFunction(SamplerCompareFunction.N); + + // green cube isn't rendered + await testHelper.capture( + viewer.view, "material_instance_stencil_always_fail"); + }, postProcessing: true, bg: null); + }); + + test('fail stencil not equal', () async { + await testHelper.withViewer((viewer) async { + final ( + :blueCube, + :blueMaterialInstance, + :greenCube, + :greenMaterialInstance + ) = await setup(viewer); + + // this ensures the blue cube is rendered before the green cube + await viewer.setPriority(blueCube.entity, 0); + await viewer.setPriority(greenCube.entity, 1); + + await blueMaterialInstance.setStencilWriteEnabled(true); + await blueMaterialInstance.setStencilReferenceValue(1); + await blueMaterialInstance + .setStencilCompareFunction(SamplerCompareFunction.A); + await blueMaterialInstance + .setStencilOpDepthStencilPass(StencilOperation.REPLACE); + + await greenMaterialInstance.setStencilReferenceValue(1); + await greenMaterialInstance + .setStencilCompareFunction(SamplerCompareFunction.E); + + // green cube is only rendered where it intersects with the blue cube + await testHelper.capture(viewer.view, "fail_stencil_ne"); + }, postProcessing: true); + }); }