add texture methods (including unproject)

This commit is contained in:
Nick Fisher
2024-09-16 20:51:14 +08:00
parent 7d2cf3f91b
commit 676ddc3773
19 changed files with 1093 additions and 724 deletions

View File

@@ -1,14 +1,16 @@
import 'dart:math'; import 'dart:math';
import 'dart:typed_data';
import 'package:thermion_dart/thermion_dart/viewer/shared_types/geometry.dart'; import 'package:thermion_dart/thermion_dart/viewer/shared_types/geometry.dart';
import 'package:thermion_dart/thermion_dart/viewer/thermion_viewer_base.dart';
class GeometryHelper { class GeometryHelper {
static Geometry sphere({bool normals = true, bool uvs = true}) { static Geometry sphere({bool normals = true, bool uvs = true}) {
int latitudeBands = 20; int latitudeBands = 20;
int longitudeBands = 20; int longitudeBands = 20;
List<double> vertices = []; List<double> verticesList = [];
List<double> _normals = []; List<double> normalsList = [];
List<double> uvsList = [];
List<int> indices = []; List<int> indices = [];
for (int latNumber = 0; latNumber <= latitudeBands; latNumber++) { for (int latNumber = 0; latNumber <= latitudeBands; latNumber++) {
@@ -25,12 +27,10 @@ class GeometryHelper {
double y = cosTheta; double y = cosTheta;
double z = sinPhi * sinTheta; double z = sinPhi * sinTheta;
vertices.addAll([x, y, z]); verticesList.addAll([x, y, z]);
_normals.addAll([ normalsList.addAll([x, y, z]);
x,
y, uvsList.addAll([longNumber / longitudeBands, latNumber / latitudeBands]);
z
]); // For a sphere, normals are the same as vertex positions
} }
} }
@@ -39,127 +39,171 @@ class GeometryHelper {
int first = (latNumber * (longitudeBands + 1)) + longNumber; int first = (latNumber * (longitudeBands + 1)) + longNumber;
int second = first + longitudeBands + 1; int second = first + longitudeBands + 1;
indices indices.addAll([first, second, first + 1, second, second + 1, first + 1]);
.addAll([first, second, first + 1, second, second + 1, first + 1]);
} }
} }
return Geometry(vertices, indices, normals: normals ? _normals : null); Float32List vertices = Float32List.fromList(verticesList);
Float32List? _normals = normals ? Float32List.fromList(normalsList) : null;
Float32List? _uvs = uvs ? Float32List.fromList(uvsList) : null;
return Geometry(vertices, indices, normals: _normals, uvs: _uvs);
} }
static Geometry cube({bool normals = true, bool uvs = true}) { static Geometry cube({bool normals = true, bool uvs = true}) {
final vertices = <double>[ final vertices = Float32List.fromList([
// Front face // Front face
-1, -1, 1, -1, -1, 1,
1, -1, 1, 1, -1, 1,
1, 1, 1, 1, 1, 1,
-1, 1, 1, -1, 1, 1,
// Back face // Back face
-1, -1, -1, -1, -1, -1,
-1, 1, -1, -1, 1, -1,
1, 1, -1, 1, 1, -1,
1, -1, -1, 1, -1, -1,
// Top face // Top face
-1, 1, -1, -1, 1, -1,
-1, 1, 1, -1, 1, 1,
1, 1, 1, 1, 1, 1,
1, 1, -1, 1, 1, -1,
// Bottom face // Bottom face
-1, -1, -1, -1, -1, -1,
1, -1, -1, 1, -1, -1,
1, -1, 1, 1, -1, 1,
-1, -1, 1, -1, -1, 1,
// Right face // Right face
1, -1, -1, 1, -1, -1,
1, 1, -1, 1, 1, -1,
1, 1, 1, 1, 1, 1,
1, -1, 1, 1, -1, 1,
// Left face // Left face
-1, -1, -1, -1, -1, -1,
-1, -1, 1, -1, -1, 1,
-1, 1, 1, -1, 1, 1,
-1, 1, -1, -1, 1, -1,
]; ]);
final _normals = <double>[ final _normals = normals ? Float32List.fromList([
// Front face // Front face
0, 0, 1, 0, 0, 1,
0, 0, 1, 0, 0, 1,
0, 0, 1, 0, 0, 1,
0, 0, 1, 0, 0, 1,
// Back face // Back face
0, 0, -1, 0, 0, -1,
0, 0, -1, 0, 0, -1,
0, 0, -1, 0, 0, -1,
0, 0, -1, 0, 0, -1,
// Top face // Top face
0, 1, 0, 0, 1, 0,
0, 1, 0, 0, 1, 0,
0, 1, 0, 0, 1, 0,
0, 1, 0, 0, 1, 0,
// Bottom face // Bottom face
0, -1, 0, 0, -1, 0,
0, -1, 0, 0, -1, 0,
0, -1, 0, 0, -1, 0,
0, -1, 0, 0, -1, 0,
// Right face // Right face
1, 0, 0, 1, 0, 0,
1, 0, 0, 1, 0, 0,
1, 0, 0, 1, 0, 0,
1, 0, 0, 1, 0, 0,
// Left face // Left face
-1, 0, 0, -1, 0, 0,
-1, 0, 0, -1, 0, 0,
-1, 0, 0, -1, 0, 0,
-1, 0, 0, -1, 0, 0,
]; ]) : null;
final indices = [ final _uvs = uvs ? Float32List.fromList([
// Front face // Front face
0, 1, 2, 0, 2, 3, 1/3, 1/3,
// Back face 2/3, 1/3,
4, 5, 6, 4, 6, 7, 2/3, 2/3,
// Top face 1/3, 2/3,
8, 9, 10, 8, 10, 11,
// Bottom face // Back face
12, 13, 14, 12, 14, 15, 2/3, 2/3,
// Right face 2/3, 1,
16, 17, 18, 16, 18, 19, 1, 1,
// Left face 1, 2/3,
20, 21, 22, 20, 22, 23
]; // Top face
return Geometry(vertices, indices, normals: normals ? _normals : null); 1/3, 0,
} 1/3, 1/3,
2/3, 1/3,
2/3, 0,
// Bottom face
1/3, 2/3,
2/3, 2/3,
2/3, 1,
1/3, 1,
// Right face
2/3, 1/3,
2/3, 2/3,
1, 2/3,
1, 1/3,
// Left face
0, 1/3,
1/3, 1/3,
1/3, 2/3,
0, 2/3,
]) : null;
final indices = [
// Front face
0, 1, 2, 0, 2, 3,
// Back face
4, 5, 6, 4, 6, 7,
// Top face
8, 9, 10, 8, 10, 11,
// Bottom face
12, 13, 14, 12, 14, 15,
// Right face
16, 17, 18, 16, 18, 19,
// Left face
20, 21, 22, 20, 22, 23
];
return Geometry(vertices, indices, normals: _normals, uvs: _uvs);
}
static Geometry cylinder({double radius = 1.0, double length = 1.0, bool normals = true, bool uvs = true }) { static Geometry cylinder({double radius = 1.0, double length = 1.0, bool normals = true, bool uvs = true }) {
int segments = 32; int segments = 32;
List<double> vertices = []; List<double> verticesList = [];
List<double> _normals = []; List<double> normalsList = [];
List<double> uvsList = [];
List<int> indices = []; List<int> indices = [];
// Create vertices and normals // Create vertices, normals, and UVs
for (int i = 0; i <= segments; i++) { for (int i = 0; i <= segments; i++) {
double theta = i * 2 * pi / segments; double theta = i * 2 * pi / segments;
double x = radius * cos(theta); double x = radius * cos(theta);
double z = radius * sin(theta); double z = radius * sin(theta);
// Top circle // Top circle
vertices.addAll([x, length / 2, z]); verticesList.addAll([x, length / 2, z]);
_normals.addAll([x / radius, 0, z / radius]); normalsList.addAll([x / radius, 0, z / radius]);
uvsList.addAll([i / segments, 1]);
// Bottom circle // Bottom circle
vertices.addAll([x, -length / 2, z]); verticesList.addAll([x, -length / 2, z]);
_normals.addAll([x / radius, 0, z / radius]); normalsList.addAll([x / radius, 0, z / radius]);
uvsList.addAll([i / segments, 0]);
} }
// Create indices // Create indices
@@ -178,45 +222,62 @@ class GeometryHelper {
indices.addAll([bottomFirst, bottomSecond, topSecond]); indices.addAll([bottomFirst, bottomSecond, topSecond]);
} }
// Add center vertices and normals for top and bottom faces // Add center vertices, normals, and UVs for top and bottom faces
vertices.addAll([0, length / 2, 0]); // Top center verticesList.addAll([0, length / 2, 0]); // Top center
_normals.addAll([0, 1, 0]); normalsList.addAll([0, 1, 0]);
vertices.addAll([0, -length / 2, 0]); // Bottom center uvsList.addAll([0.5, 0.5]); // Center of top face
_normals.addAll([0, -1, 0]);
// Add top and bottom face normals verticesList.addAll([0, -length / 2, 0]); // Bottom center
normalsList.addAll([0, -1, 0]);
uvsList.addAll([0.5, 0.5]); // Center of bottom face
// Add top and bottom face normals and UVs
for (int i = 0; i <= segments; i++) { for (int i = 0; i <= segments; i++) {
_normals.addAll([0, 1, 0]); // Top face normal normalsList.addAll([0, 1, 0]); // Top face normal
_normals.addAll([0, -1, 0]); // Bottom face normal normalsList.addAll([0, -1, 0]); // Bottom face normal
double u = 0.5 + 0.5 * cos(i * 2 * pi / segments);
double v = 0.5 + 0.5 * sin(i * 2 * pi / segments);
uvsList.addAll([u, v]); // Top face UV
uvsList.addAll([u, v]); // Bottom face UV
} }
return Geometry(vertices, indices, normals: normals ? _normals : null); Float32List vertices = Float32List.fromList(verticesList);
Float32List? _normals = normals ? Float32List.fromList(normalsList) : null;
Float32List? _uvs = uvs ? Float32List.fromList(uvsList) : null;
return Geometry(vertices, indices, normals: _normals, uvs: _uvs);
} }
static Geometry conic({double radius = 1.0, double length = 1.0, bool normals = true, bool uvs = true}) { static Geometry conic({double radius = 1.0, double length = 1.0, bool normals = true, bool uvs = true}) {
int segments = 32; int segments = 32;
List<double> vertices = []; List<double> verticesList = [];
List<double> _normals = []; List<double> normalsList = [];
List<double> uvsList = [];
List<int> indices = []; List<int> indices = [];
// Create vertices and normals // Create vertices, normals, and UVs
for (int i = 0; i <= segments; i++) { for (int i = 0; i <= segments; i++) {
double theta = i * 2 * pi / segments; double theta = i * 2 * pi / segments;
double x = radius * cos(theta); double x = radius * cos(theta);
double z = radius * sin(theta); double z = radius * sin(theta);
// Base circle // Base circle
vertices.addAll([x, 0, z]); verticesList.addAll([x, 0, z]);
// Calculate normal for the side // Calculate normal for the side
double nx = x / sqrt(x * x + length * length); double nx = x / sqrt(x * x + length * length);
double nz = z / sqrt(z * z + length * length); double nz = z / sqrt(z * z + length * length);
double ny = radius / sqrt(radius * radius + length * length); double ny = radius / sqrt(radius * radius + length * length);
_normals.addAll([nx, ny, nz]); normalsList.addAll([nx, ny, nz]);
// UV coordinates
uvsList.addAll([i / segments, 0]);
} }
// Apex // Apex
vertices.addAll([0, length, 0]); verticesList.addAll([0, length, 0]);
_normals.addAll([0, 1, 0]); // Normal at apex points straight up normalsList.addAll([0, 1, 0]); // Normal at apex points straight up
uvsList.addAll([0.5, 1]); // UV for apex
// Create indices // Create indices
for (int i = 0; i < segments; i++) { for (int i = 0; i < segments; i++) {
@@ -226,54 +287,48 @@ class GeometryHelper {
indices.addAll([i, segments, i + 1]); indices.addAll([i, segments, i + 1]);
} }
// Add base face normals // Add base face normals and UVs
for (int i = 0; i <= segments; i++) { for (int i = 0; i <= segments; i++) {
_normals.addAll([0, -1, 0]); // Base face normal normalsList.addAll([0, -1, 0]); // Base face normal
double u = 0.5 + 0.5 * cos(i * 2 * pi / segments);
double v = 0.5 + 0.5 * sin(i * 2 * pi / segments);
uvsList.addAll([u, v]); // Base face UV
} }
return Geometry(vertices, indices, normals: normals ? _normals : null); Float32List vertices = Float32List.fromList(verticesList);
Float32List? _normals = normals ? Float32List.fromList(normalsList) : null;
Float32List? _uvs = uvs ? Float32List.fromList(uvsList) : null;
return Geometry(vertices, indices, normals: _normals, uvs: _uvs);
} }
static Geometry plane({double width = 1.0, double height = 1.0, bool normals = true, bool uvs = true}) { static Geometry plane({double width = 1.0, double height = 1.0, bool normals = true, bool uvs = true}) {
List<double> vertices = [ Float32List vertices = Float32List.fromList([
-width / 2, -width / 2, 0, -height / 2,
0, width / 2, 0, -height / 2,
-height / 2, width / 2, 0, height / 2,
width / 2, -width / 2, 0, height / 2,
0, ]);
-height / 2,
width / 2,
0,
height / 2,
-width / 2,
0,
height / 2,
];
List<double> _normals = [ Float32List? _normals = normals ? Float32List.fromList([
0, 0, 1, 0,
1, 0, 1, 0,
0, 0, 1, 0,
0, 0, 1, 0,
1, ]) : null;
0,
0, Float32List? _uvs = uvs ? Float32List.fromList([
1, 0, 0,
0, 1, 0,
0, 1, 1,
1, 0, 1,
0, ]) : null;
];
List<int> indices = [ List<int> indices = [
0, 0, 2, 1,
2, 0, 3, 2,
1,
0,
3,
2,
]; ];
return Geometry(vertices, indices, normals: normals ? _normals : null); return Geometry(vertices, indices, normals: _normals, uvs: _uvs);
} }
} }

View File

@@ -1039,37 +1039,22 @@ external void remove_animation_component(
ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>,
ffi.Pointer<ffi.Float>, ffi.Pointer<ffi.Float>,
ffi.Int, ffi.Int,
ffi.Pointer<ffi.Float>,
ffi.Int,
ffi.Pointer<ffi.Float>,
ffi.Int,
ffi.Pointer<ffi.Uint16>, ffi.Pointer<ffi.Uint16>,
ffi.Int, ffi.Int,
ffi.Int, ffi.Int,
ffi.Pointer<ffi.Char>)>(isLeaf: true) ffi.Pointer<ffi.Char>)>(isLeaf: true)
external int create_geometry( external int create_geometry(
ffi.Pointer<ffi.Void> sceneManager,
ffi.Pointer<ffi.Float> vertices,
int numVertices,
ffi.Pointer<ffi.Uint16> indices,
int numIndices,
int primitiveType,
ffi.Pointer<ffi.Char> materialPath,
);
@ffi.Native<
EntityId Function(
ffi.Pointer<ffi.Void>,
ffi.Pointer<ffi.Float>,
ffi.Int,
ffi.Pointer<ffi.Float>,
ffi.Int,
ffi.Pointer<ffi.Uint16>,
ffi.Int,
ffi.Int,
ffi.Pointer<ffi.Char>)>(isLeaf: true)
external int create_geometry_with_normals(
ffi.Pointer<ffi.Void> sceneManager, ffi.Pointer<ffi.Void> sceneManager,
ffi.Pointer<ffi.Float> vertices, ffi.Pointer<ffi.Float> vertices,
int numVertices, int numVertices,
ffi.Pointer<ffi.Float> normals, ffi.Pointer<ffi.Float> normals,
int numNormals, int numNormals,
ffi.Pointer<ffi.Float> uvs,
int numUvs,
ffi.Pointer<ffi.Uint16> indices, ffi.Pointer<ffi.Uint16> indices,
int numIndices, int numIndices,
int primitiveType, int primitiveType,
@@ -1214,6 +1199,44 @@ external void set_material_property_float4(
float4 value, float4 value,
); );
@ffi.Native<
ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId, ffi.Pointer<ffi.Uint8>,
ffi.Uint32, ffi.Uint32)>(isLeaf: true)
external void unproject_texture(
ffi.Pointer<ffi.Void> sceneManager,
int entity,
ffi.Pointer<ffi.Uint8> out,
int outWidth,
int outHeight,
);
@ffi.Native<
ffi.Pointer<ffi.Void> Function(
ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Uint8>, ffi.Size)>(isLeaf: true)
external ffi.Pointer<ffi.Void> create_texture(
ffi.Pointer<ffi.Void> sceneManager,
ffi.Pointer<ffi.Uint8> data,
int length,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>(
isLeaf: true)
external void destroy_texture(
ffi.Pointer<ffi.Void> sceneManager,
ffi.Pointer<ffi.Void> texture,
);
@ffi.Native<
ffi.Void Function(ffi.Pointer<ffi.Void>, EntityId, ffi.Pointer<ffi.Void>,
ffi.Pointer<ffi.Char>, ffi.Int)>(isLeaf: true)
external void apply_texture_to_material(
ffi.Pointer<ffi.Void> sceneManager,
int entity,
ffi.Pointer<ffi.Void> texture,
ffi.Pointer<ffi.Char> parameterName,
int materialIndex,
);
@ffi.Native< @ffi.Native<
ffi.Void Function( ffi.Void Function(
ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>,
@@ -1683,6 +1706,10 @@ external void reset_to_rest_pose_ffi(
ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>,
ffi.Pointer<ffi.Float>, ffi.Pointer<ffi.Float>,
ffi.Int, ffi.Int,
ffi.Pointer<ffi.Float>,
ffi.Int,
ffi.Pointer<ffi.Float>,
ffi.Int,
ffi.Pointer<ffi.Uint16>, ffi.Pointer<ffi.Uint16>,
ffi.Int, ffi.Int,
ffi.Int, ffi.Int,
@@ -1694,6 +1721,10 @@ external void create_geometry_ffi(
ffi.Pointer<ffi.Void> sceneManager, ffi.Pointer<ffi.Void> sceneManager,
ffi.Pointer<ffi.Float> vertices, ffi.Pointer<ffi.Float> vertices,
int numVertices, int numVertices,
ffi.Pointer<ffi.Float> normals,
int numNormals,
ffi.Pointer<ffi.Float> uvs,
int numUvs,
ffi.Pointer<ffi.Uint16> indices, ffi.Pointer<ffi.Uint16> indices,
int numIndices, int numIndices,
int primitiveType, int primitiveType,
@@ -1703,31 +1734,20 @@ external void create_geometry_ffi(
); );
@ffi.Native< @ffi.Native<
ffi.Void Function( ffi.Void Function(
ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>,
ffi.Pointer<ffi.Float>, EntityId,
ffi.Int, ffi.Pointer<ffi.Uint8>,
ffi.Pointer<ffi.Float>, ffi.Uint32,
ffi.Int, ffi.Uint32,
ffi.Pointer<ffi.Uint16>, ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
ffi.Int, external void unproject_texture_ffi(
ffi.Int,
ffi.Pointer<ffi.Char>,
ffi.Bool,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>>)>(
isLeaf: true)
external void create_geometry_with_normals_ffi(
ffi.Pointer<ffi.Void> sceneManager, ffi.Pointer<ffi.Void> sceneManager,
ffi.Pointer<ffi.Float> vertices, int entity,
int numVertices, ffi.Pointer<ffi.Uint8> out,
ffi.Pointer<ffi.Float> normals, int outWidth,
int numNormals, int outHeight,
ffi.Pointer<ffi.Uint16> indices, ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> callback,
int numIndices,
int primitiveType,
ffi.Pointer<ffi.Char> materialPath,
bool keepData,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(EntityId)>> callback,
); );
final class ResourceBuffer extends ffi.Struct { final class ResourceBuffer extends ffi.Struct {

View File

@@ -58,7 +58,8 @@ class ThermionViewerFFI extends ThermionViewer {
/// ///
Stream<SceneUpdateEvent> get sceneUpdated => Stream<SceneUpdateEvent> get sceneUpdated =>
_sceneUpdateEventController.stream; _sceneUpdateEventController.stream;
final _sceneUpdateEventController = StreamController<SceneUpdateEvent>.broadcast(); final _sceneUpdateEventController =
StreamController<SceneUpdateEvent>.broadcast();
final Pointer<Void> resourceLoader; final Pointer<Void> resourceLoader;
@@ -355,9 +356,6 @@ class ThermionViewerFFI extends ThermionViewer {
remove_ibl_ffi(_viewer!); remove_ibl_ffi(_viewer!);
} }
///
///
///
@override @override
Future<ThermionEntity> addLight( Future<ThermionEntity> addLight(
LightType type, LightType type,
@@ -376,30 +374,21 @@ class ThermionViewerFFI extends ThermionViewer {
double sunHaloSize = 10.0, double sunHaloSize = 10.0,
double sunHaloFallof = 80.0, double sunHaloFallof = 80.0,
bool castShadows = true}) async { bool castShadows = true}) async {
var entity = await withIntCallback((callback) => add_light_ffi( DirectLight directLight = DirectLight(
_viewer!, type: type,
type.index, color: colour,
colour, intensity: intensity,
intensity, position: Vector3(posX, posY, posZ),
posX, direction: Vector3(dirX, dirY, dirZ),
posY, falloffRadius: falloffRadius,
posZ, spotLightConeInner: spotLightConeInner,
dirX, spotLightConeOuter: spotLightConeOuter,
dirY, sunAngularRadius: sunAngularRadius,
dirZ, sunHaloSize: sunHaloSize,
falloffRadius, sunHaloFallof: sunHaloFallof,
spotLightConeInner, castShadows: castShadows);
spotLightConeOuter,
sunAngularRadius = 0.545,
sunHaloSize = 10.0,
sunHaloFallof = 80.0,
castShadows,
callback));
if (entity == _FILAMENT_ASSET_ERROR) {
throw Exception("Failed to add light to scene");
}
return entity; return addDirectLight(directLight);
} }
/// ///
@@ -1827,49 +1816,25 @@ class ThermionViewerFFI extends ThermionViewer {
final materialPathPtr = final materialPathPtr =
geometry.materialPath?.toNativeUtf8(allocator: allocator) ?? nullptr; geometry.materialPath?.toNativeUtf8(allocator: allocator) ?? nullptr;
final vertexPtr = allocator<Float>(geometry.vertices.length);
final indicesPtr = allocator<Uint16>(geometry.indices.length);
for (int i = 0; i < geometry.vertices.length; i++) {
vertexPtr[i] = geometry.vertices[i];
}
for (int i = 0; i < geometry.indices.length; i++) { var entity = await withIntCallback((callback) => create_geometry_ffi(
(indicesPtr + i).value = geometry.indices[i]; _sceneManager!,
} geometry.vertices.address,
geometry.vertices.length,
var normalsPtr = nullptr.cast<Float>(); geometry.normals.address,
if (geometry.normals != null) { geometry.normals.length,
normalsPtr = allocator<Float>(geometry.normals!.length); geometry.uvs.address,
for (int i = 0; i < geometry.normals!.length; i++) { geometry.uvs.length,
normalsPtr[i] = geometry.normals![i]; geometry.indices.address,
} geometry.indices.length,
} geometry.primitiveType.index,
materialPathPtr.cast<Char>(),
var entity = await withIntCallback((callback) => keepData,
create_geometry_with_normals_ffi( callback));
_sceneManager!,
vertexPtr,
geometry.vertices.length,
normalsPtr,
geometry.normals?.length ?? 0,
indicesPtr,
geometry.indices.length,
geometry.primitiveType.index,
materialPathPtr.cast<Char>(),
keepData,
callback));
if (entity == _FILAMENT_ASSET_ERROR) { if (entity == _FILAMENT_ASSET_ERROR) {
throw Exception("Failed to create geometry"); throw Exception("Failed to create geometry");
} }
allocator.free(materialPathPtr);
allocator.free(vertexPtr);
allocator.free(indicesPtr);
if (geometry.normals != null) {
allocator.free(normalsPtr);
}
_sceneUpdateEventController _sceneUpdateEventController
.add(SceneUpdateEvent.addGeometry(entity, geometry)); .add(SceneUpdateEvent.addGeometry(entity, geometry));
@@ -2001,4 +1966,38 @@ class ThermionViewerFFI extends ThermionViewer {
_sceneManager!, entity, materialIndex, ptr.cast<Char>(), struct); _sceneManager!, entity, materialIndex, ptr.cast<Char>(), struct);
allocator.free(ptr); allocator.free(ptr);
} }
Future<Uint8List> unproject(
ThermionEntity entity, int outWidth, int outHeight) async {
final outPtr = Uint8List(outWidth * outHeight * 4);
await withVoidCallback((callback) {
unproject_texture_ffi(
_viewer!, entity, outPtr.address, outWidth, outHeight, callback);
});
return outPtr.buffer.asUint8List();
}
Future<ThermionTexture> createTexture(Uint8List data) async {
var ptr = create_texture(_sceneManager!, data.address, data.length);
return ThermionFFITexture(ptr);
}
Future applyTexture(ThermionFFITexture texture, ThermionEntity entity,
{int materialIndex = 0, String parameterName = "baseColorMap"}) async {
using(parameterName.toNativeUtf8(), (namePtr) async {
apply_texture_to_material(_sceneManager!, entity, texture._pointer,
namePtr.cast<Char>(), materialIndex);
});
}
Future destroyTexture(ThermionFFITexture texture) async {
destroy_texture(_sceneManager!, texture._pointer);
}
}
class ThermionFFITexture extends ThermionTexture {
final Pointer<Void> _pointer;
ThermionFFITexture(this._pointer);
} }

View File

@@ -7,3 +7,7 @@ export 'light_options.dart';
// a handle that can be safely passed back to the rendering layer to manipulate an Entity // a handle that can be safely passed back to the rendering layer to manipulate an Entity
typedef ThermionEntity = int; typedef ThermionEntity = int;
abstract class ThermionTexture {
}

View File

@@ -1,19 +1,34 @@
import 'dart:typed_data';
import 'package:thermion_dart/thermion_dart/viewer/thermion_viewer_base.dart'; import 'package:thermion_dart/thermion_dart/viewer/thermion_viewer_base.dart';
class Geometry { class Geometry {
final List<double> vertices; final Float32List vertices;
final List<int> indices; final Uint16List indices;
final List<double>? normals; final Float32List normals;
final List<(double, double)>? uvs; final Float32List uvs;
final PrimitiveType primitiveType; final PrimitiveType primitiveType;
final String? materialPath; final String? materialPath;
Geometry(this.vertices, this.indices, { this.normals=null, this.uvs=null, Geometry(
this.primitiveType = PrimitiveType.TRIANGLES, this.materialPath = null}); this.vertices,
List<int> indices, {
Float32List? normals,
Float32List? uvs,
this.primitiveType = PrimitiveType.TRIANGLES,
this.materialPath,
}) : indices = Uint16List.fromList(indices),
normals = normals ?? Float32List(0),
uvs = uvs ?? Float32List(0) {
assert(this.uvs.length == 0 || this.uvs.length == (vertices.length ~/ 3) * 2);
}
void scale(double factor) { void scale(double factor) {
for (int i = 0; i < vertices.length; i++) { for (int i = 0; i < vertices.length; i++) {
vertices[i] = vertices[i] * factor; vertices[i] = vertices[i] * factor;
} }
} }
bool get hasNormals => normals.isNotEmpty;
bool get hasUVs => uvs.isNotEmpty;
} }

View File

@@ -11,10 +11,9 @@ import 'dart:async';
import 'package:animation_tools_dart/animation_tools_dart.dart'; import 'package:animation_tools_dart/animation_tools_dart.dart';
abstract class ThermionViewer { abstract class ThermionViewer {
///
///
/// A Future that resolves when the underlying rendering context has been successfully created. /// A Future that resolves when the underlying rendering context has been successfully created.
/// ///
Future<bool> get initialized; Future<bool> get initialized;
/// ///
@@ -136,7 +135,8 @@ abstract class ThermionViewer {
/// Note that [sunAngularRadius] is in degrees, /// Note that [sunAngularRadius] is in degrees,
/// whereas [spotLightConeInner] and [spotLightConeOuter] are in radians /// whereas [spotLightConeInner] and [spotLightConeOuter] are in radians
/// ///
@Deprecated("This will be removed in future versions. Use addDirectLight instead.") @Deprecated(
"This will be removed in future versions. Use addDirectLight instead.")
Future<ThermionEntity> addLight( Future<ThermionEntity> addLight(
LightType type, LightType type,
double colour, double colour,
@@ -161,8 +161,7 @@ abstract class ThermionViewer {
/// Note that [sunAngularRadius] is in degrees, /// Note that [sunAngularRadius] is in degrees,
/// whereas [spotLightConeInner] and [spotLightConeOuter] are in radians /// whereas [spotLightConeInner] and [spotLightConeOuter] are in radians
/// ///
Future<ThermionEntity> addDirectLight( Future<ThermionEntity> addDirectLight(DirectLight light);
DirectLight light);
/// ///
/// Remove a light from the scene. /// Remove a light from the scene.
@@ -787,7 +786,7 @@ abstract class ThermionViewer {
/// Creates a (renderable) entity with the specified geometry and adds to the scene. /// Creates a (renderable) entity with the specified geometry and adds to the scene.
/// If [keepData] is true, the source data will not be released. /// If [keepData] is true, the source data will not be released.
/// ///
Future createGeometry(Geometry geometry, { bool keepData= false}); Future createGeometry(Geometry geometry, {bool keepData = false});
/// ///
/// Gets the parent entity of [entity]. Returns null if the entity has no parent. /// Gets the parent entity of [entity]. Returns null if the entity has no parent.
@@ -853,4 +852,21 @@ abstract class ThermionViewer {
/// Removes the outline around [entity]. Noop if there was no highlight. /// Removes the outline around [entity]. Noop if there was no highlight.
/// ///
Future removeStencilHighlight(ThermionEntity entity); Future removeStencilHighlight(ThermionEntity entity);
///
/// Decodes the specified image data and creates a texture.
///
Future<ThermionTexture> createTexture(Uint8List data);
///
///
///
Future applyTexture(covariant ThermionTexture texture, ThermionEntity entity,
{int materialIndex = 0, String parameterName = "baseColorMap"});
///
///
///
Future destroyTexture(covariant ThermionTexture texture);
} }

View File

@@ -24,6 +24,8 @@ public:
uint32_t numVertices, uint32_t numVertices,
float* normals, float* normals,
uint32_t numNormals, uint32_t numNormals,
float *uvs,
uint32_t numUvs,
uint16_t* indices, uint16_t* indices,
uint32_t numIndices, uint32_t numIndices,
RenderableManager::PrimitiveType primitiveType, RenderableManager::PrimitiveType primitiveType,
@@ -36,6 +38,7 @@ public:
float* vertices = nullptr; float* vertices = nullptr;
float* normals = nullptr; float* normals = nullptr;
float *uvs = nullptr;
uint32_t numVertices = 0; uint32_t numVertices = 0;
uint16_t* indices = 0; uint16_t* indices = 0;
uint32_t numIndices = 0; uint32_t numIndices = 0;

View File

@@ -138,7 +138,7 @@ namespace thermion_filament
void setRecording(bool recording); void setRecording(bool recording);
void setRecordingOutputDirectory(const char *path); void setRecordingOutputDirectory(const char *path);
void capture(uint8_t *out, void (*onComplete)()); void capture(uint8_t *out, bool useFence, void (*onComplete)());
void setAntiAliasing(bool msaaEnabled, bool fxaaEnabled, bool taaEnabled); void setAntiAliasing(bool msaaEnabled, bool fxaaEnabled, bool taaEnabled);
void setDepthOfField(); void setDepthOfField();
@@ -151,6 +151,8 @@ namespace thermion_filament
return (SceneManager *const)_sceneManager; return (SceneManager *const)_sceneManager;
} }
void unprojectTexture(EntityId entity, uint8_t* out, uint32_t outWidth, uint32_t outHeight);
private: private:
const ResourceLoaderWrapperImpl *const _resourceLoaderWrapper; const ResourceLoaderWrapperImpl *const _resourceLoaderWrapper;
void* _context = nullptr; void* _context = nullptr;

View File

@@ -4,6 +4,7 @@
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <map> #include <map>
#include <set>
#include <filament/Scene.h> #include <filament/Scene.h>
#include <filament/Camera.h> #include <filament/Camera.h>
@@ -168,7 +169,11 @@ namespace thermion_filament
void playAnimation(EntityId e, int index, bool loop, bool reverse, bool replaceActive, float crossfade = 0.3f, float startOffset = 0.0f); void playAnimation(EntityId e, int index, bool loop, bool reverse, bool replaceActive, float crossfade = 0.3f, float startOffset = 0.0f);
void stopAnimation(EntityId e, int index); void stopAnimation(EntityId e, int index);
void setMorphTargetWeights(const char *const entityName, float *weights, int count); void setMorphTargetWeights(const char *const entityName, float *weights, int count);
void loadTexture(EntityId entity, const char *resourcePath, int renderableIndex);
Texture* createTexture(const uint8_t* data, size_t length, const char* name);
bool applyTexture(EntityId entityId, Texture *texture, const char* slotName, int materialIndex);
void destroyTexture(Texture* texture);
void setAnimationFrame(EntityId entity, int animationIndex, int animationFrame); void setAnimationFrame(EntityId entity, int animationIndex, int animationFrame);
bool hide(EntityId entity, const char *meshName); bool hide(EntityId entity, const char *meshName);
bool reveal(EntityId entity, const char *meshName); bool reveal(EntityId entity, const char *meshName);
@@ -226,29 +231,16 @@ namespace thermion_filament
void setLayerEnabled(int layer, bool enabled); void setLayerEnabled(int layer, bool enabled);
/// ///
/// Creates an entity with the specified geometry/material and adds to the scene. /// Creates an entity with the specified geometry/material/normals and adds to the scene.
/// If [keepData] is true, stores /// If [keepData] is true, stores
/// ///
EntityId createGeometry( EntityId createGeometry(
float *vertices,
uint32_t numVertices,
uint16_t *indices,
uint32_t numIndices,
filament::RenderableManager::PrimitiveType primitiveType = RenderableManager::PrimitiveType::TRIANGLES,
const char *materialPath = nullptr,
bool keepData = false
);
///
/// Creates an entity with the specified geometry/material/normals and adds to the scene.
/// If [keepData] is true, stores
///
EntityId createGeometryWithNormals(
float *vertices, float *vertices,
uint32_t numVertices, uint32_t numVertices,
float *normals, float *normals,
uint32_t numNormals, uint32_t numNormals,
float *uvs,
uint32_t numUvs,
uint16_t *indices, uint16_t *indices,
uint32_t numIndices, uint32_t numIndices,
filament::RenderableManager::PrimitiveType primitiveType = RenderableManager::PrimitiveType::TRIANGLES, filament::RenderableManager::PrimitiveType primitiveType = RenderableManager::PrimitiveType::TRIANGLES,
@@ -313,9 +305,9 @@ namespace thermion_filament
_instances; _instances;
tsl::robin_map<EntityId, gltfio::FilamentAsset *> _assets; tsl::robin_map<EntityId, gltfio::FilamentAsset *> _assets;
tsl::robin_map<EntityId, unique_ptr<CustomGeometry>> _geometry; tsl::robin_map<EntityId, unique_ptr<CustomGeometry>> _geometry;
tsl::robin_map<EntityId, unique_ptr<HighlightOverlay>> _highlighted; tsl::robin_map<EntityId, unique_ptr<HighlightOverlay>> _highlighted;
tsl::robin_map<EntityId, std::tuple<math::float3, bool, math::quatf, bool, float>> _transformUpdates; tsl::robin_map<EntityId, std::tuple<math::float3, bool, math::quatf, bool, float>> _transformUpdates;
std::set<Texture*> _textures;
AnimationComponentManager *_animationComponentManager = nullptr; AnimationComponentManager *_animationComponentManager = nullptr;
CollisionComponentManager *_collisionComponentManager = nullptr; CollisionComponentManager *_collisionComponentManager = nullptr;

View File

@@ -262,8 +262,7 @@ extern "C"
EMSCRIPTEN_KEEPALIVE bool add_animation_component(void *const sceneManager, EntityId entityId); EMSCRIPTEN_KEEPALIVE bool add_animation_component(void *const sceneManager, EntityId entityId);
EMSCRIPTEN_KEEPALIVE void remove_animation_component(void *const sceneManager, EntityId entityId); EMSCRIPTEN_KEEPALIVE void remove_animation_component(void *const sceneManager, EntityId entityId);
EMSCRIPTEN_KEEPALIVE EntityId create_geometry(void *const sceneManager, float *vertices, int numVertices, uint16_t *indices, int numIndices, int primitiveType, const char *materialPath); EMSCRIPTEN_KEEPALIVE EntityId create_geometry(void *const sceneManager, float *vertices, int numVertices, float *normals, int numNormals, float *uvs, int numUvs, uint16_t *indices, int numIndices, int primitiveType, const char *materialPath);
EMSCRIPTEN_KEEPALIVE EntityId create_geometry_with_normals(void *const sceneManager, float *vertices, int numVertices, float *normals, int numNormals, uint16_t *indices, int numIndices, int primitiveType, const char *materialPath);
EMSCRIPTEN_KEEPALIVE EntityId get_parent(void *const sceneManager, EntityId child); EMSCRIPTEN_KEEPALIVE EntityId get_parent(void *const sceneManager, EntityId child);
EMSCRIPTEN_KEEPALIVE EntityId get_ancestor(void *const sceneManager, EntityId child); EMSCRIPTEN_KEEPALIVE EntityId get_ancestor(void *const sceneManager, EntityId child);
EMSCRIPTEN_KEEPALIVE void set_parent(void *const sceneManager, EntityId child, EntityId parent, bool preserveScaling); EMSCRIPTEN_KEEPALIVE void set_parent(void *const sceneManager, EntityId child, EntityId parent, bool preserveScaling);
@@ -279,6 +278,10 @@ extern "C"
EMSCRIPTEN_KEEPALIVE void remove_stencil_highlight(void *const sceneManager, EntityId entity); EMSCRIPTEN_KEEPALIVE void remove_stencil_highlight(void *const sceneManager, EntityId entity);
EMSCRIPTEN_KEEPALIVE void set_material_property_float(void *const sceneManager, EntityId entity, int materialIndex, const char* property, float value); EMSCRIPTEN_KEEPALIVE void set_material_property_float(void *const sceneManager, EntityId entity, int materialIndex, const char* property, float value);
EMSCRIPTEN_KEEPALIVE void set_material_property_float4(void *const sceneManager, EntityId entity, int materialIndex, const char* property, float4 value); EMSCRIPTEN_KEEPALIVE void set_material_property_float4(void *const sceneManager, EntityId entity, int materialIndex, const char* property, float4 value);
EMSCRIPTEN_KEEPALIVE void unproject_texture(void *const sceneManager, EntityId entity, uint8_t* out, uint32_t outWidth, uint32_t outHeight);
EMSCRIPTEN_KEEPALIVE void* const create_texture(void *const sceneManager, uint8_t* data, size_t length);
EMSCRIPTEN_KEEPALIVE void destroy_texture(void *const sceneManager, void* const texture);
EMSCRIPTEN_KEEPALIVE void apply_texture_to_material(void *const sceneManager, EntityId entity, void* const texture, const char* parameterName, int materialIndex);
#ifdef __cplusplus #ifdef __cplusplus

View File

@@ -101,8 +101,9 @@ extern "C"
void (*callback)(bool)); void (*callback)(bool));
EMSCRIPTEN_KEEPALIVE void set_post_processing_ffi(void *const viewer, bool enabled); EMSCRIPTEN_KEEPALIVE void set_post_processing_ffi(void *const viewer, bool enabled);
EMSCRIPTEN_KEEPALIVE void reset_to_rest_pose_ffi(void *const sceneManager, EntityId entityId, void(*callback)()); EMSCRIPTEN_KEEPALIVE void reset_to_rest_pose_ffi(void *const sceneManager, EntityId entityId, void(*callback)());
EMSCRIPTEN_KEEPALIVE void create_geometry_ffi(void *const sceneManager, float *vertices, int numVertices, uint16_t *indices, int numIndices, int primitiveType, const char *materialPath, bool keepData, void (*callback)(EntityId)); EMSCRIPTEN_KEEPALIVE void create_geometry_ffi(void *const sceneManager, float *vertices, int numVertices, float *normals, int numNormals, float *uvs, int numUvs, uint16_t *indices, int numIndices, int primitiveType, const char *materialPath, bool keepData, void (*callback)(EntityId));
EMSCRIPTEN_KEEPALIVE void create_geometry_with_normals_ffi(void *const sceneManager, float *vertices, int numVertices, float *normals, int numNormals, uint16_t *indices, int numIndices, int primitiveType, const char *materialPath, bool keepData, void (*callback)(EntityId)); EMSCRIPTEN_KEEPALIVE void unproject_texture_ffi(void *const sceneManager, EntityId entity, uint8_t* out, uint32_t outWidth, uint32_t outHeight, void(*callback)());
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -0,0 +1,39 @@
#include <filament/Engine.h>
#include <filament/Camera.h>
#include <filament/Texture.h>
#include <filament/VertexBuffer.h>
#include <filament/IndexBuffer.h>
#include <filament/RenderableManager.h>
#include <filament/TransformManager.h>
#include <math/mat4.h>
#include <math/vec2.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <utils/EntityManager.h>
#include <backend/PixelBufferDescriptor.h>
#include <vector>
#include <algorithm>
#include "CustomGeometry.hpp"
namespace thermion_filament {
class UnprojectTexture {
public:
UnprojectTexture(const CustomGeometry * geometry, Camera& camera, Engine* engine)
: _geometry(geometry), _camera(camera), _engine(engine) {}
void unproject(utils::Entity entity, const uint8_t* inputTexture, uint8_t* outputTexture, uint32_t inputWidth, uint32_t inputHeight,
uint32_t outputWidth, uint32_t outputHeight);
private:
const CustomGeometry * _geometry;
const Camera& _camera;
Engine* _engine;
math::float3 doUnproject(const math::float2& screenPos, float depth, const math::mat4& invViewProj);
bool isInsideTriangle(const math::float2& p, const math::float2& a, const math::float2& b, const math::float2& c);
math::float3 barycentric(const math::float2& p, const math::float2& a, const math::float2& b, const math::float2& c);
};
}

View File

@@ -1,7 +1,5 @@
#include <vector> #include <vector>
#include "math.h" #include "math.h"
#include <filament/Engine.h> #include <filament/Engine.h>
#include <filament/TransformManager.h> #include <filament/TransformManager.h>
#include <filament/Texture.h> #include <filament/Texture.h>
@@ -22,6 +20,8 @@ CustomGeometry::CustomGeometry(
uint32_t numVertices, uint32_t numVertices,
float* normals, float* normals,
uint32_t numNormals, uint32_t numNormals,
float* uvs,
uint32_t numUvs,
uint16_t* indices, uint16_t* indices,
uint32_t numIndices, uint32_t numIndices,
RenderableManager::PrimitiveType primitiveType, RenderableManager::PrimitiveType primitiveType,
@@ -35,6 +35,16 @@ CustomGeometry::CustomGeometry(
Log("numNormals %d", numNormals); Log("numNormals %d", numNormals);
this->normals = new float[numNormals]; this->normals = new float[numNormals];
std::memcpy(this->normals, normals, numNormals * sizeof(float)); std::memcpy(this->normals, normals, numNormals * sizeof(float));
} else {
Log("no normals");
}
if(numUvs > 0) {
Log("numUvs %d", numUvs);
this->uvs = new float[numUvs];
std::memcpy(this->uvs, uvs, numUvs * sizeof(float));
} else {
this->uvs = nullptr;
} }
this->indices = new uint16_t[numIndices]; this->indices = new uint16_t[numIndices];
@@ -76,7 +86,6 @@ VertexBuffer* CustomGeometry::vertexBuffer() const {
triangles.push_back(triangle); triangles.push_back(triangle);
} }
// Create a SurfaceOrientation builder // Create a SurfaceOrientation builder
geometry::SurfaceOrientation::Builder builder; geometry::SurfaceOrientation::Builder builder;
builder.vertexCount(numVertices) builder.vertexCount(numVertices)
@@ -92,8 +101,13 @@ VertexBuffer* CustomGeometry::vertexBuffer() const {
auto quats = new std::vector<filament::math::quatf>(numVertices); auto quats = new std::vector<filament::math::quatf>(numVertices);
orientation->getQuats(quats->data(), numVertices); orientation->getQuats(quats->data(), numVertices);
// Create dummy UV data // Use provided UVs or create dummy UV data
auto dummyUVs = new std::vector<filament::math::float2>(numVertices, filament::math::float2{0.0f, 0.0f}); std::vector<filament::math::float2>* uvData;
if (this->uvs != nullptr) {
uvData = new std::vector<filament::math::float2>((filament::math::float2*)this->uvs, (filament::math::float2*)(this->uvs + numVertices * 2));
} else {
uvData = new std::vector<filament::math::float2>(numVertices, filament::math::float2{0.0f, 0.0f});
}
// Create dummy vertex color data (white color for all vertices) // Create dummy vertex color data (white color for all vertices)
auto dummyColors = new std::vector<filament::math::float4>(numVertices, filament::math::float4{1.0f, 1.0f, 1.0f, 1.0f}); auto dummyColors = new std::vector<filament::math::float4>(numVertices, filament::math::float4{1.0f, 1.0f, 1.0f, 1.0f});
@@ -118,22 +132,21 @@ VertexBuffer* CustomGeometry::vertexBuffer() const {
vertexBuffer->setBufferAt(*_engine, 0, VertexBuffer::BufferDescriptor( vertexBuffer->setBufferAt(*_engine, 0, VertexBuffer::BufferDescriptor(
this->vertices, vertexBuffer->getVertexCount() * sizeof(math::float3), vertexCallback)); this->vertices, vertexBuffer->getVertexCount() * sizeof(math::float3), vertexCallback));
// Set UV0 buffer
// Set UV0 buffer
vertexBuffer->setBufferAt(*_engine, 1, VertexBuffer::BufferDescriptor( vertexBuffer->setBufferAt(*_engine, 1, VertexBuffer::BufferDescriptor(
dummyUVs->data(), dummyUVs->size() * sizeof(math::float2), uvData->data(), uvData->size() * sizeof(math::float2),
[](void* buf, size_t, void* data) { [](void* buf, size_t, void* data) {
delete static_cast<std::vector<math::float2>*>(data); delete static_cast<std::vector<math::float2>*>(data);
}, dummyUVs)); }, uvData));
// Set UV1 buffer // Set UV1 buffer (reusing UV0 data)
vertexBuffer->setBufferAt(*_engine, 2, VertexBuffer::BufferDescriptor( vertexBuffer->setBufferAt(*_engine, 2, VertexBuffer::BufferDescriptor(
dummyUVs->data(), dummyUVs->size() * sizeof(math::float2), uvData->data(), uvData->size() * sizeof(math::float2),
[](void* buf, size_t, void* data) { [](void* buf, size_t, void* data) {
// Do nothing here, as we're reusing the same data as UV0 // Do nothing here, as we're reusing the same data as UV0
}, nullptr)); }, nullptr));
// Set vertex color buffer // Set vertex color buffer
vertexBuffer->setBufferAt(*_engine, 3, VertexBuffer::BufferDescriptor( vertexBuffer->setBufferAt(*_engine, 3, VertexBuffer::BufferDescriptor(
dummyColors->data(), dummyColors->size() * sizeof(math::float4), dummyColors->data(), dummyColors->size() * sizeof(math::float4),
[](void* buf, size_t, void* data) { [](void* buf, size_t, void* data) {
@@ -154,6 +167,8 @@ VertexBuffer* CustomGeometry::vertexBuffer() const {
CustomGeometry::~CustomGeometry() { CustomGeometry::~CustomGeometry() {
delete[] vertices; delete[] vertices;
delete[] indices; delete[] indices;
if (normals) delete[] normals;
if (uvs) delete[] uvs;
} }
void CustomGeometry::computeBoundingBox() { void CustomGeometry::computeBoundingBox() {

View File

@@ -97,7 +97,7 @@
#include "StreamBufferAdapter.hpp" #include "StreamBufferAdapter.hpp"
#include "material/image.h" #include "material/image.h"
#include "TimeIt.hpp" #include "TimeIt.hpp"
#include "ThreadPool.hpp" #include "UnprojectTexture.hpp"
namespace filament namespace filament
{ {
@@ -306,7 +306,7 @@ namespace thermion_filament
bool shadows) bool shadows)
{ {
auto light = EntityManager::get().create(); auto light = EntityManager::get().create();
auto result = LightManager::Builder(t) auto result = LightManager::Builder(t)
.color(Color::cct(colour)) .color(Color::cct(colour))
.intensity(intensity) .intensity(intensity)
@@ -1197,7 +1197,7 @@ namespace thermion_filament
} }
}; };
void FilamentViewer::capture(uint8_t *out, void (*onComplete)()) void FilamentViewer::capture(uint8_t *out, bool useFence, void (*onComplete)())
{ {
Viewport const &vp = _view->getViewport(); Viewport const &vp = _view->getViewport();
@@ -1209,16 +1209,19 @@ namespace thermion_filament
uint8_t *out = (uint8_t *)(frameCallbackData->at(0)); uint8_t *out = (uint8_t *)(frameCallbackData->at(0));
void *callbackPtr = frameCallbackData->at(1); void *callbackPtr = frameCallbackData->at(1);
void (*callback)(void) = (void (*)(void))callbackPtr;
memcpy(out, buf, size); memcpy(out, buf, size);
delete frameCallbackData; delete frameCallbackData;
callback(); if(callbackPtr) {
void (*callback)(void) = (void (*)(void))callbackPtr;
callback();
}
}; };
// Create a fence // Create a fence
#ifndef __EMSCRIPTEN__ Fence* fence = nullptr;
Fence* fence = _engine->createFence(); if(useFence) {
#endif fence = _engine->createFence();
}
auto userData = new std::vector<void *>{out, (void *)onComplete}; auto userData = new std::vector<void *>{out, (void *)onComplete};
@@ -1245,9 +1248,10 @@ namespace thermion_filament
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
_engine->execute(); _engine->execute();
emscripten_webgl_commit_frame(); emscripten_webgl_commit_frame();
#else
Fence::waitAndDestroy(fence);
#endif #endif
if(fence) {
Fence::waitAndDestroy(fence);
}
} }
void FilamentViewer::savePng(void *buf, size_t size, int frameNumber) void FilamentViewer::savePng(void *buf, size_t size, int frameNumber)
@@ -1464,6 +1468,21 @@ namespace thermion_filament
}); });
} }
void FilamentViewer::unprojectTexture(EntityId entityId, uint8_t* out, uint32_t outWidth, uint32_t outHeight) {
const auto * geometry = _sceneManager->getGeometry(entityId);
if(!geometry->uvs) {
Log("No UVS");
return;
}
const auto& viewport = _view->getViewport();
auto viewportCapture = new uint8_t[viewport.width * viewport.height * 4];
capture(viewportCapture, true, nullptr);
UnprojectTexture unproject(geometry, _view->getCamera(), _engine);
unproject.unproject(utils::Entity::import(entityId), viewportCapture, out, viewport.width, viewport.height, outWidth, outHeight);
}
} // namespace thermion_filament } // namespace thermion_filament

View File

@@ -31,6 +31,7 @@
#include "Log.hpp" #include "Log.hpp"
#include "SceneManager.hpp" #include "SceneManager.hpp"
#include "CustomGeometry.hpp" #include "CustomGeometry.hpp"
#include "UnprojectTexture.hpp"
extern "C" extern "C"
{ {
@@ -217,8 +218,9 @@ namespace thermion_filament
FilamentInstance *inst = asset->getInstance(); FilamentInstance *inst = asset->getInstance();
inst->getAnimator()->updateBoneMatrices(); inst->getAnimator()->updateBoneMatrices();
inst->recomputeBoundingBoxes(); inst->recomputeBoundingBoxes();
if(!keepData) { if (!keepData)
{
asset->releaseSourceData(); asset->releaseSourceData();
} }
@@ -292,7 +294,8 @@ namespace thermion_filament
_instances.emplace(instanceEntityId, inst); _instances.emplace(instanceEntityId, inst);
} }
if(!keepData) { if (!keepData)
{
asset->releaseSourceData(); asset->releaseSourceData();
} }
@@ -362,7 +365,8 @@ namespace thermion_filament
const auto asset = pos->second; const auto asset = pos->second;
auto instance = _assetLoader->createInstance(asset); auto instance = _assetLoader->createInstance(asset);
if(!instance) { if (!instance)
{
Log("Failed to create instance"); Log("Failed to create instance");
return 0; return 0;
} }
@@ -497,6 +501,12 @@ namespace thermion_filament
asset.second->getLightEntityCount()); asset.second->getLightEntityCount());
_assetLoader->destroyAsset(asset.second); _assetLoader->destroyAsset(asset.second);
} }
for(auto* texture : _textures) {
_engine->destroy(texture);
}
// TODO - free geometry?
_textures.clear();
_assets.clear(); _assets.clear();
} }
@@ -608,6 +618,7 @@ namespace thermion_filament
auto entity = Entity::import(entityId); auto entity = Entity::import(entityId);
if (_animationComponentManager->hasComponent(entity)) if (_animationComponentManager->hasComponent(entity))
{ {
_animationComponentManager->removeComponent(entity); _animationComponentManager->removeComponent(entity);
@@ -620,6 +631,10 @@ namespace thermion_filament
_scene->remove(entity); _scene->remove(entity);
if(isGeometryEntity(entityId)) {
return;
}
const auto *instance = getInstanceByEntityId(entityId); const auto *instance = getInstanceByEntityId(entityId);
if (instance) if (instance)
@@ -645,7 +660,6 @@ namespace thermion_filament
if (!asset) if (!asset)
{ {
Log("ERROR: could not find FilamentInstance or FilamentAsset associated with the given entity id");
return; return;
} }
_assets.erase(entityId); _assets.erase(entityId);
@@ -1288,78 +1302,100 @@ namespace thermion_filament
animationComponent.gltfAnimations.end()); animationComponent.gltfAnimations.end());
} }
void SceneManager::loadTexture(EntityId entity, const char *resourcePath, int renderableIndex) Texture *SceneManager::createTexture(const uint8_t *data, size_t length, const char *name)
{ {
using namespace filament;
// const auto &pos = _instances.find(entity); // Create an input stream from the data
// if (pos == _instances.end()) std::istringstream stream(std::string(reinterpret_cast<const char *>(data), length));
// {
// Log("ERROR: asset not found for entity.");
// return;
// }
// const auto *instance = pos->second;
// Log("Loading texture at %s for renderableIndex %d", resourcePath, renderableIndex); // Decode the image
image::LinearImage linearImage = image::ImageDecoder::decode(stream, name, image::ImageDecoder::ColorSpace::SRGB);
// string rp(resourcePath); if (!linearImage.isValid())
{
Log("Failed to decode image.");
return nullptr;
}
// if (asset.texture) uint32_t w = linearImage.getWidth();
// { uint32_t h = linearImage.getHeight();
// _engine->destroy(asset.texture); uint32_t channels = linearImage.getChannels();
// asset.texture = nullptr;
// }
// ResourceBuffer imageResource = _resourceLoaderWrapper->load(rp.c_str()); Texture::InternalFormat textureFormat = channels == 3 ? Texture::InternalFormat::RGB16F
: Texture::InternalFormat::RGBA16F;
Texture::Format bufferFormat = channels == 3 ? Texture::Format::RGB
: Texture::Format::RGBA;
// StreamBufferAdapter sb((char *)imageResource.data, (char *)imageResource.data + imageResource.size); Texture *texture = Texture::Builder()
.width(w)
.height(h)
.levels(1)
.format(textureFormat)
.sampler(Texture::Sampler::SAMPLER_2D)
.build(*_engine);
// istream *inputStream = new std::istream(&sb); if (!texture)
{
Log("Failed to create texture: ");
return nullptr;
}
// LinearImage *image = new LinearImage(ImageDecoder::decode( Texture::PixelBufferDescriptor buffer(
// *inputStream, rp.c_str(), ImageDecoder::ColorSpace::SRGB)); linearImage.getPixelRef(),
size_t(w * h * channels * sizeof(float)),
bufferFormat,
Texture::Type::FLOAT);
// if (!image->isValid()) texture->setImage(*_engine, 0, std::move(buffer));
// {
// Log("Invalid image : %s", rp.c_str());
// delete inputStream;
// _resourceLoaderWrapper->free(imageResource);
// return;
// }
// uint32_t channels = image->getChannels(); Log("Created texture: %s (%d x %d, %d channels)", name, w, h, channels);
// uint32_t w = image->getWidth();
// uint32_t h = image->getHeight();
// asset.texture = Texture::Builder()
// .width(w)
// .height(h)
// .levels(0xff)
// .format(channels == 3 ? Texture::InternalFormat::RGB16F
// : Texture::InternalFormat::RGBA16F)
// .sampler(Texture::Sampler::SAMPLER_2D)
// .build(*_engine);
// Texture::PixelBufferDescriptor::Callback freeCallback = [](void *buf, size_t, _textures.insert(texture);
// void *data)
// {
// delete reinterpret_cast<LinearImage *>(data);
// };
// Texture::PixelBufferDescriptor buffer( return texture;
// image->getPixelRef(), size_t(w * h * channels * sizeof(float)), }
// channels == 3 ? Texture::Format::RGB : Texture::Format::RGBA,
// Texture::Type::FLOAT, freeCallback);
// asset.texture->setImage(*_engine, 0, std::move(buffer)); bool SceneManager::applyTexture(EntityId entityId, Texture *texture, const char* parameterName, int materialIndex)
// MaterialInstance *const *inst = instance->getMaterialInstances(); {
// size_t mic = instance->getMaterialInstanceCount(); auto entity = Entity::import(entityId);
// Log("Material instance count : %d", mic);
// auto sampler = TextureSampler(); if (entity.isNull())
// inst[0]->setParameter("baseColorIndex", 0); {
// inst[0]->setParameter("baseColorMap", asset.texture, sampler); Log("Entity %d is null?", entityId);
// delete inputStream; return false;
}
// _resourceLoaderWrapper->free(imageResource); RenderableManager &rm = _engine->getRenderableManager();
auto renderable = rm.getInstance(entity);
if (!renderable.isValid())
{
Log("Renderable not valid, was the entity id correct (%d)?", entityId);
return false;
}
MaterialInstance *mi = rm.getMaterialInstanceAt(renderable, materialIndex);
if (!mi)
{
Log("ERROR: material index must be less than number of material instances");
return false;
}
auto sampler = TextureSampler();
mi->setParameter(parameterName, texture, sampler);
Log("Applied texture to entity %d", entityId);
return true;
}
void SceneManager::destroyTexture(Texture* texture) {
if(_textures.find(texture) == _textures.end()) {
Log("Warning: couldn't find texture");
}
_textures.erase(texture);
_engine->destroy(texture);
} }
void SceneManager::setAnimationFrame(EntityId entityId, int animationIndex, int animationFrame) void SceneManager::setAnimationFrame(EntityId entityId, int animationIndex, int animationFrame)
@@ -1549,10 +1585,12 @@ namespace thermion_filament
const auto child = Entity::import(childEntityId); const auto child = Entity::import(childEntityId);
auto transformInstance = tm.getInstance(child); auto transformInstance = tm.getInstance(child);
Entity parent; Entity parent;
while(true) { while (true)
{
auto newParent = tm.getParent(transformInstance); auto newParent = tm.getParent(transformInstance);
if(newParent.isNull()) { if (newParent.isNull())
{
break; break;
} }
parent = newParent; parent = newParent;
@@ -1571,12 +1609,14 @@ namespace thermion_filament
const auto &parentInstance = tm.getInstance(parent); const auto &parentInstance = tm.getInstance(parent);
const auto &childInstance = tm.getInstance(child); const auto &childInstance = tm.getInstance(child);
if(!parentInstance.isValid()) { if (!parentInstance.isValid())
{
Log("Parent instance is not valid"); Log("Parent instance is not valid");
return; return;
} }
if(!childInstance.isValid()) { if (!childInstance.isValid())
{
Log("Child instance is not valid"); Log("Child instance is not valid");
return; return;
} }
@@ -1843,170 +1883,174 @@ namespace thermion_filament
tm.setTransform(transformInstance, newTransform); tm.setTransform(transformInstance, newTransform);
} }
void SceneManager::queueRelativePositionUpdateFromViewportVector(EntityId entityId, float viewportCoordX, float viewportCoordY) void SceneManager::queueRelativePositionUpdateFromViewportVector(EntityId entityId, float viewportCoordX, float viewportCoordY)
{
// Get the camera and viewport
const auto &camera = _view->getCamera();
const auto &vp = _view->getViewport();
// Convert viewport coordinates to NDC space
float ndcX = (2.0f * viewportCoordX) / vp.width - 1.0f;
float ndcY = 1.0f - (2.0f * viewportCoordY) / vp.height;
// Get the current position of the entity
auto &tm = _engine->getTransformManager();
auto entity = Entity::import(entityId);
auto transformInstance = tm.getInstance(entity);
auto currentTransform = tm.getTransform(transformInstance);
// get entity model origin in camera space
auto entityPositionInCameraSpace = camera.getViewMatrix() * currentTransform * filament::math::float4 { 0.0f, 0.0f, 0.0f, 1.0f };
// get entity model origin in clip space
auto entityPositionInClipSpace = camera.getProjectionMatrix() * entityPositionInCameraSpace;
auto entityPositionInNdcSpace = entityPositionInClipSpace / entityPositionInClipSpace.w;
// Viewport coords in NDC space (use entity position in camera space Z to project onto near plane)
math::float4 ndcNearPlanePos = {ndcX, ndcY, -1.0f, 1.0f};
math::float4 ndcFarPlanePos = {ndcX, ndcY, 0.99f, 1.0f};
math::float4 ndcEntityPlanePos = {ndcX, ndcY, entityPositionInNdcSpace.z, 1.0f};
// Get viewport coords in clip space
math::float4 nearPlaneInClipSpace = Camera::inverseProjection(camera.getProjectionMatrix()) * ndcNearPlanePos;
auto nearPlaneInCameraSpace = nearPlaneInClipSpace / nearPlaneInClipSpace.w;
math::float4 farPlaneInClipSpace = Camera::inverseProjection(camera.getProjectionMatrix()) * ndcFarPlanePos;
auto farPlaneInCameraSpace = farPlaneInClipSpace / farPlaneInClipSpace.w;
math::float4 entityPlaneInClipSpace = Camera::inverseProjection(camera.getProjectionMatrix()) * ndcEntityPlanePos;
auto entityPlaneInCameraSpace = entityPlaneInClipSpace / entityPlaneInClipSpace.w;
auto entityPlaneInWorldSpace = camera.getModelMatrix() * entityPlaneInCameraSpace;
// Queue the position update (as a relative movement)
queuePositionUpdate(entityId, entityPlaneInWorldSpace.x, entityPlaneInWorldSpace.y, entityPlaneInWorldSpace.z, false);
}
void SceneManager::queueRelativePositionUpdateWorldAxis(EntityId entity, float viewportCoordX, float viewportCoordY, float x, float y, float z)
{
auto worldAxis = math::float3{x, y, z};
// Get the camera
const auto &camera = _view->getCamera();
const auto &vp = _view->getViewport();
auto viewMatrix = camera.getViewMatrix();
math::float3 cameraPosition = camera.getPosition();
math::float3 cameraForward = -viewMatrix.upperLeft()[2];
// Scale the viewport movement to NDC coordinates view axis
math::float2 viewportMovementNDC(viewportCoordX / (vp.width / 2), viewportCoordY / (vp.height / 2));
// calculate the translation axis in view space
math::float3 viewSpaceAxis = viewMatrix.upperLeft() * worldAxis;
// Apply projection matrix to get clip space axis
math::float4 clipAxis = camera.getProjectionMatrix() * math::float4(viewSpaceAxis, 0.0f);
// Perform perspective division to get the translation axis in normalized device coordinates (NDC)
math::float2 ndcAxis = (clipAxis.xyz / clipAxis.w).xy;
const float epsilon = 1e-6f;
bool isAligned = false;
if (std::isnan(ndcAxis.x) || std::isnan(ndcAxis.y) || length(ndcAxis) < epsilon || std::abs(dot(normalize(worldAxis), cameraForward)) > 0.99f)
{ {
isAligned = true; // Get the camera and viewport
// Find a suitable perpendicular axis: const auto &camera = _view->getCamera();
math::float3 perpendicularAxis; const auto &vp = _view->getViewport();
if (std::abs(worldAxis.x) < epsilon && std::abs(worldAxis.z) < epsilon)
{
// If worldAxis is (0, y, 0), use (1, 0, 0)
perpendicularAxis = {1.0f, 0.0f, 0.0f};
}
else
{
// Otherwise, calculate a perpendicular vector
perpendicularAxis = normalize(cross(cameraForward, worldAxis));
}
ndcAxis = (camera.getProjectionMatrix() * math::float4(viewMatrix.upperLeft() * perpendicularAxis, 0.0f)).xy; // Convert viewport coordinates to NDC space
float ndcX = (2.0f * viewportCoordX) / vp.width - 1.0f;
float ndcY = 1.0f - (2.0f * viewportCoordY) / vp.height;
if (std::isnan(ndcAxis.x) || std::isnan(ndcAxis.y)) { // Get the current position of the entity
return; auto &tm = _engine->getTransformManager();
} auto entity = Entity::import(entityId);
auto transformInstance = tm.getInstance(entity);
auto currentTransform = tm.getTransform(transformInstance);
// get entity model origin in camera space
auto entityPositionInCameraSpace = camera.getViewMatrix() * currentTransform * filament::math::float4{0.0f, 0.0f, 0.0f, 1.0f};
// get entity model origin in clip space
auto entityPositionInClipSpace = camera.getProjectionMatrix() * entityPositionInCameraSpace;
auto entityPositionInNdcSpace = entityPositionInClipSpace / entityPositionInClipSpace.w;
// Viewport coords in NDC space (use entity position in camera space Z to project onto near plane)
math::float4 ndcNearPlanePos = {ndcX, ndcY, -1.0f, 1.0f};
math::float4 ndcFarPlanePos = {ndcX, ndcY, 0.99f, 1.0f};
math::float4 ndcEntityPlanePos = {ndcX, ndcY, entityPositionInNdcSpace.z, 1.0f};
// Get viewport coords in clip space
math::float4 nearPlaneInClipSpace = Camera::inverseProjection(camera.getProjectionMatrix()) * ndcNearPlanePos;
auto nearPlaneInCameraSpace = nearPlaneInClipSpace / nearPlaneInClipSpace.w;
math::float4 farPlaneInClipSpace = Camera::inverseProjection(camera.getProjectionMatrix()) * ndcFarPlanePos;
auto farPlaneInCameraSpace = farPlaneInClipSpace / farPlaneInClipSpace.w;
math::float4 entityPlaneInClipSpace = Camera::inverseProjection(camera.getProjectionMatrix()) * ndcEntityPlanePos;
auto entityPlaneInCameraSpace = entityPlaneInClipSpace / entityPlaneInClipSpace.w;
auto entityPlaneInWorldSpace = camera.getModelMatrix() * entityPlaneInCameraSpace;
// Queue the position update (as a relative movement)
queuePositionUpdate(entityId, entityPlaneInWorldSpace.x, entityPlaneInWorldSpace.y, entityPlaneInWorldSpace.z, false);
} }
void SceneManager::queueRelativePositionUpdateWorldAxis(EntityId entity, float viewportCoordX, float viewportCoordY, float x, float y, float z)
// project the viewport movement (i.e pointer drag) vector onto the translation axis
// this gives the proportion of the pointer drag vector to translate along the translation axis
float projectedMovement = dot(viewportMovementNDC, normalize(ndcAxis));
auto translationNDC = projectedMovement * normalize(ndcAxis);
float dotProduct = dot(normalize(worldAxis), cameraForward);
// Ensure minimum translation and correct direction
const float minTranslation = 0.01f;
if (isAligned || std::abs(projectedMovement) < minTranslation) {
// Use the dominant component of the viewport movement
float dominantMovement = std::abs(viewportMovementNDC.x) > std::abs(viewportMovementNDC.y) ? viewportMovementNDC.x : viewportMovementNDC.y;
projectedMovement = (std::abs(dominantMovement) < minTranslation) ? minTranslation : std::abs(dominantMovement);
projectedMovement *= (dominantMovement >= 0) ? 1.0f : -1.0f;
translationNDC = projectedMovement * normalize(ndcAxis);
}
// Log("projectedMovement %f dotProduct %f", projectedMovement, dotProduct);
// Get the camera's field of view and aspect ratio
float fovY = camera.getFieldOfViewInDegrees(filament::Camera::Fov::VERTICAL);
float fovX = camera.getFieldOfViewInDegrees(filament::Camera::Fov::HORIZONTAL);
// Convert to radians
fovY = (fovY / 180) * M_PI;
fovX = (fovX / 180) * M_PI;
float aspectRatio = static_cast<float>(vp.width) / vp.height;
auto &transformManager = _engine->getTransformManager();
auto transformInstance = transformManager.getInstance(Entity::import(entity));
const auto &transform = transformManager.getWorldTransform(transformInstance);
math::float3 translation;
math::quatf rotation;
math::float3 scale;
decomposeMatrix(transform, &translation, &rotation, &scale);
const auto entityWorldPosition = transform * math::float4{0.0f, 0.0f, 0.0f, 1.0f};
float distanceToCamera = length(entityWorldPosition.xyz - camera.getPosition());
// Calculate the height of the view frustum at the given distance
float frustumHeight = 2.0f * distanceToCamera * tan(fovY * 0.5f);
// Calculate the width of the view frustum at the given distance
float frustumWidth = frustumHeight * aspectRatio;
// Convert projected viewport movement to world space distance
float worldDistance = length(math::float2{(translationNDC / 2) * math::float2{frustumWidth, frustumHeight}});
// Determine the sign based on the alignment of world axis and camera forward
float sign = (dotProduct >= 0) ? -1.0f : 1.0f;
// If aligned, use the sign of the projected movement instead
if (isAligned) {
sign = (projectedMovement >= 0) ? 1.0f : -1.0f;
} else if (projectedMovement < 0) {
sign *= -1.0f;
}
// Flip the sign for the Z-axis
if (std::abs(z) > 0.001)
{ {
sign *= -1.0f; auto worldAxis = math::float3{x, y, z};
// Get the camera
const auto &camera = _view->getCamera();
const auto &vp = _view->getViewport();
auto viewMatrix = camera.getViewMatrix();
math::float3 cameraPosition = camera.getPosition();
math::float3 cameraForward = -viewMatrix.upperLeft()[2];
// Scale the viewport movement to NDC coordinates view axis
math::float2 viewportMovementNDC(viewportCoordX / (vp.width / 2), viewportCoordY / (vp.height / 2));
// calculate the translation axis in view space
math::float3 viewSpaceAxis = viewMatrix.upperLeft() * worldAxis;
// Apply projection matrix to get clip space axis
math::float4 clipAxis = camera.getProjectionMatrix() * math::float4(viewSpaceAxis, 0.0f);
// Perform perspective division to get the translation axis in normalized device coordinates (NDC)
math::float2 ndcAxis = (clipAxis.xyz / clipAxis.w).xy;
const float epsilon = 1e-6f;
bool isAligned = false;
if (std::isnan(ndcAxis.x) || std::isnan(ndcAxis.y) || length(ndcAxis) < epsilon || std::abs(dot(normalize(worldAxis), cameraForward)) > 0.99f)
{
isAligned = true;
// Find a suitable perpendicular axis:
math::float3 perpendicularAxis;
if (std::abs(worldAxis.x) < epsilon && std::abs(worldAxis.z) < epsilon)
{
// If worldAxis is (0, y, 0), use (1, 0, 0)
perpendicularAxis = {1.0f, 0.0f, 0.0f};
}
else
{
// Otherwise, calculate a perpendicular vector
perpendicularAxis = normalize(cross(cameraForward, worldAxis));
}
ndcAxis = (camera.getProjectionMatrix() * math::float4(viewMatrix.upperLeft() * perpendicularAxis, 0.0f)).xy;
if (std::isnan(ndcAxis.x) || std::isnan(ndcAxis.y))
{
return;
}
}
// project the viewport movement (i.e pointer drag) vector onto the translation axis
// this gives the proportion of the pointer drag vector to translate along the translation axis
float projectedMovement = dot(viewportMovementNDC, normalize(ndcAxis));
auto translationNDC = projectedMovement * normalize(ndcAxis);
float dotProduct = dot(normalize(worldAxis), cameraForward);
// Ensure minimum translation and correct direction
const float minTranslation = 0.01f;
if (isAligned || std::abs(projectedMovement) < minTranslation)
{
// Use the dominant component of the viewport movement
float dominantMovement = std::abs(viewportMovementNDC.x) > std::abs(viewportMovementNDC.y) ? viewportMovementNDC.x : viewportMovementNDC.y;
projectedMovement = (std::abs(dominantMovement) < minTranslation) ? minTranslation : std::abs(dominantMovement);
projectedMovement *= (dominantMovement >= 0) ? 1.0f : -1.0f;
translationNDC = projectedMovement * normalize(ndcAxis);
}
// Log("projectedMovement %f dotProduct %f", projectedMovement, dotProduct);
// Get the camera's field of view and aspect ratio
float fovY = camera.getFieldOfViewInDegrees(filament::Camera::Fov::VERTICAL);
float fovX = camera.getFieldOfViewInDegrees(filament::Camera::Fov::HORIZONTAL);
// Convert to radians
fovY = (fovY / 180) * M_PI;
fovX = (fovX / 180) * M_PI;
float aspectRatio = static_cast<float>(vp.width) / vp.height;
auto &transformManager = _engine->getTransformManager();
auto transformInstance = transformManager.getInstance(Entity::import(entity));
const auto &transform = transformManager.getWorldTransform(transformInstance);
math::float3 translation;
math::quatf rotation;
math::float3 scale;
decomposeMatrix(transform, &translation, &rotation, &scale);
const auto entityWorldPosition = transform * math::float4{0.0f, 0.0f, 0.0f, 1.0f};
float distanceToCamera = length(entityWorldPosition.xyz - camera.getPosition());
// Calculate the height of the view frustum at the given distance
float frustumHeight = 2.0f * distanceToCamera * tan(fovY * 0.5f);
// Calculate the width of the view frustum at the given distance
float frustumWidth = frustumHeight * aspectRatio;
// Convert projected viewport movement to world space distance
float worldDistance = length(math::float2{(translationNDC / 2) * math::float2{frustumWidth, frustumHeight}});
// Determine the sign based on the alignment of world axis and camera forward
float sign = (dotProduct >= 0) ? -1.0f : 1.0f;
// If aligned, use the sign of the projected movement instead
if (isAligned)
{
sign = (projectedMovement >= 0) ? 1.0f : -1.0f;
}
else if (projectedMovement < 0)
{
sign *= -1.0f;
}
// Flip the sign for the Z-axis
if (std::abs(z) > 0.001)
{
sign *= -1.0f;
}
worldDistance *= sign;
auto newWorldTranslation = worldAxis * worldDistance;
queuePositionUpdate(entity, newWorldTranslation.x, newWorldTranslation.y, newWorldTranslation.z, true);
} }
worldDistance *= sign;
auto newWorldTranslation = worldAxis * worldDistance;
queuePositionUpdate(entity, newWorldTranslation.x, newWorldTranslation.y, newWorldTranslation.z, true);
}
void SceneManager::queuePositionUpdate(EntityId entity, float x, float y, float z, bool relative) void SceneManager::queuePositionUpdate(EntityId entity, float x, float y, float z, bool relative)
{ {
std::lock_guard lock(_mutex); std::lock_guard lock(_mutex);
@@ -2343,10 +2387,12 @@ void SceneManager::queueRelativePositionUpdateWorldAxis(EntityId entity, float v
_view->setLayerEnabled(layer, enabled); _view->setLayerEnabled(layer, enabled);
} }
void SceneManager::removeStencilHighlight(EntityId entityId) { void SceneManager::removeStencilHighlight(EntityId entityId)
{
std::lock_guard lock(_stencilMutex); std::lock_guard lock(_stencilMutex);
auto found = _highlighted.find(entityId); auto found = _highlighted.find(entityId);
if(found == _highlighted.end()) { if (found == _highlighted.end())
{
Log("Entity %d has no stencil highlight, skipping removal", entityId); Log("Entity %d has no stencil highlight, skipping removal", entityId);
return; return;
} }
@@ -2355,147 +2401,164 @@ void SceneManager::queueRelativePositionUpdateWorldAxis(EntityId entity, float v
_highlighted.erase(entityId); _highlighted.erase(entityId);
} }
void SceneManager::setStencilHighlight(EntityId entityId, float r, float g, float b) { void SceneManager::setStencilHighlight(EntityId entityId, float r, float g, float b)
{
std::lock_guard lock(_stencilMutex); std::lock_guard lock(_stencilMutex);
auto highlightEntity = std::make_unique<HighlightOverlay>(entityId, this, _engine, r, g, b); auto highlightEntity = std::make_unique<HighlightOverlay>(entityId, this, _engine, r, g, b);
if(highlightEntity->isValid()) { if (highlightEntity->isValid())
{
_highlighted.emplace(entityId, std::move(highlightEntity)); _highlighted.emplace(entityId, std::move(highlightEntity));
} }
} }
/// EntityId SceneManager::createGeometry(
/// Creates an entity with the specified geometry/material/normals and adds to the scene. float *vertices,
/// If [keepData] is true, stores uint32_t numVertices,
/// float *normals,
EntityId SceneManager::createGeometryWithNormals( uint32_t numNormals,
float *vertices, float *uvs,
uint32_t numVertices, uint32_t numUvs,
float *normals, uint16_t *indices,
uint32_t numNormals, uint32_t numIndices,
uint16_t *indices, filament::RenderableManager::PrimitiveType primitiveType,
uint32_t numIndices, const char *materialPath,
filament::RenderableManager::PrimitiveType primitiveType, bool keepData)
const char *materialPath, {
bool keepData auto geometry = std::make_unique<CustomGeometry>(vertices, numVertices, normals, numNormals, uvs, numUvs, indices, numIndices, primitiveType, _engine);
) {
auto geometry = std::make_unique<CustomGeometry>(vertices, numVertices, normals, numNormals, indices, numIndices, primitiveType, _engine);
auto renderable = utils::EntityManager::get().create(); auto entity = utils::EntityManager::get().create();
RenderableManager::Builder builder(1); RenderableManager::Builder builder(1);
builder.boundingBox(geometry->getBoundingBox()) builder.boundingBox(geometry->getBoundingBox())
.geometry(0, primitiveType, geometry->vertexBuffer(), geometry->indexBuffer(), 0, numIndices) .geometry(0, primitiveType, geometry->vertexBuffer(), geometry->indexBuffer(), 0, numIndices)
.culling(true) .culling(true)
.receiveShadows(true) .receiveShadows(true)
.castShadows(true); .castShadows(true);
if (materialPath) { filament::Material *mat = nullptr;
filament::Material* mat = nullptr; filament::MaterialInstance* materialInstance = nullptr;
auto matData = _resourceLoaderWrapper->load(materialPath);
mat = Material::Builder().package(matData.data, matData.size).build(*_engine);
_resourceLoaderWrapper->free(matData);
builder.material(0, mat->getDefaultInstance());
} else {
filament::gltfio::MaterialKey config;
config.unlit = false;
config.doubleSided = false;
config.useSpecularGlossiness = false;
config.alphaMode = filament::gltfio::AlphaMode::OPAQUE;
config.hasBaseColorTexture = false;
config.hasClearCoat = false;
config.hasClearCoatNormalTexture = false;
config.hasClearCoatRoughnessTexture = false;
config.hasEmissiveTexture = false;
config.hasIOR = false;
config.hasMetallicRoughnessTexture = false;
config.hasNormalTexture = false;
config.hasOcclusionTexture = false;
config.hasSheen = false;
config.hasSheenColorTexture = false;
config.hasSheenRoughnessTexture = false;
config.hasSpecularGlossinessTexture = false;
config.hasTextureTransforms = false;
config.hasTransmission = false;
config.hasTransmissionTexture = false;
config.hasVolume = false;
config.hasVolumeThicknessTexture = false;
config.hasVertexColors = false; if (materialPath)
config.hasVolume = false; {
// config.enableDiagnostics = true; auto matData = _resourceLoaderWrapper->load(materialPath);
mat = Material::Builder().package(matData.data, matData.size).build(*_engine);
filament::gltfio::UvMap uvmap; _resourceLoaderWrapper->free(matData);
auto materialInstance = _ubershaderProvider->createMaterialInstance(&config, &uvmap); materialInstance = mat->getDefaultInstance();
materialInstance->setParameter("baseColorFactor", RgbaType::sRGB, filament::math::float4 { 1.0f, 0.0f, 0.0f, 1.0f }); }
// auto materialInstance = _unlitMaterialProvider->createMaterialInstance(&config, &uvmap); else
builder.material(0, materialInstance); {
filament::gltfio::MaterialKey config;
} config.unlit = false;
config.doubleSided = false;
config.useSpecularGlossiness = false;
config.alphaMode = filament::gltfio::AlphaMode::OPAQUE;
config.hasBaseColorTexture = uvs != nullptr;
config.hasClearCoat = false;
config.hasClearCoatNormalTexture = false;
config.hasClearCoatRoughnessTexture = false;
config.hasEmissiveTexture = false;
config.hasIOR = false;
config.hasMetallicRoughnessTexture = false;
config.hasNormalTexture = false;
config.hasOcclusionTexture = false;
config.hasSheen = false;
config.hasSheenColorTexture = false;
config.hasSheenRoughnessTexture = false;
config.hasSpecularGlossinessTexture = false;
config.hasTextureTransforms = false;
config.hasTransmission = false;
config.hasTransmissionTexture = false;
config.hasVolume = false;
config.hasVolumeThicknessTexture = false;
config.baseColorUV = 0;
config.hasVertexColors = false;
config.hasVolume = false;
builder.build(*_engine, renderable); filament::gltfio::UvMap uvmap;
materialInstance = _ubershaderProvider->createMaterialInstance(&config, &uvmap);
_scene->addEntity(renderable); materialInstance->setParameter("baseColorFactor", RgbaType::sRGB, filament::math::float4{1.0f, 0.0f, 1.0f, 1.0f});
auto entityId = Entity::smuggle(renderable);
_geometry.emplace(entityId, std::move(geometry));
return entityId;
} }
EntityId SceneManager::createGeometry( // Set up texture and sampler if UVs are available
float *vertices, if (uvs != nullptr && numUvs > 0)
uint32_t numVertices, {
uint16_t *indices, // Create a default white texture
uint32_t numIndices, static constexpr uint32_t textureSize = 1;
RenderableManager::PrimitiveType primitiveType, static constexpr uint32_t white = 0x00ffffff;
const char *materialPath, Texture* texture = Texture::Builder()
bool keepData .width(textureSize)
) { .height(textureSize)
return createGeometryWithNormals(vertices, numVertices, nullptr, 0, indices, numIndices, primitiveType, materialPath, keepData); .levels(1)
} .format(Texture::InternalFormat::RGBA8)
.build(*_engine);
void SceneManager::setMaterialProperty(EntityId entityId, int materialIndex, const char* property, float value) { _textures.insert(texture);
auto entity = Entity::import(entityId);
const auto& rm = _engine->getRenderableManager(); filament::backend::PixelBufferDescriptor pbd(&white, 4, Texture::Format::RGBA, Texture::Type::UBYTE);
auto renderableInstance = rm.getInstance(entity); texture->setImage(*_engine, 0, std::move(pbd));
if(!renderableInstance.isValid()) {
Log("ERROR");
return;
}
auto materialInstance = rm.getMaterialInstanceAt(renderableInstance, materialIndex);
if(!materialInstance->getMaterial()->hasParameter(property)) {
Log("Parameter %s not found", property);
return;
}
materialInstance->setParameter(property, value);
}
void SceneManager::setMaterialProperty(EntityId entityId, int materialIndex, const char* property, filament::math::float4 value) { // Create a sampler
auto entity = Entity::import(entityId); TextureSampler sampler(TextureSampler::MinFilter::LINEAR, TextureSampler::MagFilter::LINEAR);
const auto& rm = _engine->getRenderableManager();
auto renderableInstance = rm.getInstance(entity); // Set the texture and sampler to the material instance
if(!renderableInstance.isValid()) { materialInstance->setParameter("baseColorMap", texture, sampler);
Log("ERROR");
return;
} }
auto materialInstance = rm.getMaterialInstanceAt(renderableInstance, materialIndex);
builder.material(0, materialInstance);
if(!materialInstance->getMaterial()->hasParameter(property)) { builder.build(*_engine, entity);
Log("Parameter %s not found", property);
return; _scene->addEntity(entity);
auto entityId = Entity::smuggle(entity);
_geometry.emplace(entityId, std::move(geometry));
return entityId;
}
void SceneManager::setMaterialProperty(EntityId entityId, int materialIndex, const char *property, float value)
{
auto entity = Entity::import(entityId);
const auto &rm = _engine->getRenderableManager();
auto renderableInstance = rm.getInstance(entity);
if (!renderableInstance.isValid())
{
Log("ERROR");
return;
}
auto materialInstance = rm.getMaterialInstanceAt(renderableInstance, materialIndex);
if (!materialInstance->getMaterial()->hasParameter(property))
{
Log("Parameter %s not found", property);
return;
}
materialInstance->setParameter(property, value);
}
void SceneManager::setMaterialProperty(EntityId entityId, int materialIndex, const char *property, filament::math::float4 value)
{
auto entity = Entity::import(entityId);
const auto &rm = _engine->getRenderableManager();
auto renderableInstance = rm.getInstance(entity);
if (!renderableInstance.isValid())
{
Log("ERROR");
return;
}
auto materialInstance = rm.getMaterialInstanceAt(renderableInstance, materialIndex);
if (!materialInstance->getMaterial()->hasParameter(property))
{
Log("Parameter %s not found", property);
return;
}
materialInstance->setParameter(property, value);
} }
materialInstance->setParameter(property, value);
}
} // namespace thermion_filament } // namespace thermion_filament

View File

@@ -352,7 +352,12 @@ extern "C"
uint8_t *pixelBuffer, uint8_t *pixelBuffer,
void (*callback)(void)) void (*callback)(void))
{ {
((FilamentViewer *)viewer)->capture(pixelBuffer, callback); #ifdef __EMSCRIPTEN__
bool useFence = true;
#else
bool useFence = false;
#endif
((FilamentViewer *)viewer)->capture(pixelBuffer, useFence, callback);
}; };
EMSCRIPTEN_KEEPALIVE void set_frame_interval( EMSCRIPTEN_KEEPALIVE void set_frame_interval(
@@ -850,14 +855,10 @@ extern "C"
((SceneManager *)sceneManager)->removeAnimationComponent(entityId); ((SceneManager *)sceneManager)->removeAnimationComponent(entityId);
} }
EMSCRIPTEN_KEEPALIVE EntityId create_geometry(void *const sceneManager, float *vertices, int numVertices, uint16_t *indices, int numIndices, int primitiveType, const char *materialPath)
{
return ((SceneManager *)sceneManager)->createGeometry(vertices, (uint32_t)numVertices, indices, numIndices, (filament::RenderableManager::PrimitiveType)primitiveType, materialPath);
}
EMSCRIPTEN_KEEPALIVE EntityId create_geometry_with_normals(void *const sceneManager, float *vertices, int numVertices, float *normals, int numNormals, uint16_t *indices, int numIndices, int primitiveType, const char *materialPath) EMSCRIPTEN_KEEPALIVE EntityId create_geometry(void *const sceneManager, float *vertices, int numVertices, float *normals, int numNormals, float *uvs, int numUvs, uint16_t *indices, int numIndices, int primitiveType, const char *materialPath)
{ {
return ((SceneManager *)sceneManager)->createGeometryWithNormals(vertices, (uint32_t)numVertices, normals, (uint32_t)numNormals, indices, numIndices, (filament::RenderableManager::PrimitiveType)primitiveType, materialPath); return ((SceneManager *)sceneManager)->createGeometry(vertices, (uint32_t)numVertices, normals, (uint32_t)numNormals, uvs, numUvs, indices, numIndices, (filament::RenderableManager::PrimitiveType)primitiveType, materialPath);
} }
EMSCRIPTEN_KEEPALIVE EntityId find_child_entity_by_name(void *const sceneManager, const EntityId parent, const char *name) EMSCRIPTEN_KEEPALIVE EntityId find_child_entity_by_name(void *const sceneManager, const EntityId parent, const char *name)
@@ -950,9 +951,24 @@ extern "C"
EMSCRIPTEN_KEEPALIVE void set_material_property_float4(void *const sceneManager, EntityId entity, int materialIndex, const char* property, float4 value) { EMSCRIPTEN_KEEPALIVE void set_material_property_float4(void *const sceneManager, EntityId entity, int materialIndex, const char* property, float4 value) {
filament::math::float4 filamentValue { value.x, value.y, value.z, value.w }; filament::math::float4 filamentValue { value.x, value.y, value.z, value.w };
((SceneManager *)sceneManager)->setMaterialProperty(entity, materialIndex, property, filamentValue); ((SceneManager *)sceneManager)->setMaterialProperty(entity, materialIndex, property, filamentValue);
} }
EMSCRIPTEN_KEEPALIVE void unproject_texture(void *const viewer, EntityId entity, uint8_t* out, uint32_t outWidth, uint32_t outHeight) {
((FilamentViewer *)viewer)->unprojectTexture(entity, out, outWidth, outHeight);
}
EMSCRIPTEN_KEEPALIVE void* const create_texture(void *const sceneManager, uint8_t* data, size_t length) {
return (void* const) ((SceneManager *)sceneManager)->createTexture(data, length, "SOMETEXTURE");
}
EMSCRIPTEN_KEEPALIVE void apply_texture_to_material(void *const sceneManager, EntityId entity, void* const texture, const char* parameterName, int materialIndex) {
((SceneManager*)sceneManager)->applyTexture(entity, reinterpret_cast<Texture*>(texture), parameterName, materialIndex);
}
EMSCRIPTEN_KEEPALIVE void destroy_texture(void *const sceneManager, void* const texture) {
((SceneManager*)sceneManager)->destroyTexture(reinterpret_cast<Texture*>(texture));
}
} }

View File

@@ -78,32 +78,24 @@ public:
} }
} }
void iter() { void iter() {
const auto frameStart = std::chrono::steady_clock::now();
auto frameStart = std::chrono::high_resolution_clock::now();
if (_rendering) { if (_rendering) {
doRender(); doRender();
} }
auto now = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(now - frameStart).count();
std::function<void()> task;
std::unique_lock<std::mutex> lock(_access); std::unique_lock<std::mutex> lock(_access);
while(true) { const auto frameEnd = frameStart + std::chrono::microseconds(_frameIntervalInMicroseconds);
now = std::chrono::high_resolution_clock::now();
elapsed = std::chrono::duration_cast<std::chrono::microseconds>(now - frameStart).count(); while (std::chrono::steady_clock::now() < frameEnd) {
if(elapsed >= _frameIntervalInMicroseconds) { if (!_tasks.empty()) {
break; auto task = std::move(_tasks.front());
} _tasks.pop_front();
if(!_tasks.empty()) { lock.unlock();
task = std::move(_tasks.front()); task();
_tasks.pop_front(); lock.lock();
task(); } else {
} else { _cond.wait_until(lock, frameEnd);
_cond.wait_for(lock, std::chrono::duration<float, std::milli>(1));
} }
} }
} }
@@ -841,6 +833,10 @@ extern "C"
void *const sceneManager, void *const sceneManager,
float *vertices, float *vertices,
int numVertices, int numVertices,
float *normals,
int numNormals,
float *uvs,
int numUvs,
uint16_t *indices, uint16_t *indices,
int numIndices, int numIndices,
int primitiveType, int primitiveType,
@@ -851,7 +847,7 @@ extern "C"
std::packaged_task<EntityId()> lambda( std::packaged_task<EntityId()> lambda(
[=] [=]
{ {
auto entity = create_geometry(sceneManager, vertices, numVertices, indices, numIndices, primitiveType, materialPath); auto entity = create_geometry(sceneManager, vertices, numVertices, normals, numNormals, uvs, numUvs, indices, numIndices, primitiveType, materialPath);
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
MAIN_THREAD_EM_ASM({ MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0,$1); moduleArg.dartFilamentResolveCallback($0,$1);
@@ -864,31 +860,12 @@ extern "C"
auto fut = _rl->add_task(lambda); auto fut = _rl->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void create_geometry_with_normals_ffi( EMSCRIPTEN_KEEPALIVE void unproject_texture_ffi(void *const viewer, EntityId entity, uint8_t* out, uint32_t outWidth, uint32_t outHeight, void(*callback)()) {
void *const sceneManager, std::packaged_task<void()> lambda(
float *vertices,
int numVertices,
float *normals,
int numNormals,
uint16_t *indices,
int numIndices,
int primitiveType,
const char *materialPath,
bool keepData,
void (*callback)(EntityId))
{
std::packaged_task<EntityId()> lambda(
[=] [=]
{ {
auto entity = create_geometry_with_normals(sceneManager, vertices, numVertices, normals, numNormals, indices, numIndices, primitiveType, materialPath); unproject_texture(viewer, entity, out, outWidth, outHeight);
#ifdef __EMSCRIPTEN__ callback();
MAIN_THREAD_EM_ASM({
moduleArg.dartFilamentResolveCallback($0,$1);
}, callback, entity);
#else
callback(entity);
#endif
return entity;
}); });
auto fut = _rl->add_task(lambda); auto fut = _rl->add_task(lambda);
} }

View File

@@ -0,0 +1,130 @@
#include <filament/Engine.h>
#include <filament/Camera.h>
#include <filament/Texture.h>
#include <filament/VertexBuffer.h>
#include <filament/IndexBuffer.h>
#include <filament/RenderableManager.h>
#include <filament/TransformManager.h>
#include <math/mat4.h>
#include <math/vec2.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <utils/EntityManager.h>
#include <backend/PixelBufferDescriptor.h>
#include "Log.hpp"
#include <vector>
#include <algorithm>
#include <iostream>
#include "CustomGeometry.hpp"
#include "UnprojectTexture.hpp"
namespace thermion_filament {
void UnprojectTexture::unproject(utils::Entity entity, const uint8_t* inputTexture, uint8_t* outputTexture, uint32_t inputWidth, uint32_t inputHeight,
uint32_t outputWidth, uint32_t outputHeight) {
auto& rm = _engine->getRenderableManager();
auto& tm = _engine->getTransformManager();
// Get the inverse view-projection matrix
math::mat4 invViewProj = Camera::inverseProjection(_camera.getProjectionMatrix()) * _camera.getModelMatrix();
// Get the world transform of the entity
auto ti = tm.getInstance(entity);
math::mat4f worldTransform = tm.getWorldTransform(ti);
auto inverseWorldTransform = inverse(worldTransform);
// Get vertex, normal, UV, and index data from CustomGeometry
const float* vertices = _geometry->vertices;
const float* uvs = _geometry->uvs;
const uint16_t* indices = _geometry->indices;
uint32_t numIndices = _geometry->numIndices;
// Iterate over each pixel in the output texture
for (uint32_t y = 0; y < outputHeight; ++y) {
for (uint32_t x = 0; x < outputWidth; ++x) {
// Convert output texture coordinates to UV space
math::float2 uv(static_cast<float>(x) / outputWidth, static_cast<float>(y) / outputHeight);
// Use the UV coordinates to get the corresponding 3D position on the renderable
math::float3 objectPos;
math::float2 interpolatedUV;
bool found = false;
// Iterate over triangles to find which one contains this UV coordinate
for (size_t i = 0; i < numIndices; i += 3) {
math::float2 uv0 = *(math::float2*)&uvs[indices[i] * 2];
math::float2 uv1 = *(math::float2*)&uvs[indices[i+1] * 2];
math::float2 uv2 = *(math::float2*)&uvs[indices[i+2] * 2];
if (isInsideTriangle(uv, uv0, uv1, uv2)) {
// Compute barycentric coordinates in UV space
math::float3 bary = barycentric(uv, uv0, uv1, uv2);
// Interpolate 3D position
math::float3 v0(vertices[indices[i] * 3], vertices[indices[i] * 3 + 1], vertices[indices[i] * 3 + 2]);
math::float3 v1(vertices[indices[i+1] * 3], vertices[indices[i+1] * 3 + 1], vertices[indices[i+1] * 3 + 2]);
math::float3 v2(vertices[indices[i+2] * 3], vertices[indices[i+2] * 3 + 1], vertices[indices[i+2] * 3 + 2]);
objectPos = v0 * bary.x + v1 * bary.y + v2 * bary.z;
interpolatedUV = uv;
found = true;
break;
}
}
if (found) {
// Transform the object position to world space
math::float3 worldPos = (worldTransform * math::float4(objectPos, 1.0f)).xyz;
// Project the world position to screen space
math::float4 clipPos = _camera.getProjectionMatrix() * _camera.getViewMatrix() * math::float4(worldPos, 1.0f);
math::float3 ndcPos = clipPos.xyz / clipPos.w;
// Convert NDC to screen coordinates
int sx = static_cast<int>((ndcPos.x * 0.5f + 0.5f) * inputWidth);
int sy = static_cast<int>((1.0f - (ndcPos.y * 0.5f + 0.5f)) * inputHeight);
// Ensure we're within the input texture bounds
if (sx >= 0 && sx < inputWidth && sy >= 0 && sy < inputHeight) {
// Sample the input texture
int inputIndex = (sy * inputWidth + sx) * 4;
int outputIndex = (y * outputWidth + x) * 4;
// Copy the color to the output texture
std::copy_n(&inputTexture[inputIndex], 4, &outputTexture[outputIndex]);
}
}
}
}
}
math::float3 UnprojectTexture::doUnproject(const math::float2& screenPos, float depth, const math::mat4& invViewProj) {
math::float4 clipSpace(screenPos.x * 2.0f - 1.0f, screenPos.y * 2.0f - 1.0f, depth * 2.0f - 1.0f, 1.0f);
math::float4 worldSpace = invViewProj * clipSpace;
return math::float3(worldSpace.xyz) / worldSpace.w;
}
bool UnprojectTexture::isInsideTriangle(const math::float2& p, const math::float2& a, const math::float2& b, const math::float2& c) {
float d1 = (p.x - b.x) * (a.y - b.y) - (a.x - b.x) * (p.y - b.y);
float d2 = (p.x - c.x) * (b.y - c.y) - (b.x - c.x) * (p.y - c.y);
float d3 = (p.x - a.x) * (c.y - a.y) - (c.x - a.x) * (p.y - a.y);
return (d1 >= 0 && d2 >= 0 && d3 >= 0) || (d1 <= 0 && d2 <= 0 && d3 <= 0);
}
math::float3 UnprojectTexture::barycentric(const math::float2& p, const math::float2& a, const math::float2& b, const math::float2& c) {
math::float2 v0 = b - a, v1 = c - a, v2 = p - a;
float d00 = dot(v0, v0);
float d01 = dot(v0, v1);
float d11 = dot(v1, v1);
float d20 = dot(v2, v0);
float d21 = dot(v2, v1);
float denom = d00 * d11 - d01 * d01;
float v = (d11 * d20 - d01 * d21) / denom;
float w = (d00 * d21 - d01 * d20) / denom;
float u = 1.0f - v - w;
return math::float3(u, v, w);
}
} // namespace thermion_filament

View File

@@ -2,7 +2,7 @@ import 'dart:async';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'dart:ffi'; import 'dart:ffi';
import 'package:thermion_dart/thermion_dart.dart'; import 'package:thermion_dart/thermion_dart.dart';
import 'package:thermion_dart/thermion_dart/thermion_viewer_ffi.dart'; import 'package:thermion_dart/thermion_dart/viewer/ffi/thermion_viewer_ffi.dart';
import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_interface.dart'; import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_interface.dart';
import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart'; import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';