diff --git a/thermion_dart/test/helpers.dart b/thermion_dart/test/helpers.dart new file mode 100644 index 00000000..39b8826d --- /dev/null +++ b/thermion_dart/test/helpers.dart @@ -0,0 +1,193 @@ +import 'dart:io'; + +import 'dart:typed_data'; +import 'package:thermion_dart/thermion_dart.dart'; +import 'package:thermion_dart/thermion_dart/swift/swift_bindings.g.dart'; +import 'package:thermion_dart/thermion_dart/thermion_viewer_ffi.dart'; +import 'package:thermion_dart/thermion_dart/utils/dart_resources.dart'; +import 'package:thermion_dart/thermion_dart/compatibility/compatibility.dart'; +import 'package:thermion_dart/thermion_dart/thermion_viewer.dart'; + +List cubeVertices = [ + // Front face + -1, -1, 1, + 1, -1, 1, + 1, 1, 1, + -1, 1, 1, + + // Back face + -1, -1, -1, + -1, 1, -1, + 1, 1, -1, + 1, -1, -1, + + // Top face + -1, 1, -1, + -1, 1, 1, + 1, 1, 1, + 1, 1, -1, + + // Bottom face + -1, -1, -1, + 1, -1, -1, + 1, -1, 1, + -1, -1, 1, + + // Right face + 1, -1, -1, + 1, 1, -1, + 1, 1, 1, + 1, -1, 1, + + // Left face + -1, -1, -1, + -1, -1, 1, + -1, 1, 1, + -1, 1, -1, +]; + +// Define the indices for the cube +List cubeIndices = [ + // Front face + 0, 3, 2, 0, 2, 1, + // Back face + 4, 7, 6, 4, 6, 5, + // Top face + 8, 11, 10, 8, 10, 9, + // Bottom face + 12, 15, 14, 12, 14, 13, + // Right face + 16, 19, 18, 16, 18, 17, + // Left face + 20, 23, 22, 20, 22, 21 +]; + +final viewportDimensions = (width: 500, height: 500); + + +/// Test files are run in a variety of ways, find this package root in all. +/// +/// Test files can be run from source from any working directory. The Dart SDK +/// `tools/test.py` runs them from the root of the SDK for example. +/// +/// Test files can be run from dill from the root of package. `package:test` +/// does this. +Uri findPackageRoot(String packageName) { + final script = Platform.script; + final fileName = script.name; + if (fileName.endsWith('_test.dart')) { + // We're likely running from source. + var directory = script.resolve('.'); + while (true) { + final dirName = directory.name; + if (dirName == packageName) { + return directory; + } + final parent = directory.resolve('..'); + if (parent == directory) break; + directory = parent; + } + } else if (fileName.endsWith('.dill')) { + final cwd = Directory.current.uri; + final dirName = cwd.name; + if (dirName == packageName) { + return cwd; + } + } + throw StateError("Could not find package root for package '$packageName'. " + 'Tried finding the package root via Platform.script ' + "'${Platform.script.toFilePath()}' and Directory.current " + "'${Directory.current.uri.toFilePath()}'."); +} + +extension on Uri { + String get name => pathSegments.where((e) => e != '').last; +} + +late String testDir; + +Future pixelBufferToBmp( + Uint8List pixelBuffer, int width, int height, String outputPath) async { + // BMP file header (14 bytes) + final fileHeader = ByteData(14); + fileHeader.setUint16(0, 0x4D42, Endian.little); // 'BM' + final fileSize = 54 + width * height * 3; // 54 bytes header + RGB data + fileHeader.setUint32(2, fileSize, Endian.little); + fileHeader.setUint32(10, 54, Endian.little); // Offset to pixel data + + // BMP info header (40 bytes) + final infoHeader = ByteData(40); + infoHeader.setUint32(0, 40, Endian.little); // Info header size + infoHeader.setInt32(4, width, Endian.little); + infoHeader.setInt32(8, -height, Endian.little); // Negative for top-down + infoHeader.setUint16(12, 1, Endian.little); // Number of color planes + infoHeader.setUint16(14, 24, Endian.little); // Bits per pixel (RGB) + infoHeader.setUint32(16, 0, Endian.little); // No compression + infoHeader.setUint32(20, width * height * 3, Endian.little); // Image size + infoHeader.setInt32(24, 2835, Endian.little); // X pixels per meter + infoHeader.setInt32(28, 2835, Endian.little); // Y pixels per meter + + // Calculate row size and padding + final rowSize = (width * 3 + 3) & ~3; + final padding = rowSize - (width * 3); + + // Pixel data (BMP stores in BGR format) + final bmpData = Uint8List(rowSize * height); + for (var y = 0; y < height; y++) { + for (var x = 0; x < width; x++) { + final srcIndex = (y * width + x) * 4; // RGBA format + final dstIndex = y * rowSize + x * 3; // BGR format + bmpData[dstIndex] = pixelBuffer[srcIndex + 2]; // Blue + bmpData[dstIndex + 1] = pixelBuffer[srcIndex + 1]; // Green + bmpData[dstIndex + 2] = pixelBuffer[srcIndex]; // Red + // Alpha channel is discarded + } + // Add padding to the end of each row + for (var p = 0; p < padding; p++) { + bmpData[y * rowSize + width * 3 + p] = 0; + } + } + + // Write BMP file + final file = File(outputPath); + final sink = file.openWrite(); + sink.add(fileHeader.buffer.asUint8List()); + sink.add(infoHeader.buffer.asUint8List()); + sink.add(bmpData); + await sink.close(); + + print('BMP image saved to: $outputPath'); +} + +Future createViewer() async { + + final packageUri = findPackageRoot('thermion_dart'); + + final lib = ThermionDartTexture1(DynamicLibrary.open( + '${packageUri.toFilePath()}/native/lib/macos/swift/libthermion_swift.dylib')); + final object = ThermionDartTexture.new1(lib); + object.initWithWidth_height_( + viewportDimensions.width, viewportDimensions.height); + + final resourceLoader = calloc(1); + var loadToOut = NativeCallable< + Void Function(Pointer, + Pointer)>.listener(DartResourceLoader.loadResource); + + resourceLoader.ref.loadToOut = loadToOut.nativeFunction; + var freeResource = NativeCallable.listener( + DartResourceLoader.freeResource); + resourceLoader.ref.freeResource = freeResource.nativeFunction; + + var viewer = ThermionViewerFFI(resourceLoader: resourceLoader.cast()); + + await viewer.initialized; + await viewer.createSwapChain(viewportDimensions.width.toDouble(), + viewportDimensions.height.toDouble()); + await viewer.createRenderTarget(viewportDimensions.width.toDouble(), + viewportDimensions.height.toDouble(), object.metalTextureAddress); + await viewer.updateViewportAndCameraProjection( + viewportDimensions.width.toDouble(), + viewportDimensions.height.toDouble()); + return viewer; +} diff --git a/thermion_dart/test/integration_test.dart b/thermion_dart/test/integration_test.dart index fafdb5b5..9889b62a 100644 --- a/thermion_dart/test/integration_test.dart +++ b/thermion_dart/test/integration_test.dart @@ -1,148 +1,24 @@ import 'dart:io'; import 'dart:math'; -import 'dart:typed_data'; import 'package:thermion_dart/thermion_dart.dart'; -import 'package:thermion_dart/thermion_dart/swift/swift_bindings.g.dart'; -import 'package:thermion_dart/thermion_dart/thermion_viewer_ffi.dart'; -import 'package:thermion_dart/thermion_dart/utils/dart_resources.dart'; -import 'package:thermion_dart/thermion_dart/compatibility/compatibility.dart'; import 'package:test/test.dart'; import 'package:animation_tools_dart/animation_tools_dart.dart'; import 'package:path/path.dart' as p; import 'package:vector_math/vector_math_64.dart'; -/// Test files are run in a variety of ways, find this package root in all. -/// -/// Test files can be run from source from any working directory. The Dart SDK -/// `tools/test.py` runs them from the root of the SDK for example. -/// -/// Test files can be run from dill from the root of package. `package:test` -/// does this. -Uri findPackageRoot(String packageName) { - final script = Platform.script; - final fileName = script.name; - if (fileName.endsWith('_test.dart')) { - // We're likely running from source. - var directory = script.resolve('.'); - while (true) { - final dirName = directory.name; - if (dirName == packageName) { - return directory; - } - final parent = directory.resolve('..'); - if (parent == directory) break; - directory = parent; - } - } else if (fileName.endsWith('.dill')) { - final cwd = Directory.current.uri; - final dirName = cwd.name; - if (dirName == packageName) { - return cwd; - } - } - throw StateError("Could not find package root for package '$packageName'. " - 'Tried finding the package root via Platform.script ' - "'${Platform.script.toFilePath()}' and Directory.current " - "'${Directory.current.uri.toFilePath()}'."); -} +import 'helpers.dart'; -extension on Uri { - String get name => pathSegments.where((e) => e != '').last; -} - -late String testDir; - -Future pixelBufferToBmp( - Uint8List pixelBuffer, int width, int height, String outputPath) async { - // BMP file header (14 bytes) - final fileHeader = ByteData(14); - fileHeader.setUint16(0, 0x4D42, Endian.little); // 'BM' - final fileSize = 54 + width * height * 3; // 54 bytes header + RGB data - fileHeader.setUint32(2, fileSize, Endian.little); - fileHeader.setUint32(10, 54, Endian.little); // Offset to pixel data - - // BMP info header (40 bytes) - final infoHeader = ByteData(40); - infoHeader.setUint32(0, 40, Endian.little); // Info header size - infoHeader.setInt32(4, width, Endian.little); - infoHeader.setInt32(8, -height, Endian.little); // Negative for top-down - infoHeader.setUint16(12, 1, Endian.little); // Number of color planes - infoHeader.setUint16(14, 24, Endian.little); // Bits per pixel (RGB) - infoHeader.setUint32(16, 0, Endian.little); // No compression - infoHeader.setUint32(20, width * height * 3, Endian.little); // Image size - infoHeader.setInt32(24, 2835, Endian.little); // X pixels per meter - infoHeader.setInt32(28, 2835, Endian.little); // Y pixels per meter - - // Calculate row size and padding - final rowSize = (width * 3 + 3) & ~3; - final padding = rowSize - (width * 3); - - // Pixel data (BMP stores in BGR format) - final bmpData = Uint8List(rowSize * height); - for (var y = 0; y < height; y++) { - for (var x = 0; x < width; x++) { - final srcIndex = (y * width + x) * 4; // RGBA format - final dstIndex = y * rowSize + x * 3; // BGR format - bmpData[dstIndex] = pixelBuffer[srcIndex + 2]; // Blue - bmpData[dstIndex + 1] = pixelBuffer[srcIndex + 1]; // Green - bmpData[dstIndex + 2] = pixelBuffer[srcIndex]; // Red - // Alpha channel is discarded - } - // Add padding to the end of each row - for (var p = 0; p < padding; p++) { - bmpData[y * rowSize + width * 3 + p] = 0; - } - } - - // Write BMP file - final file = File(outputPath); - final sink = file.openWrite(); - sink.add(fileHeader.buffer.asUint8List()); - sink.add(infoHeader.buffer.asUint8List()); - sink.add(bmpData); - await sink.close(); - - print('BMP image saved to: $outputPath'); -} - -final viewportDimensions = (width: 500, height: 500); void main() async { final packageUri = findPackageRoot('thermion_dart'); testDir = Directory("${packageUri.toFilePath()}/test").path; - final lib = ThermionDartTexture1(DynamicLibrary.open( - '${packageUri.toFilePath()}/native/lib/macos/swift/libthermion_swift.dylib')); - final object = ThermionDartTexture.new1(lib); - object.initWithWidth_height_( - viewportDimensions.width, viewportDimensions.height); - - final resourceLoader = calloc(1); - var loadToOut = NativeCallable< - Void Function(Pointer, - Pointer)>.listener(DartResourceLoader.loadResource); - - resourceLoader.ref.loadToOut = loadToOut.nativeFunction; - var freeResource = NativeCallable.listener( - DartResourceLoader.freeResource); - resourceLoader.ref.freeResource = freeResource.nativeFunction; - - var viewer = ThermionViewerFFI(resourceLoader: resourceLoader.cast()); - - await viewer.initialized; - await viewer.createSwapChain(viewportDimensions.width.toDouble(), - viewportDimensions.height.toDouble()); - await viewer.createRenderTarget(viewportDimensions.width.toDouble(), - viewportDimensions.height.toDouble(), object.metalTextureAddress); - await viewer.updateViewportAndCameraProjection( - viewportDimensions.width.toDouble(), - viewportDimensions.height.toDouble()); var outDir = Directory("$testDir/output"); // outDir.deleteSync(recursive: true); outDir.createSync(); - Future _capture(String outputFilename) async { + Future _capture(ThermionViewer viewer, String outputFilename) async { var outPath = p.join(outDir.path, "$outputFilename.bmp"); var pixelBuffer = await viewer.capture(); await pixelBufferToBmp(pixelBuffer, viewportDimensions.width, @@ -151,303 +27,307 @@ void main() async { group('background', () { test('set background color to solid green', () async { - await viewer.setRendering(true); + var viewer = await createViewer(); await viewer.setBackgroundColor(0.0, 1.0, 0.0, 1.0); - await _capture("bgcolor"); - await viewer.setRendering(false); + await _capture(viewer, "bgcolor"); + await viewer.dispose(); }); test('load skybox', () async { - var outDir = Directory("$testDir/skybox"); - outDir.createSync(); - await viewer.setRendering(true); + var viewer = await createViewer(); await viewer.loadSkybox( "file:///$testDir/../../examples/assets/default_env/default_env_skybox.ktx"); await Future.delayed(Duration(seconds: 1)); - await _capture("skybox"); - await viewer.setRendering(false); + await _capture(viewer, "skybox"); }); }); group("gltf", () { test('load glb', () async { + var viewer = await createViewer(); var model = await viewer.loadGlb("$testDir/cube.glb"); await viewer.transformToUnitCube(model); await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0); await viewer.setCameraPosition(0, 1, 5); await viewer .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); - await viewer.setRendering(true); - await _capture("load_glb"); - await viewer.setRendering(false); - }); - - test('create instance from glb when keepData is true', () async { - var model = await viewer.loadGlb("$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); - await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0); - await viewer.setCameraPosition(0, 1, 5); - await viewer - .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); - await viewer.setRendering(true); - await _capture("glb_create_instance"); - await viewer.setRendering(false); - }); - - test('create instance from glb fails when keepData is false', () async { - var model = await viewer.loadGlb("$testDir/cube.glb", keepData: false); - bool thrown = false; - try { - await viewer.createInstance(model); - } catch (err) { - thrown = true; - } - expect(thrown, true); + await _capture(viewer, "load_glb"); }); }); - group('Skinning & animations', () { - test('get bone names', () async { - var model = await viewer.loadGlb("$testDir/assets/shapes.glb"); - var names = await viewer.getBoneNames(model); - expect(names.first, "Bone"); - }); + // test('create instance from glb when keepData is true', () async { + // var model = await viewer.loadGlb("$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); + // await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0); + // await viewer.setCameraPosition(0, 1, 5); + // await viewer + // .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); + // await viewer.setRendering(true); + // await _capture(viewer, "glb_create_instance"); + // await viewer.setRendering(false); + // }); - test('reset bones', () async { - var model = await viewer.loadGlb("$testDir/assets/shapes.glb"); - await viewer.resetBones(model); - }); - test('set from BVH', () async { - var model = await viewer.loadGlb("$testDir/assets/shapes.glb"); - var animation = BVHParser.parse( - File("$testDir/assets/animation.bvh").readAsStringSync(), - boneRegex: RegExp(r"Bone$")); - await viewer.addBoneAnimation(model, animation); - }); + // test('create instance from glb fails when keepData is false', () async { + // var model = await viewer.loadGlb("$testDir/cube.glb", keepData: false); + // bool thrown = false; + // try { + // await viewer.createInstance(model); + // } catch (err) { + // thrown = true; + // } + // expect(thrown, true); + // }); + // }); - test('fade in/out', () async { - var model = await viewer.loadGlb("$testDir/assets/shapes.glb"); - var animation = BVHParser.parse( - File("$testDir/assets/animation.bvh").readAsStringSync(), - boneRegex: RegExp(r"Bone$")); - await viewer.addBoneAnimation(model, animation, - fadeInInSecs: 0.5, fadeOutInSecs: 0.5); - await Future.delayed(Duration(seconds: 1)); - }); + // group('Skinning & animations', () { + // test('get bone names', () async { + // var model = await viewer.loadGlb("$testDir/assets/shapes.glb"); + // var names = await viewer.getBoneNames(model); + // expect(names.first, "Bone"); + // }); - test('create geometry', () async { - // Define the vertices of the cube - List vertices = [ - // Front face - -1, -1, 1, - 1, -1, 1, - 1, 1, 1, - -1, 1, 1, + // test('reset bones', () async { + // var model = await viewer.loadGlb("$testDir/assets/shapes.glb"); + // await viewer.resetBones(model); + // }); + // test('set from BVH', () async { + // var model = await viewer.loadGlb("$testDir/assets/shapes.glb"); + // var animation = BVHParser.parse( + // File("$testDir/assets/animation.bvh").readAsStringSync(), + // boneRegex: RegExp(r"Bone$")); + // await viewer.addBoneAnimation(model, animation); + // }); - // Back face - -1, -1, -1, - -1, 1, -1, - 1, 1, -1, - 1, -1, -1, + // test('fade in/out', () async { + // var model = await viewer.loadGlb("$testDir/assets/shapes.glb"); + // var animation = BVHParser.parse( + // File("$testDir/assets/animation.bvh").readAsStringSync(), + // boneRegex: RegExp(r"Bone$")); + // await viewer.addBoneAnimation(model, animation, + // fadeInInSecs: 0.5, fadeOutInSecs: 0.5); + // await Future.delayed(Duration(seconds: 1)); + // }); - // Top face - -1, 1, -1, - -1, 1, 1, - 1, 1, 1, - 1, 1, -1, - - // Bottom face - -1, -1, -1, - 1, -1, -1, - 1, -1, 1, - -1, -1, 1, - - // Right face - 1, -1, -1, - 1, 1, -1, - 1, 1, 1, - 1, -1, 1, - - // Left face - -1, -1, -1, - -1, -1, 1, - -1, 1, 1, - -1, 1, -1, - ]; - - // Define the indices for the cube - List indices = [ - 0, 1, 2, 0, 2, 3, // Front face - 4, 5, 6, 4, 6, 7, // Back face - 8, 9, 10, 8, 10, 11, // Top face - 12, 13, 14, 12, 14, 15, // Bottom face - 16, 17, 18, 16, 18, 19, // Right face - 20, 21, 22, 20, 22, 23 // Left face - ]; + group("geometry", () { + test('create custom geometry', () async { + var viewer = await createViewer(); await viewer.createIbl(1.0, 1.0, 1.0, 1000); - await viewer.setCameraPosition(0, 0.5, 6); + await viewer.setCameraPosition(0, 0, 6); await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0); - await viewer - .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 8)); - await viewer.setRendering(true); - // Create the cube geometry - await viewer.createGeometry(vertices, indices, + await viewer.createGeometry(cubeVertices, cubeIndices, primitiveType: PrimitiveType.TRIANGLES); - await _capture("geometry_cube"); - await viewer.setRendering(false); + await _capture(viewer, "geometry_cube"); }); + }); - test('create sphere', () async { - // Define the parameters for the sphere - int latitudeBands = 30; - int longitudeBands = 30; - double radius = 1.0; - - List vertices = []; - List indices = []; - - // Generate vertices - for (int latNumber = 0; latNumber <= latitudeBands; latNumber++) { - double theta = latNumber * pi / latitudeBands; - double sinTheta = sin(theta); - double cosTheta = cos(theta); - - for (int longNumber = 0; longNumber <= longitudeBands; longNumber++) { - double phi = longNumber * 2 * pi / longitudeBands; - double sinPhi = sin(phi); - double cosPhi = cos(phi); - - double x = cosPhi * sinTheta; - double y = cosTheta; - double z = sinPhi * sinTheta; - - vertices.addAll([radius * x, radius * y, radius * z]); - } - } - - // Generate indices - for (int latNumber = 0; latNumber < latitudeBands; latNumber++) { - for (int longNumber = 0; longNumber < longitudeBands; longNumber++) { - int first = (latNumber * (longitudeBands + 1)) + longNumber; - int second = first + longitudeBands + 1; - - indices.addAll( - [first, second, first + 1, second, second + 1, first + 1]); - } - } - + group("transforms", () { + test('set position based on screenspace coord', () async { + var viewer = await createViewer(); + print(await viewer.getCameraFov(true)); await viewer.createIbl(1.0, 1.0, 1.0, 1000); - await viewer.setCameraPosition(0, 0.5, 10); + await viewer.setCameraPosition(0, 0, 6); await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0); - await viewer - .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 8)); - await viewer.setRendering(true); + // Create the cube geometry + final cube = await viewer.createGeometry(cubeVertices, cubeIndices, + primitiveType: PrimitiveType.TRIANGLES); + // await viewer.setPosition(cube, -0.05, 0.04, 5.9); + // await viewer.setPosition(cube, -2.54, 2.54, 0); + await viewer.queuePositionUpdateFromViewportCoords(cube, 0, 0); - // Create the sphere geometry - // final sphere = await viewer.createGeometry(vertices, indices, - // primitiveType: PrimitiveType.TRIANGLES); + // we need an explicit render call here to process the transform queue + await viewer.render(); - // await viewer.gizmo!.attach(sphere); - // await viewer.setPosition(sphere, -1.0, 0.0, -10.0); - // await viewer.setRotationQuat( - // sphere, Quaternion.axisAngle(Vector3(1, 0, 0), pi / 8)); - await _capture("geometry_sphere"); - await viewer.setRendering(false); + await _capture(viewer, "set_position_from_viewport_coords"); }); + }); - test('enable grid overlay', () async { - await viewer.setBackgroundColor(0, 0, 0, 1); - await viewer.setCameraPosition(0, 0.5, 0); - await viewer - .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.1)); - await viewer.setRendering(true); - await viewer.setLayerEnabled(2, true); - await _capture("grid"); - await viewer.setRendering(false); - }); + // test('create sphere', () async { + // // Define the parameters for the sphere + // int latitudeBands = 30; + // int longitudeBands = 30; + // double radius = 1.0; - test('point light', () async { - var model = await viewer.loadGlb("$testDir/cube.glb"); - await viewer.transformToUnitCube(model); - var light = await viewer.addLight( - LightType.POINT, 6500, 1000000, 0, 2, 0, 0, -1, 0, - falloffRadius: 10.0); - await viewer.setBackgroundColor(0.0, 0.0, 0.0, 1.0); - await viewer.setCameraPosition(0, 1, 5); - await viewer - .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); - await viewer.setRendering(true); - await _capture("point_light"); - await viewer.setRendering(false); - }); + // List vertices = []; + // List indices = []; - test('set point light position', () async { - var model = await viewer.loadGlb("$testDir/cube.glb"); - await viewer.transformToUnitCube(model); - var light = await viewer.addLight( - LightType.POINT, 6500, 1000000, 0, 2, 0, 0, -1, 0, - falloffRadius: 10.0); - await viewer.setLightPosition(light, 0.5, 2, 0); - await viewer.setBackgroundColor(0.0, 0.0, 0.0, 1.0); - await viewer.setCameraPosition(0, 1, 5); - await viewer - .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); - await viewer.setRendering(true); - await _capture("move_point_light"); - await viewer.setRendering(false); - }); + // // Generate vertices + // for (int latNumber = 0; latNumber <= latitudeBands; latNumber++) { + // double theta = latNumber * pi / latitudeBands; + // double sinTheta = sin(theta); + // double cosTheta = cos(theta); - test('directional light', () async { - var model = await viewer.loadGlb("$testDir/cube.glb"); - await viewer.transformToUnitCube(model); - var light = await viewer.addLight( - LightType.SUN, 6500, 1000000, 0, 0, 0, 0, -1, 0); - await viewer.setBackgroundColor(0.0, 0.0, 0.0, 1.0); - await viewer.setCameraPosition(0, 1, 5); - await viewer - .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); - await viewer.setRendering(true); - await _capture("directional_light"); - await viewer.setRendering(false); - }); + // for (int longNumber = 0; longNumber <= longitudeBands; longNumber++) { + // double phi = longNumber * 2 * pi / longitudeBands; + // double sinPhi = sin(phi); + // double cosPhi = cos(phi); - test('set directional light direction', () async { - var model = await viewer.loadGlb("$testDir/cube.glb"); - await viewer.transformToUnitCube(model); - var light = await viewer.addLight( - LightType.SUN, 6500, 1000000, 0, 0, 0, 0, -1, 0); - await viewer.setLightDirection(light, Vector3(-1, -1, -1)); - await viewer.setBackgroundColor(0.0, 0.0, 0.0, 1.0); - await viewer.setCameraPosition(0, 1, 5); - await viewer - .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); - await viewer.setRendering(true); - await _capture("set_directional_light_direction"); - await viewer.setRendering(false); - }); + // double x = cosPhi * sinTheta; + // double y = cosTheta; + // double z = sinPhi * sinTheta; - test('set stencil highlight', () async { - var model = await viewer.loadGlb("$testDir/cube.glb"); - await viewer.transformToUnitCube(model); + // vertices.addAll([radius * x, radius * y, radius * z]); + // } + // } + + // // Generate indices + // for (int latNumber = 0; latNumber < latitudeBands; latNumber++) { + // for (int longNumber = 0; longNumber < longitudeBands; longNumber++) { + // int first = (latNumber * (longitudeBands + 1)) + longNumber; + // int second = first + longitudeBands + 1; + + // indices.addAll( + // [first, second, first + 1, second, second + 1, first + 1]); + // } + // } + + // await viewer.createIbl(1.0, 1.0, 1.0, 1000); + // await viewer.setCameraPosition(0, 0.5, 10); + // await viewer.setBackgroundColor(0.0, 0.0, 1.0, 1.0); + // await viewer + // .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 8)); + // await viewer.setRendering(true); + + // // Create the sphere geometry + // // final sphere = await viewer.createGeometry(vertices, indices, + // // primitiveType: PrimitiveType.TRIANGLES); + + // // await viewer.gizmo!.attach(sphere); + // // await viewer.setPosition(sphere, -1.0, 0.0, -10.0); + // // await viewer.setRotationQuat( + // // sphere, Quaternion.axisAngle(Vector3(1, 0, 0), pi / 8)); + // await _capture(viewer, "geometry_sphere"); + // await viewer.setRendering(false); + // }); + + // test('enable grid overlay', () async { + // await viewer.setBackgroundColor(0, 0, 0, 1); + // await viewer.setCameraPosition(0, 0.5, 0); + // await viewer + // .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.1)); + // await viewer.setRendering(true); + // await viewer.setLayerEnabled(2, true); + // await _capture(viewer, "grid"); + // await viewer.setRendering(false); + // }); + + // test('point light', () async { + // var model = await viewer.loadGlb("$testDir/cube.glb"); + // await viewer.transformToUnitCube(model); + // var light = await viewer.addLight( + // LightType.POINT, 6500, 1000000, 0, 2, 0, 0, -1, 0, + // falloffRadius: 10.0); + // await viewer.setBackgroundColor(0.0, 0.0, 0.0, 1.0); + // await viewer.setCameraPosition(0, 1, 5); + // await viewer + // .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); + // await viewer.setRendering(true); + // await _capture(viewer, "point_light"); + // await viewer.setRendering(false); + // }); + + // test('set point light position', () async { + // var model = await viewer.loadGlb("$testDir/cube.glb"); + // await viewer.transformToUnitCube(model); + // var light = await viewer.addLight( + // LightType.POINT, 6500, 1000000, 0, 2, 0, 0, -1, 0, + // falloffRadius: 10.0); + // await viewer.setLightPosition(light, 0.5, 2, 0); + // await viewer.setBackgroundColor(0.0, 0.0, 0.0, 1.0); + // await viewer.setCameraPosition(0, 1, 5); + // await viewer + // .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); + // await viewer.setRendering(true); + // await _capture(viewer, "move_point_light"); + // await viewer.setRendering(false); + // }); + + // test('directional light', () async { + // var model = await viewer.loadGlb("$testDir/cube.glb"); + // await viewer.transformToUnitCube(model); + // var light = await viewer.addLight( + // LightType.SUN, 6500, 1000000, 0, 0, 0, 0, -1, 0); + // await viewer.setBackgroundColor(0.0, 0.0, 0.0, 1.0); + // await viewer.setCameraPosition(0, 1, 5); + // await viewer + // .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); + // await viewer.setRendering(true); + // await _capture(viewer, "directional_light"); + // await viewer.setRendering(false); + // }); + + // test('set directional light direction', () async { + // var model = await viewer.loadGlb("$testDir/cube.glb"); + // await viewer.transformToUnitCube(model); + // var light = await viewer.addLight( + // LightType.SUN, 6500, 1000000, 0, 0, 0, 0, -1, 0); + // await viewer.setLightDirection(light, Vector3(-1, -1, -1)); + // await viewer.setBackgroundColor(0.0, 0.0, 0.0, 1.0); + // await viewer.setCameraPosition(0, 1, 5); + // await viewer + // .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); + // await viewer.setRendering(true); + // await _capture(viewer, "set_directional_light_direction"); + // await viewer.setRendering(false); + // }); + + group("stencil", () { + test('set stencil highlight for glb', () async { + final viewer = await createViewer(); + var model = await viewer.loadGlb("$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(-1, -1, -1)); + await viewer.setLightDirection(light, Vector3(0, 1, -1)); - await viewer.setBackgroundColor(0.0, 0.0, 0.0, 1.0); + 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 _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, 1, 5); await viewer .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5)); - await viewer.setStencilHighlight(model); - await viewer.setRendering(true); - await Future.delayed(Duration(milliseconds: 500)); - await _capture("stencil_highlight"); - await viewer.setRendering(false); + + var cube = await viewer.createGeometry(cubeVertices, cubeIndices, + primitiveType: PrimitiveType.TRIANGLES); + await viewer.setStencilHighlight(cube); + + await _capture(viewer, "stencil_highlight_geometry"); + + await viewer.removeStencilHighlight(cube); + + await _capture(viewer, "stencil_highlight_geometry_remove"); + }); + + 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(cubeVertices, cubeIndices, + primitiveType: PrimitiveType.TRIANGLES); + var cube2 = await viewer.createGeometry(cubeVertices, cubeIndices, + primitiveType: PrimitiveType.TRIANGLES); + 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 _capture(viewer, "stencil_highlight_multiple_geometry"); }); }); }