feature!:

This is a breaking change needed to fully implement instancing and stencil highlighting.

Previously, users would work directly with entities (on the Dart side, ThermionEntity), e.g.

final entity = await viewer.loadGlb("some.glb");

However, Filament "entities" are a lower-level abstraction.

Loading a glTF file, for example, inserts multiple entities into the scene.

For example, each mesh, light, and camera within a glTF asset will be assigned an entity. A top-level (non-renderable) entity will also be created for the glTF asset, which can be used to transform the entire hierarchy.

"Asset" is a better representation for loading/inserting objects into the scene; think of this as a bundle of entities.

Unless you need to work directly with transforms, instancing, materials and renderables, you can work directly with ThermionAsset.
This commit is contained in:
Nick Fisher
2024-11-21 15:04:10 +08:00
parent 9ada6aae64
commit ed444b0615
195 changed files with 18061 additions and 12628 deletions

View File

@@ -2,7 +2,6 @@ import 'dart:async';
import 'dart:typed_data';
import 'package:animation_tools_dart/animation_tools_dart.dart';
import 'package:test/test.dart';
import 'package:vector_math/vector_math_64.dart';
import 'helpers.dart';
void main() async {
@@ -10,23 +9,21 @@ void main() async {
group('morph animation tests', () {
test('set morph animation', () async {
var viewer = await testHelper.createViewer(
bg: kRed, cameraPosition: Vector3(0, 0, 5));
await testHelper.withViewer((viewer) async {
final cube = await viewer.loadGlb(
"${testHelper.testDir}/assets/cube_with_morph_targets.glb");
var morphData = MorphAnimationData(
Float32List.fromList(List<double>.generate(60, (i) => i / 60)),
["Key 1"]);
final cube = await viewer
.loadGlb("${testHelper.testDir}/assets/cube_with_morph_targets.glb");
var morphData = MorphAnimationData(
Float32List.fromList(List<double>.generate(60, (i) => i / 60)),
["Key 1"]);
await viewer.setMorphAnimationData(cube, morphData);
for (int i = 0; i < 60; i++) {
await viewer.requestFrame();
await Future.delayed(Duration(milliseconds: 17));
}
await testHelper.capture(viewer, "morph_animation");
await viewer.dispose();
for (int i = 0; i < 60; i++) {
await viewer.requestFrame();
await Future.delayed(Duration(milliseconds: 17));
}
await testHelper.capture(viewer, "morph_animation");
});
});
});
}

View File

@@ -11,218 +11,212 @@ void main() async {
final testHelper = TestHelper("integration");
group('camera', () {
test('getCameraModelMatrix, getCameraPosition, rotation', () async {
var viewer = await testHelper.createViewer();
var matrix = await viewer.getCameraModelMatrix();
expect(matrix.trace(), 4);
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;
expect(position.x, 2.0);
expect(position.y, 2.0);
expect(position.z, 2.0);
await viewer.setCameraPosition(2.0, 2.0, 2.0);
matrix = await viewer.getCameraModelMatrix();
var position = matrix.getColumn(3).xyz;
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);
await viewer.dispose();
position = await viewer.getCameraPosition();
expect(position.x, 2.0);
expect(position.y, 2.0);
expect(position.z, 2.0);
});
});
test('getCameraViewMatrix', () async {
var viewer = await testHelper.createViewer();
await testHelper.withViewer((viewer) async {
await viewer.setCameraModelMatrix4(Matrix4.identity());
var modelMatrix = await viewer.getCameraModelMatrix();
var viewMatrix = await viewer.getCameraViewMatrix();
var modelMatrix = await viewer.getCameraModelMatrix();
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));
await viewer.dispose();
// 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 {
var viewer = await testHelper.createViewer();
var projectionMatrix = await viewer.getCameraProjectionMatrix();
print(projectionMatrix);
await viewer.dispose();
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('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();
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());
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());
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");
// 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");
});
});
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);
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");
});
});
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)));
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);
});
});
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());
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);
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);
});
});
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());
await testHelper.capture(viewer, "create_camera_main_camera");
var newCamera = await viewer.createCamera();
await newCamera.setTransform(Matrix4.translation(Vector3(0, 0, 4)));
newCamera.setLensProjection();
await viewer.setActiveCamera(newCamera);
expect(await viewer.getActiveCamera(), newCamera);
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");
expect(viewer.getCameraCount(), 2);
expect(viewer.getCameraAt(0), await viewer.getMainCamera());
expect(viewer.getCameraAt(1), newCamera);
await expectLater(
() => viewer.getCameraAt(2), throwsA(isA<Exception>()));
});
});
});
test('getCameraCullingProjectionMatrix', () async {
throw Exception("TODO");
// ignore: dead_code
var viewer = await testHelper.createViewer();
var matrix = await viewer.getCameraCullingProjectionMatrix();
print(matrix);
await viewer.dispose();
});
test('getCameraFrustum', () async {
var viewer = await testHelper.createViewer();
var frustum = await viewer.getCameraFrustum();
print(frustum.plane5.normal);
print(frustum.plane5.constant);
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 viewer.dispose();
});
test('set orthographic projection', () async {
var viewer = await testHelper.createViewer(
bg: kRed, cameraPosition: Vector3(0, 0, 4));
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 viewer.dispose();
});
test('set perspective projection/culling matrix', () async {
var viewer = await testHelper.createViewer(
bg: kRed, cameraPosition: Vector3(0, 0, 4));
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);
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);
await testHelper.capture(
viewer, "camera_set_perspective_projection_culling_matrix_object_fov120");
await viewer.dispose();
});
test('set custom projection/culling matrix (orthographic)', () async {
var viewer = await testHelper.createViewer(
bg: kRed, cameraPosition: Vector3(0, 0, 4));
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);
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");
await viewer.dispose();
});
test('setting transform on camera updates model matrix (no parent)',
() async {
var viewer = await testHelper.createViewer();
var cameraEntity = await viewer.getMainCameraEntity();
var camera = await viewer.getMainCamera();
await viewer.setPosition(cameraEntity, 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);
await viewer.dispose();
});
test('setting transform on camera updates model matrix (with parent)',
() async {
var viewer = await testHelper.createViewer();
var cameraEntity = await viewer.getMainCameraEntity();
var camera = await viewer.getMainCamera();
var parent = await viewer.createGeometry(GeometryHelper.cube());
await viewer.setParent(camera.getEntity(), parent);
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);
await viewer.setTransform(parent, 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.dispose();
});
test('create camera', () async {
var viewer = await testHelper.createViewer();
await viewer.setCameraPosition(0, 0, 5);
await viewer.setBackgroundColor(1.0, 0.0, 1.0, 1.0);
await viewer.createGeometry(GeometryHelper.cube());
await testHelper.capture(viewer, "create_camera_main_camera");
var newCamera = await viewer.createCamera();
await newCamera.setTransform(Matrix4.translation(Vector3(0, 0, 4)));
newCamera.setLensProjection();
await viewer.setActiveCamera(newCamera);
expect(await viewer.getActiveCamera(), newCamera);
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");
expect(viewer.getCameraCount(), 2);
expect(viewer.getCameraAt(0), await viewer.getMainCamera());
expect(viewer.getCameraAt(1), newCamera);
await expectLater(() => viewer.getCameraAt(2), throwsA(isA<Exception>()));
await viewer.dispose();
});
});
}

View File

@@ -13,71 +13,118 @@ import 'helpers.dart';
void main() async {
final testHelper = TestHelper("geometry");
group("custom geometry", () {
test('create cube (no uvs/normals)', () async {
var viewer = await testHelper.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, 1.0, 1.0, 1.0);
final cube = await viewer
.createGeometry(GeometryHelper.cube(normals: false, uvs: false));
await testHelper.capture(viewer, "geometry_cube_no_uv_no_normal");
await viewer.removeEntity(cube);
await testHelper.capture(viewer, "geometry_cube_removed");
await viewer.dispose();
});
test('create cube (no normals)', () async {
var viewer = await testHelper.createViewer();
var light = await viewer.addLight(
LightType.POINT, 6500, 10000000, 0, 2, 0, 0, 0, 0,
falloffRadius: 100.0);
await viewer.setCameraPosition(0, 2, 6);
await viewer
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 8));
await viewer.setBackgroundColor(1.0, 0.0, 1.0, 1.0);
await viewer
.createGeometry(GeometryHelper.cube(normals: false, uvs: false));
await testHelper.capture(viewer, "geometry_cube_no_normals");
test('create cube', () async {
await testHelper.withViewer((viewer) async {
final cube = await viewer
.createGeometry(GeometryHelper.cube(normals: false, uvs: false));
await testHelper.capture(viewer, "geometry_add_cube");
await viewer.removeEntity(cube);
await testHelper.capture(viewer, "geometry_remove_cube");
});
});
test('create cube (with normals)', () async {
var viewer = await testHelper.createViewer();
var light = await viewer.addLight(
LightType.POINT, 6500, 10000000, 0, 2, 0, 0, 0, 0,
falloffRadius: 100.0);
await viewer.setCameraPosition(0, 2, 6);
await viewer
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 8));
await viewer.setBackgroundColor(1.0, 1.0, 1.0, 1.0);
await viewer
.createGeometry(GeometryHelper.cube(normals: true, uvs: false));
await testHelper.capture(viewer, "geometry_cube_with_normals");
});
test('create instance', () async {
await testHelper.withViewer((viewer) async {
final cube = await viewer
.createGeometry(GeometryHelper.cube(normals: false, uvs: false));
await viewer.setTransform(
cube.entity, Matrix4.translation(Vector3.all(-1)));
final instance = await cube.createInstance();
await instance.addToScene();
await viewer.setTransform(
instance.entity, Matrix4.translation(Vector3.all(1)));
await testHelper.capture(viewer, "geometry_instanced");
});
});
// test('create instance (shared material)', () async {
// await testHelper.withViewer((viewer) async {
// final materialInstance = await viewer.createUnlitMaterialInstance();
// await materialInstance.setParameterFloat4(
// "baseColorFactor", 1.0, 0.0, 0.0, 1.0);
// final cube = await viewer.createGeometry(
// GeometryHelper.cube(normals: true, uvs: false),
// materialInstances: [materialInstance]);
// final instance = await viewer
// .createInstance(cube, materialInstances: [materialInstance]);
// await viewer.setTransform(
// instance.entity, Matrix4.translation(Vector3.all(1)));
// await testHelper.capture(
// viewer, "geometry_instanced_with_shared_material_instance");
// });
// });
// test('create instance (no material on second instance)', () async {
// await testHelper.withViewer((viewer) async {
// final materialInstance = await viewer.createUnlitMaterialInstance();
// await materialInstance.setParameterFloat4(
// "baseColorFactor", 1.0, 0.0, 0.0, 1.0);
// final cube = await viewer.createGeometry(
// GeometryHelper.cube(normals: true, uvs: false),
// materialInstances: [materialInstance]);
// final instance = await viewer
// .createInstance(cube);
// await viewer.setTransform(
// instance.entity, Matrix4.translation(Vector3.all(1)));
// await testHelper.capture(
// viewer, "geometry_instanced_with_no_material_instance");
// });
// });
// test('create instance (separate materials)', () async {
// await testHelper.withViewer((viewer) async {
// final materialInstance = await viewer.createUnlitMaterialInstance();
// await materialInstance.setParameterFloat4(
// "baseColorFactor", 1.0, 0.0, 0.0, 1.0);
// final cube = await viewer.createGeometry(
// GeometryHelper.cube(normals: true, uvs: false),
// materialInstances: [materialInstance]);
// final materialInstance2 = await viewer.createUnlitMaterialInstance();
// await materialInstance2.setParameterFloat4(
// "baseColorFactor", 0.0, 1.0, 0.0, 1.0);
// final instance = await viewer
// .createInstance(cube, materialInstances: [materialInstance2]);
// await viewer.setTransform(
// instance.entity, Matrix4.translation(Vector3.all(1)));
// await testHelper.capture(
// viewer, "geometry_instanced_with_separate_material_instances");
// });
// });
test('create cube with custom ubershader material instance (color)',
() async {
var viewer = await testHelper.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, 1.0, 1.0);
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));
await viewer.setBackgroundColor(1.0, 0.0, 1.0, 1.0);
var materialInstance =
await viewer.createUbershaderMaterialInstance(unlit: true);
final cube = await viewer.createGeometry(
GeometryHelper.cube(uvs: false, normals: true),
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 viewer.removeEntity(cube);
await viewer.destroyMaterialInstance(materialInstance);
var materialInstance =
await viewer.createUbershaderMaterialInstance(unlit: true);
final cube = await viewer.createGeometry(
GeometryHelper.cube(uvs: false, normals: true),
materialInstances: [materialInstance]);
await materialInstance.setParameterFloat4(
"baseColorFactor", 0.0, 1.0, 0.0, 0.0);
await testHelper.capture(
viewer, "geometry_cube_with_custom_material_ubershader");
await viewer.removeEntity(cube);
});
});
test('create cube with custom ubershader material instance (texture)',
@@ -92,86 +139,68 @@ void main() async {
var materialInstance = await viewer.createUbershaderMaterialInstance();
final cube = await viewer.createGeometry(
GeometryHelper.cube(uvs: true, normals: true),
materialInstance: materialInstance);
materialInstances: [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 viewer.applyTexture(texture as ThermionFFITexture, cube.entity);
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 testHelper.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);
await testHelper.withViewer((viewer) async {
var materialInstance = await viewer.createUnlitMaterialInstance();
var cube = await viewer.createGeometry(GeometryHelper.cube(),
materialInstances: [materialInstance]);
var materialInstance = await viewer.createUnlitMaterialInstance();
var cube = await viewer.createGeometry(GeometryHelper.cube(),
materialInstance: materialInstance);
await materialInstance.setParameterFloat4(
"baseColorFactor", 0.0, 1.0, 0.0, 1.0);
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();
await testHelper.capture(viewer, "unlit_material_base_color");
});
});
test('create cube with custom material instance (unlit)', () async {
var viewer = await testHelper.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);
test('unlit material with texture', () async {
await testHelper.withViewer((viewer) async {
var materialInstance = await viewer.createUnlitMaterialInstance();
var cube = await viewer.createGeometry(GeometryHelper.cube(),
materialInstances: [materialInstance]);
var materialInstance = await viewer.createUnlitMaterialInstance();
var cube = await viewer.createGeometry(GeometryHelper.cube(),
materialInstance: materialInstance);
await materialInstance.setParameterInt("baseColorIndex", 0);
var textureData =
File("${testHelper.testDir}/assets/cube_texture_512x512.png")
.readAsBytesSync();
var texture = await viewer.createTexture(textureData);
await viewer.applyTexture(texture, cube.entity);
await testHelper.capture(viewer, "unlit_material_texture_only");
await viewer.removeEntity(cube);
});
});
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);
test('shared material instance with texture and base color', () async {
await testHelper.withViewer((viewer) async {
var materialInstance = await viewer.createUnlitMaterialInstance();
var cube1 = await viewer.createGeometry(GeometryHelper.cube(),
materialInstances: [materialInstance]);
var cube2 = await viewer.createGeometry(GeometryHelper.cube(),
materialInstances: [materialInstance]);
await viewer.setTransform(
cube2.entity, Matrix4.translation(Vector3(1, 1, 1)));
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();
await materialInstance.setParameterInt("baseColorIndex", 0);
var textureData =
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);
await testHelper.capture(viewer, "unlit_material_shared");
await viewer.destroyTexture(texture);
});
});
test('create sphere (no normals)', () async {
@@ -183,15 +212,15 @@ void main() async {
await testHelper.capture(viewer, "geometry_sphere_no_normals");
});
test('create geometry instance', () async {
var viewer = await testHelper.createViewer(
cameraPosition: Vector3(0, 0, 6), bg: kRed);
final cube = await viewer
.createGeometry(GeometryHelper.sphere(normals: false, uvs: false));
await viewer.setTransform(cube, Matrix4.translation(Vector3(2, 1, 1)));
final cube2 = await viewer.createInstance(cube);
await viewer.setTransform(cube2, Matrix4.translation(Vector3(-2, 1, 1)));
await testHelper.capture(viewer, "geometry_instance");
});
// test('create geometry instance', () async {
// var viewer = await testHelper.createViewer(
// cameraPosition: Vector3(0, 0, 6), bg: kRed);
// final cube = await viewer
// .createGeometry(GeometryHelper.sphere(normals: false, uvs: false));
// await viewer.setTransform(cube.entity, Matrix4.translation(Vector3(2, 1, 1)));
// final cube2 = await viewer.createInstance(cube);
// await viewer.setTransform(cube2.entity, Matrix4.translation(Vector3(-2, 1, 1)));
// await testHelper.capture(viewer, "geometry_instance");
// });
});
}

View File

@@ -1,19 +1,310 @@
// ignore_for_file: unused_local_variable
import 'dart:async';
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("gizmo");
group('gizmo', () {
test('add gizmo to scene', () async {
var viewer = await testHelper.createViewer();
var view = await viewer.getViewAt(0);
var gizmo = await viewer.createGizmo(view);
await testHelper.capture(viewer, "gizmo_add_to_scene");
await viewer.dispose();
group("gizmo tests", () {
test('add gizmo', () async {
await testHelper.withViewer((viewer) async {
var modelMatrix =
makeViewMatrix(Vector3(0.5, 0.5, 0.5), Vector3.zero(), Vector3(0, 1, 0));
modelMatrix.invert();
await viewer.setCameraModelMatrix4(modelMatrix);
final view = await viewer.getViewAt(0);
final gizmo = await viewer.createGizmo(view, GizmoType.translation);
await viewer.setLayerVisibility(VisibilityLayers.OVERLAY, true);
await gizmo.addToScene();
await testHelper.capture(
viewer, "gizmo_added_to_scene_unattached_close");
modelMatrix =
makeViewMatrix(Vector3(0.5, 0.5, 0.5).scaled(10), Vector3.zero(), Vector3(0, 1, 0));
modelMatrix.invert();
await viewer.setCameraModelMatrix4(modelMatrix);
// gizmo occupies same viewport size no matter the camera position
await testHelper.capture(viewer, "gizmo_added_to_scene_unattached_far");
}, postProcessing: true, bg: kWhite);
});
test('pick gizmo when not added to scene (this should not crash)',
() async {
await testHelper.withViewer((viewer) async {
await viewer.setCameraPosition(0, 0, 1);
final view = await viewer.getViewAt(0);
final viewport = await view.getViewport();
final gizmo = await viewer.createGizmo(view, GizmoType.translation);
final completer = Completer<GizmoPickResultType>();
await gizmo.pick(viewport.width ~/ 2, viewport.height ~/ 2 + 1,
handler: (GizmoPickResultType resultType, Vector3 coords) async {
completer.complete(resultType);
});
for (int i = 0; i < 10; i++) {
await testHelper.capture(
viewer, "pick_gizmo_without_adding_to_scene");
if (completer.isCompleted) {
break;
}
}
expect(completer.isCompleted, false);
}, postProcessing: true, bg: kWhite);
});
test('pick gizmo when added to scene', () async {
await testHelper.withViewer((viewer) async {
await viewer.setCameraPosition(0, 0, 1);
final view = await viewer.getViewAt(0);
final viewport = await view.getViewport();
final gizmo = await viewer.createGizmo(view, GizmoType.translation);
await gizmo.addToScene();
await viewer.setLayerVisibility(VisibilityLayers.OVERLAY, true);
final completer = Completer<GizmoPickResultType>();
await testHelper.capture(viewer, "gizmo_before_pick_no_highlight");
await gizmo.pick(viewport.width ~/ 2, viewport.height ~/ 2 + 1,
handler: (resultType, coords) async {
completer.complete(resultType);
});
for (int i = 0; i < 10; i++) {
await testHelper.capture(viewer, "gizmo_after_pick_no_highlight");
if (completer.isCompleted) {
break;
}
}
assert(completer.isCompleted);
}, postProcessing: true, bg: kWhite);
});
test('highlight/unhighlight gizmo', () async {
await testHelper.withViewer((viewer) async {
final modelMatrix =
makeViewMatrix(Vector3(0.5, 0.5, 0.5), Vector3.zero(), Vector3(0, 1, 0));
modelMatrix.invert();
await viewer.setCameraModelMatrix4(modelMatrix);
final view = await viewer.getViewAt(0);
final viewport = await view.getViewport();
final gizmo = await viewer.createGizmo(view, GizmoType.translation);
await gizmo.addToScene();
await viewer.setLayerVisibility(VisibilityLayers.OVERLAY, true);
await testHelper.capture(viewer, "gizmo_before_highlight");
await gizmo.highlight(Axis.X);
await testHelper.capture(viewer, "gizmo_after_highlight");
await gizmo.unhighlight();
await testHelper.capture(viewer, "gizmo_after_unhighlight");
}, postProcessing: true, bg: kWhite);
});
});
// test('set uv scaling (unlit)', () async {
// var viewer = await testHelper.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");
// await viewer.dispose();
// });
// });
// group("texture", () {
// test("create/apply/dispose texture", () async {
// var viewer = await testHelper.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(),
// materialInstances: [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);
// await viewer.dispose();
// });
// });
// group("unproject", () {
// test("unproject", () async {
// final dimensions = (width: 1280, height: 768);
// var viewer = await testHelper.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 = <Uint8List>[];
// // 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)));
}

View File

@@ -12,50 +12,81 @@ void main() async {
final testHelper = TestHelper("gltf");
group("gltf", () {
test('load glb from file', () async {
var viewer = await testHelper.createViewer(bg: kRed, cameraPosition: Vector3(0, 1, 5));
var model = await viewer.loadGlb("file://${testHelper.testDir}/assets/cube.glb");
await viewer
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5));
await testHelper.capture(viewer, "load_glb_from_file");
await viewer.dispose();
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.removeEntity(model);
});
});
test('load glb from buffer', () async {
var viewer = await testHelper.createViewer();
var buffer = File("${testHelper.testDir}/assets/cube.glb").readAsBytesSync();
var model = await viewer.loadGlbFromBuffer(buffer);
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 testHelper.capture(viewer, "load_glb_from_buffer");
await viewer.dispose();
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 priority', () async {
var viewer = await testHelper.createViewer();
await viewer.addDirectLight(DirectLight.sun());
await viewer.setBackgroundColor(1.0, 1.0, 1.0, 1.0);
await viewer.setCameraPosition(0, 3, 5);
await viewer
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -0.5));
await testHelper.withViewer((viewer) async {
viewer.addDirectLight(DirectLight.sun());
var buffer =
File("${testHelper.testDir}/assets/cube.glb").readAsBytesSync();
var buffer = File("${testHelper.testDir}/assets/cube.glb").readAsBytesSync();
var model1 = await viewer.loadGlbFromBuffer(buffer, priority: 7);
var model2 = await viewer.loadGlbFromBuffer(buffer, priority: 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);
}
for (final entity in await viewer.getChildEntities(model1, true)) {
await viewer.setMaterialPropertyFloat4(
entity, "baseColorFactor", 0, 0, 0, 1.0, 1.0);
}
for (final entity in await viewer.getChildEntities(model2, true)) {
await viewer.setMaterialPropertyFloat4(
entity, "baseColorFactor", 0, 0, 1.0, 0.0, 1.0);
}
await testHelper.capture(viewer, "load_glb_from_buffer_with_priority");
// 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);
}
await viewer.dispose();
// 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: 2);
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");
});
});
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();
await viewer.setTransform(
instance.entity, Matrix4.translation(Vector3.all(1)));
await testHelper.capture(viewer, "gltf_create_instance_with_material_1");
});
});
});
}

View File

@@ -3,6 +3,7 @@
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';
@@ -79,7 +80,6 @@ class TestHelper {
outDir = Directory("$testDir/output/${dir}");
print("Out dir : $packageUri");
// outDir.deleteSync(recursive: true);
outDir.createSync(recursive: true);
print("Created out dir : $packageUri");
if (Platform.isMacOS) {
@@ -110,42 +110,63 @@ class TestHelper {
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<ThermionViewer> createViewer(
{img.Color? bg,
Vector3? cameraPosition,
bool postProcessing = false,
viewportDimensions = (width: 500, height: 500)}) async {
final resourceLoader = calloc<ResourceLoaderWrapper>(1);
cameraPosition ??= Vector3(0, 2, 6);
var loadToOut = NativeCallable<
Void Function(Pointer<Char>,
Pointer<ResourceBuffer>)>.listener(DartResourceLoader.loadResource);
resourceLoader.ref.loadToOut = loadToOut.nativeFunction;
var freeResource = NativeCallable<Void Function(ResourceBuffer)>.listener(
DartResourceLoader.freeResource);
resourceLoader.ref.freeResource = freeResource.nativeFunction;
var viewer = ThermionViewerFFI(resourceLoader: resourceLoader.cast<Void>());
await viewer.initialized;
swapChain = await viewer.createHeadlessSwapChain(
viewportDimensions.width, viewportDimensions.height);
await viewer.updateViewportAndCameraProjection(
viewportDimensions.width.toDouble(),
viewportDimensions.height.toDouble());
if (bg != null) {
await viewer.setBackgroundColor(
bg.r.toDouble(), bg.g.toDouble(), bg.b.toDouble(), bg.a.toDouble());
}
if (cameraPosition != null) {
await viewer.setCameraPosition(
cameraPosition.x, cameraPosition.y, cameraPosition.z);
}
await viewer.setCameraPosition(
cameraPosition.x, cameraPosition.y, cameraPosition.z);
await viewer.setPostProcessing(postProcessing);
await viewer.setToneMapping(ToneMapper.LINEAR);
return viewer;
}
}

View File

@@ -8,30 +8,58 @@ import 'package:vector_math/vector_math_64.dart';
import 'helpers.dart';
Future<
({
ThermionAsset blueCube,
MaterialInstance blueMaterialInstance,
ThermionAsset greenCube,
MaterialInstance greenMaterialInstance
})> setup(ThermionViewer viewer) async {
var blueMaterialInstance = await viewer.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 viewer.setTransform(
blueCube.entity, Matrix4.translation(Vector3(1.0, 0.0, -1.0)));
var greenMaterialInstance = await viewer.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("integration");
final testHelper = TestHelper("material");
group("material tests", () {
test('unlit material with color only', () async {
var viewer = await testHelper.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);
await testHelper.withViewer((viewer) async {
await viewer.setPostProcessing(true);
await viewer.setToneMapping(ToneMapper.LINEAR);
var materialInstance = await viewer.createUnlitMaterialInstance();
var cube = await viewer.createGeometry(GeometryHelper.cube(),
materialInstance: materialInstance);
var materialInstance = await viewer.createUnlitMaterialInstance();
var cube = await viewer.createGeometry(GeometryHelper.cube(),
materialInstances: [materialInstance]);
await materialInstance.setParameterFloat4(
"baseColorFactor", 0.0, 1.0, 0.0, 1.0);
await materialInstance.setParameterInt("baseColorIndex", -1);
await viewer.render();
await viewer.render();
await viewer.render();
await testHelper.capture(viewer, "unlit_material_base_color");
await viewer.dispose();
await materialInstance.setParameterFloat4(
"baseColorFactor", 0.0, 1.0, 0.0, 1.0);
await materialInstance.setParameterInt("baseColorIndex", -1);
// await viewer.render();
// await viewer.render();
// await viewer.render();
await testHelper.capture(viewer, "unlit_material_base_color");
});
});
test('unlit fixed size material', () async {
@@ -44,10 +72,10 @@ void main() async {
var materialInstance =
await viewer.createUnlitFixedSizeMaterialInstance();
var cube = await viewer.createGeometry(GeometryHelper.cube(),
materialInstance: materialInstance);
materialInstances: [materialInstance]);
await viewer.setMaterialPropertyFloat4(
cube, "baseColorFactor", 0, 0.0, 1.0, 0.0, 1.0);
await materialInstance.setParameterFloat4(
"baseColorFactor", 0.0, 1.0, 0.0, 1.0);
await testHelper.capture(viewer, "unlit_fixed_size_default_scale");
@@ -69,12 +97,12 @@ void main() async {
var materialInstance = await viewer.createUbershaderMaterialInstance();
final cube = await viewer.createGeometry(
GeometryHelper.cube(uvs: true, normals: true),
materialInstance: materialInstance);
materialInstances: [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 viewer.applyTexture(texture as ThermionFFITexture, cube.entity);
await testHelper.capture(
viewer, "geometry_cube_with_custom_material_ubershader_texture");
await viewer.removeEntity(cube);
@@ -94,34 +122,34 @@ void main() async {
var materialInstance = await viewer.createUnlitMaterialInstance();
var cube = await viewer.createGeometry(GeometryHelper.cube(),
materialInstance: materialInstance);
materialInstances: [materialInstance]);
var textureData =
File("${testHelper.testDir}/assets/cube_texture_512x512.png")
.readAsBytesSync();
var texture = await viewer.createTexture(textureData);
await viewer.applyTexture(texture, cube);
await viewer.applyTexture(texture, cube.entity);
await testHelper.capture(
viewer, "geometry_cube_with_custom_material_unlit_texture_only");
await viewer.removeEntity(cube);
cube = await viewer.createGeometry(GeometryHelper.cube(),
materialInstance: materialInstance);
materialInstances: [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 materialInstance.setParameterInt("baseColorIndex", -1);
await materialInstance.setParameterFloat4(
"baseColorFactor", 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);
materialInstances: [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 materialInstance.setParameterInt("baseColorIndex", 0);
await materialInstance.setParameterFloat4(
"baseColorFactor", 0.0, 1.0, 0.0, 0.5);
await viewer.applyTexture(texture, cube.entity);
await testHelper.capture(
viewer, "geometry_cube_with_custom_material_unlit_color_and_texture");
@@ -143,253 +171,240 @@ void main() async {
await viewer.dispose();
});
});
group("MaterialInstance", () {
test('set depth func to always', () async {
var viewer = await testHelper.createViewer(
bg: kRed, cameraPosition: Vector3(0, 0, 6));
await testHelper.withViewer((viewer) async {
final (
:blueCube,
:blueMaterialInstance,
:greenCube,
:greenMaterialInstance
) = await setup(viewer);
var materialInstance1 = await viewer.createUnlitMaterialInstance();
final cube1 = await viewer.createGeometry(GeometryHelper.cube(),
materialInstance: materialInstance1);
// with default depth func, blue cube renders behind the green cube
await testHelper.capture(
viewer, "material_instance_depth_func_default");
await materialInstance1!
.setParameterFloat4("baseColorFactor", 0.0, 1.0, 0.0, 1.0);
await greenMaterialInstance.setDepthFunc(SamplerCompareFunction.A);
var materialInstance2 = await viewer.createUnlitMaterialInstance();
final cube2 = await viewer.createGeometry(GeometryHelper.cube(),
materialInstance: materialInstance2);
await viewer.setPosition(cube2, 1.0, 0.0, -1.0);
await materialInstance2!
.setParameterFloat4("baseColorFactor", 0.0, 0.0, 1.0, 1.0);
// with default depth func, blue cube renders behind the green cube
await testHelper.capture(viewer, "material_instance_depth_func_default");
await materialInstance2.setDepthFunc(SamplerCompareFunction.A);
await testHelper.capture(viewer, "material_instance_depth_func_always");
await viewer.dispose();
// with green material depth func set to always pass, green cube will render in front of blue cube
await testHelper.capture(viewer, "material_instance_depth_func_always");
});
});
test('disable depth write', () async {
var viewer = await testHelper.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()));
await testHelper.withViewer((viewer) async {
final (
:blueCube,
:blueMaterialInstance,
:greenCube,
:greenMaterialInstance
) = await setup(viewer);
final cube1 = await viewer.createGeometry(GeometryHelper.cube());
var materialInstance = await viewer.getMaterialInstanceAt(cube1, 0);
// With depth write enabled on both materials, green cube renders behind the blue cube
await testHelper.capture(
viewer, "material_instance_depth_write_enabled");
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);
// 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, "material_instance_depth_write_disabled");
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");
await viewer.dispose();
// Set priority for greenCube to render last, making it appear in front
await viewer.setPriority(greenCube.entity, 7);
await testHelper.capture(
viewer, "material_instance_depth_write_disabled_with_priority");
});
});
test('set uv scaling (unlit)', () async {
var viewer = await testHelper.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()));
test('enable stencil write', () async {
await testHelper.withViewer((viewer) async {
final (
:blueCube,
:blueMaterialInstance,
:greenCube,
:greenMaterialInstance
) = await setup(viewer);
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);
// force depth to always pass so we're just comparing stencil test
await greenMaterialInstance.setDepthFunc(SamplerCompareFunction.A);
await blueMaterialInstance.setDepthFunc(SamplerCompareFunction.A);
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");
await viewer.dispose();
await testHelper.capture(
viewer, "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, "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, "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, "fail_stencil_ne");
}, postProcessing: true);
});
});
}
group("stencil", () {
test('set stencil highlight for glb', () async {
final viewer = await testHelper.createViewer();
var model = await viewer.loadGlb("${testHelper.testDir}/assets/cube.glb",
keepData: true);
await viewer.setPostProcessing(true);
// group("MaterialInstance", () {
var light = await viewer.addLight(
LightType.SUN, 6500, 1000000, 0, 0, 0, 0, -1, 0);
await viewer.setLightDirection(light, Vector3(0, 1, -1));
// test('disable depth write', () async {
// var viewer = await testHelper.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()));
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");
// final cube1 = await viewer.createGeometry(GeometryHelper.cube());
// var materialInstance = await viewer.getMaterialInstanceAt(cube1, 0);
await viewer.dispose();
});
// 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);
test('set stencil highlight for geometry', () async {
var viewer = await testHelper.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));
// expect(materialInstance, isNotNull);
var cube = await viewer.createGeometry(GeometryHelper.cube());
await viewer.setStencilHighlight(cube);
// // with depth write enabled on both materials, cube2 renders behind the white cube
// await testHelper.capture(viewer, "material_instance_depth_write_enabled");
await testHelper.capture(viewer, "stencil_highlight_geometry");
// // 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 viewer.removeStencilHighlight(cube);
// // 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");
// await viewer.dispose();
// });
await testHelper.capture(viewer, "stencil_highlight_geometry_remove");
// test('set uv scaling (unlit)', () async {
// var viewer = await testHelper.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()));
await viewer.dispose();
});
// 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);
test('set stencil highlight for gltf asset', () async {
var viewer = await testHelper.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 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");
// await viewer.dispose();
// });
// });
var cube1 = await viewer.loadGlb("${testHelper.testDir}/assets/cube.glb",
keepData: true);
await viewer.transformToUnitCube(cube1);
// group("texture", () {
// test("create/apply/dispose texture", () async {
// var viewer = await testHelper.createViewer();
await viewer.setStencilHighlight(cube1);
// var textureData =
// File("${testHelper.testDir}/assets/cube_texture_512x512.png")
// .readAsBytesSync();
await testHelper.capture(viewer, "stencil_highlight_gltf");
// 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(),
// materialInstances: [materialInstance]);
await viewer.removeStencilHighlight(cube1);
// await viewer.setPostProcessing(true);
// await viewer.setToneMapping(ToneMapper.LINEAR);
await testHelper.capture(viewer, "stencil_highlight_gltf_removed");
// await viewer.applyTexture(texture, cube,
// materialIndex: 0, parameterName: "baseColorMap");
await viewer.dispose();
});
// await testHelper.capture(viewer, "texture_applied_to_geometry");
test('set stencil highlight for multiple geometry ', () async {
var viewer = await testHelper.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");
await viewer.dispose();
});
test('set stencil highlight for multiple gltf assets ', () async {
var viewer = await testHelper.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}/assets/cube.glb",
keepData: true);
await viewer.transformToUnitCube(cube1);
var cube2 = await viewer.loadGlb("${testHelper.testDir}/assets/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");
await viewer.dispose();
});
});
group("texture", () {
test("create/apply/dispose texture", () async {
var viewer = await testHelper.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);
await viewer.dispose();
});
});
// await viewer.removeEntity(cube);
// await viewer.destroyTexture(texture);
// await viewer.dispose();
// });
// });
// group("unproject", () {
// test("unproject", () async {
@@ -523,4 +538,4 @@ void main() async {
// }
// }, timeout: Timeout(Duration(minutes: 2)));
// });
}
// }

View File

@@ -0,0 +1,268 @@
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("overlay");
group("overlay tests", () {
group("stencil", () {
test('set stencil highlight for geometry', () async {
await testHelper.withViewer((viewer) async {
var cube = await viewer.createGeometry(GeometryHelper.cube());
await testHelper.capture(viewer, "geometry_before_stencil_highlight");
await cube.setStencilHighlight();
await testHelper.capture(viewer, "geometry_add_stencil_highlight");
await cube.removeStencilHighlight();
await testHelper.capture(viewer, "geometry_remove_stencil_highlight");
}, postProcessing: true);
});
test('set stencil highlight for glb', () async {
await testHelper.withViewer((viewer) async {
var cube = await viewer.loadGlb(
"${testHelper.testDir}/assets/cube.glb",
numInstances: 2);
await testHelper.capture(viewer, "glb_before_stencil_highlight");
// gltf is slightly more complicated, because the "head" entity is
// not renderable, so we need to pick a child entity with a renderable
// component.
await cube.setStencilHighlight(entityIndex: 0);
await testHelper.capture(viewer, "glb_add_stencil_highlight");
await cube.removeStencilHighlight();
await testHelper.capture(viewer, "glb_remove_stencil_highlight");
}, postProcessing: true, bg: kWhite);
});
test('add gizmo', () async {
await testHelper.withViewer((viewer) async {
var cube = await viewer.createGeometry(GeometryHelper.cube());
final view = await viewer.getViewAt(0);
final gizmo = await viewer.createGizmo(view);
await viewer.setLayerVisibility(VisibilityLayers.OVERLAY, true);
await viewer.addEntityToScene(gizmo);
await viewer.setTransform(gizmo, Matrix4.identity());
// await gizmo.attach(cube.entity);
await testHelper.capture(viewer, "gizmo_attached");
// await gizmo.detach();
// await testHelper.capture(viewer, "gizmo_detached");
}, postProcessing: true, bg: kWhite);
});
});
});
// test('set uv scaling (unlit)', () async {
// var viewer = await testHelper.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");
// await viewer.dispose();
// });
// });
// group("texture", () {
// test("create/apply/dispose texture", () async {
// var viewer = await testHelper.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(),
// materialInstances: [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);
// await viewer.dispose();
// });
// });
// group("unproject", () {
// test("unproject", () async {
// final dimensions = (width: 1280, height: 768);
// var viewer = await testHelper.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 = <Uint8List>[];
// // 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)));
}