diff --git a/thermion_dart/test/texture_tests.dart b/thermion_dart/test/texture_tests.dart index d45c5076..ea5e67fa 100644 --- a/thermion_dart/test/texture_tests.dart +++ b/thermion_dart/test/texture_tests.dart @@ -1,13 +1,18 @@ +@Timeout(const Duration(seconds: 600)) +import 'dart:ffi'; import 'dart:io'; +import 'dart:math'; +import 'dart:typed_data'; 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("material"); group("image", () { - test('set texture from decoded image', () async { + test('create 2D texture & set from decoded image', () async { await testHelper.withViewer((viewer) async { var imageData = File("${testHelper.testDir}/assets/cube_texture_512x512.png") @@ -26,7 +31,7 @@ void main() async { }, bg: kRed); }); - test('setImage', () async { + test('create 2D texture and set image from raw buffer', () async { await testHelper.withViewer((viewer) async { var imageData = File("${testHelper.testDir}/assets/cube_texture_512x512.png") @@ -40,15 +45,78 @@ void main() async { await image.getWidth(), await image.getHeight(), textureFormat: TextureFormat.RGBA32F); var data = await image.getData(); - - await texture.setImage( - 0, - data.buffer.asUint8List(data.offsetInBytes), - 512, 512, 4, PixelDataFormat.RGBA, PixelDataType.FLOAT - ); + + await texture.setImage(0, data.buffer.asUint8List(data.offsetInBytes), + 512, 512, 4, PixelDataFormat.RGBA, PixelDataType.FLOAT); await texture.dispose(); }, bg: kRed); }); + + test('create 3D texture and set image from buffers', () async { + await testHelper.withViewer((viewer) async { + final width = 128; + final height = 128; + final channels = 4; + final depth = 5; + final texture = await viewer.createTexture(width, height, + depth: depth, + textureSamplerType: TextureSamplerType.SAMPLER_3D, + textureFormat: TextureFormat.RGBA32F); + + for (int i = 0; i < depth; i++) { + final buffer = Uint8List(width * height * channels * sizeOf()); + await texture.setImage3D(0, 0, 0, i, width, height, channels, 1, + buffer, PixelDataFormat.RGBA, PixelDataType.FLOAT); + } + await texture.dispose(); + }, bg: kRed); + }); + + test('apply 3D texture material ', () async { + await testHelper.withViewer((viewer) async { + final material = await viewer.createMaterial(File( + "/Users/nickfisher/Documents/thermion/materials/texture_array.filamat") + .readAsBytesSync()); + final materialInstance = await material.createInstance(); + final sampler = await viewer.createTextureSampler(); + final cube = await viewer.createGeometry(GeometryHelper.cube(), + materialInstances: [materialInstance]); + + final width = 1; + final height = 1; + final channels = 4; + final numTextures = 2; + final texture = await viewer.createTexture(width, height, + depth: numTextures, + textureSamplerType: TextureSamplerType.SAMPLER_3D, + textureFormat: TextureFormat.RGBA32F); + + for (int i = 0; i < numTextures; i++) { + var pixelBuffer = Float32List.fromList( + [i == 0 ? 1.0 : 0.0, i == 1 ? 1.0 : 0.0, 0.0, 1.0]); + var byteBuffer = + pixelBuffer.buffer.asUint8List(pixelBuffer.offsetInBytes); + + await texture.setImage3D(0, 0, 0, i, width, height, channels, 1, + byteBuffer, PixelDataFormat.RGBA, PixelDataType.FLOAT); + } + + await materialInstance.setParameterTexture( + "textures", texture, sampler); + await materialInstance.setParameterInt("activeTexture", 0); + + await testHelper.capture(viewer, "3d_texture_0"); + + await materialInstance.setParameterInt("activeTexture", 1); + + await testHelper.capture(viewer, "3d_texture_1"); + + await viewer.destroyAsset(cube); + await materialInstance.dispose(); + await material.dispose(); + await texture.dispose(); + }); + }); }); group("sampler", () { @@ -58,4 +126,258 @@ void main() async { }, bg: kRed); }); }); + + group('projection', () { + test('project texture & UV unwrap', () async { + await testHelper.withViewer((viewer) async { + // setup base material + geometry + final sampler = await viewer.createTextureSampler(); + var inputTextureData = + File("${testHelper.testDir}/assets/cube_texture2_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 material = + await viewer.createUbershaderMaterialInstance(unlit: true); + + // var material = + // await viewer.createUnlitMaterialInstance(); + await material.setParameterInt("baseColorIndex", 0); + await material.setParameterTexture( + "baseColorMap", inputTexture, sampler); + await material.setParameterFloat4( + "baseColorFactor", 1.0, 1.0, 1.0, 1.0); + + final cube = await viewer.createGeometry(GeometryHelper.cube(), + materialInstances: [material]); + + await cube.removeFromScene(); + var objects = {"cube": cube}; + + // setup render target + 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); + + var captureUvMaterial = await viewer.createMaterial(File( + "/Users/nickfisher/Documents/thermion/materials/capture_uv.filamat") + .readAsBytesSync()); + expect(await captureUvMaterial.hasParameter("flipUVs"), true); + var captureUvMaterialInstance = + await captureUvMaterial.createInstance(); + await captureUvMaterialInstance.setParameterBool("flipUVs", false); + + await captureUvMaterialInstance.setParameterTexture( + "color", await rt.getColorTexture(), sampler); + + for (final entry in objects.entries) { + final object = entry.value; + final key = entry.key; + + await object.addToScene(); + + var divisions = 8; + for (int i = 0; i < divisions; i++) { + var matrix = makeViewMatrix( + Vector3(sin(i / divisions * pi) * 5, 0, + cos(i / divisions * pi) * 5), + Vector3.zero(), + Vector3(0, 1, 0)); + matrix.invert(); + await viewer.setCameraModelMatrix4(matrix); + await testHelper.capture(viewer, "color_${key}_$i", + renderTarget: rt); + + await object.setMaterialInstanceAt(captureUvMaterialInstance); + + var projected = await testHelper + .capture(viewer, "uv_capture_${key}_$i", renderTarget: rt); + + var floatPixelBuffer = Float32List.fromList( + projected.map((p) => p.toDouble() / 255.0).toList()); + + final reappliedImage = await viewer.createImage(512, 512, 4); + final data = await reappliedImage.getData(); + data.setRange(0, data.length, floatPixelBuffer); + final reappliedTexture = await viewer.createTexture(512, 512, + textureFormat: TextureFormat.RGBA32F); + await reappliedTexture.setLinearImage( + reappliedImage, PixelDataFormat.RGBA, PixelDataType.FLOAT); + + await material.setParameterTexture( + "baseColorMap", reappliedTexture, sampler); + await object.setMaterialInstanceAt(material); + await testHelper.capture(viewer, "retextured_${key}_$i", + renderTarget: rt); + await material.setParameterTexture( + "baseColorMap", inputTexture, sampler); + } + await viewer.destroyAsset(object); + } + + // cleanup + + await sampler.dispose(); + await captureUvMaterialInstance.dispose(); + await captureUvMaterial.dispose(); + }, viewportDimensions: (width: 512, height: 512)); + }); + + test('view dependent texture mapping', () async { + await testHelper.withViewer((viewer) async { + final sampler = await viewer.createTextureSampler(); + final numCameraPositions = 3; + final textureDims = (width: 1, height: 1, channels: 4); + + var texture = await viewer.createTexture( + textureDims.width, textureDims.height, + textureSamplerType: TextureSamplerType.SAMPLER_3D, + depth: numCameraPositions, + textureFormat: TextureFormat.RGBA32F); + + for (int i = 0; i < numCameraPositions; i++) { + final pixelBuffer = Float32List.fromList( + [1 - (i/ numCameraPositions), i / numCameraPositions, 0.0, 1.0]); + var byteBuffer = + pixelBuffer.buffer.asUint8List(pixelBuffer.offsetInBytes); + await texture.setImage3D( + 0, + 0, + 0, + i, + textureDims.width, + textureDims.height, + textureDims.channels, + 1, + byteBuffer, + PixelDataFormat.RGBA, + PixelDataType.FLOAT); + } + final vdtm = await viewer.createMaterial( + File("/Users/nickfisher/Documents/thermion/materials/vdtm.filamat") + .readAsBytesSync()); + + final materialInstance = await vdtm.createInstance(); + await materialInstance.setParameterTexture( + "perspectives", texture, sampler); + + final cube = await viewer.createGeometry(GeometryHelper.cube(), + materialInstances: [materialInstance]); + + final cameraPositions = [Vector3(0, 0, 5), Vector3(0, 0, -5)]; + await materialInstance.setParameterFloat3Array( + "cameraPositions", cameraPositions); + + for (int i = 0; i < 8; i++) { + final cameraPosition = Vector3(sin(pi * (i / 7)) * 5, 0, cos(pi * (i / 7))* 5 ); + final viewMatrix = + makeViewMatrix(cameraPosition, Vector3.zero(), Vector3(0, 1, 0)); + viewMatrix.invert(); + await viewer.setCameraModelMatrix4(viewMatrix); + + await testHelper.capture(viewer, "view_dependent_texture_mapping_$i"); + } + + // // var material = + // // await viewer.createUnlitMaterialInstance(); + // await material.setParameterInt("baseColorIndex", 0); + // await material.setParameterTexture( + // "baseColorMap", inputTexture, sampler); + // await material.setParameterFloat4( + // "baseColorFactor", 1.0, 1.0, 1.0, 1.0); + // // final plane = await viewer.createGeometry( + // // GeometryHelper.plane(width: 3, height: 3), + // // materialInstances: [material]); + // // await plane.removeFromScene(); + // // var cube = await viewer + // // .loadGlb("file://${testHelper.testDir}/assets/cube.glb"); + // // await cube.setMaterialInstanceAt(material); + // final cube = await viewer.createGeometry(GeometryHelper.cube(), + // materialInstances: [material]); + + // await cube.removeFromScene(); + // var objects = { + // // "plane": plane, + // "cube": cube + // }; + + // // setup render target + // 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); + + // var captureUvMaterial = await viewer.createMaterial(File( + // "/Users/nickfisher/Documents/thermion/materials/capture_uv.filamat") + // .readAsBytesSync()); + // expect(await captureUvMaterial.hasParameter("flipUVs"), true); + // var captureUvMaterialInstance = + // await captureUvMaterial.createInstance(); + // await captureUvMaterialInstance.setParameterBool("flipUVs", false); + + // await captureUvMaterialInstance.setParameterTexture( + // "color", await rt.getColorTexture(), sampler); + + // for (final entry in objects.entries) { + // final object = entry.value; + // final key = entry.key; + + // await object.addToScene(); + + // var divisions = 8; + // for (int i = 0; i < divisions; i++) { + // var matrix = makeViewMatrix( + // Vector3(sin(i / divisions * pi) * 5, 0, + // cos(i / divisions * pi) * 5), + // Vector3.zero(), + // Vector3(0, 1, 0)); + // matrix.invert(); + // await viewer.setCameraModelMatrix4(matrix); + // await testHelper.capture(viewer, "color_${key}_$i", + // renderTarget: rt); + // // continue; + + // await object.setMaterialInstanceAt(captureUvMaterialInstance); + + // var projected = await testHelper + // .capture(viewer, "uv_capture_${key}_$i", renderTarget: rt); + // // File("/tmp/projected_texture.bmp") + // // .writeAsBytesSync(await pixelBufferToBmp(projected, 512, 512)); + // var floatPixelBuffer = Float32List.fromList( + // projected.map((p) => p.toDouble() / 255.0).toList()); + + // final reappliedImage = await viewer.createImage(512, 512, 4); + // final data = await reappliedImage.getData(); + // data.setRange(0, data.length, floatPixelBuffer); + // final reappliedTexture = await viewer.createTexture(512, 512, + // textureFormat: TextureFormat.RGBA32F); + // await reappliedTexture.setLinearImage( + // reappliedImage, PixelDataFormat.RGBA, PixelDataType.FLOAT); + + // await material.setParameterTexture( + // "baseColorMap", reappliedTexture, sampler); + // await object.setMaterialInstanceAt(material); + // await testHelper.capture(viewer, "retextured_${key}_$i", + // renderTarget: rt); + // await material.setParameterTexture( + // "baseColorMap", inputTexture, sampler); + // } + // await viewer.destroyAsset(object); + // } + + // // cleanup + + // await sampler.dispose(); + // await captureUvMaterialInstance.dispose(); + // await captureUvMaterial.dispose(); + }, viewportDimensions: (width: 512, height: 512)); + }); + }); }