feat! js_interop improvements

This commit is contained in:
Nick Fisher
2025-05-07 17:06:38 +08:00
parent 63e2dcd0ca
commit 2f16908992
159 changed files with 12989 additions and 8377 deletions

View File

@@ -1,356 +0,0 @@
import 'dart:typed_data';
import 'package:vector_math/vector_math_64.dart' as v64;
import 'package:animation_tools_dart/src/bone_animation_data.dart';
import 'package:animation_tools_dart/src/morph_animation_data.dart';
import 'package:thermion_dart/src/filament/src/layers.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_scene.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_texture.dart';
import 'package:thermion_dart/thermion_dart.dart';
class BackgroundImage extends ThermionAsset {
final ThermionAsset asset;
ThermionEntity get entity => asset.entity;
Texture? texture;
FFITextureSampler? sampler;
final MaterialInstance mi;
final FFIScene scene;
BackgroundImage._(
this.asset, this.scene, this.texture, this.sampler, this.mi);
///
///
///
Future destroy() async {
Scene_removeEntity(scene.scene, entity);
await texture?.dispose();
await sampler?.dispose();
await mi.destroy();
}
///
///
///
static Future<BackgroundImage> create(
ThermionViewer viewer, FFIScene scene) async {
var imageMaterialInstance =
await FilamentApp.instance!.createImageMaterialInstance();
var backgroundImage =
await viewer.createGeometry(GeometryHelper.fullscreenQuad());
await imageMaterialInstance.setParameterInt("showImage", 0);
await imageMaterialInstance.setParameterMat4(
"transform", Matrix4.identity());
backgroundImage.setMaterialInstanceAt(imageMaterialInstance);
await scene.add(backgroundImage as FFIAsset);
return BackgroundImage._(
backgroundImage, scene, null, null, imageMaterialInstance);
}
///
///
///
Future setBackgroundColor(double r, double g, double b, double a) async {
await mi.setParameterFloat4("backgroundColor", r, g, b, a);
}
Future hideImage() async {
await mi.setParameterInt("showImage", 0);
}
///
///
///
Future setImage(Uint8List imageData) async {
final image = await FilamentApp.instance!.decodeImage(imageData);
texture ??= await FilamentApp.instance!.createTexture(
await image.getWidth(), await image.getHeight(),
textureFormat: TextureFormat.RGBA32F);
await texture!
.setLinearImage(image, PixelDataFormat.RGBA, PixelDataType.FLOAT);
sampler ??=
await FilamentApp.instance!.createTextureSampler() as FFITextureSampler;
await mi.setParameterTexture(
"image", texture as FFITexture, sampler as FFITextureSampler);
await setBackgroundColor(1, 1, 1, 0);
await mi.setParameterInt("showImage", 1);
}
///
///
///
@override
Future<ThermionAsset> createInstance(
{covariant List<MaterialInstance>? materialInstances = null}) {
throw UnimplementedError();
}
///
///
///
@override
Future<List<ThermionEntity>> getChildEntities() async {
return [];
}
@override
Future<ThermionAsset> getInstance(int index) {
throw UnimplementedError();
}
@override
Future<int> getInstanceCount() async {
return 0;
}
@override
Future<List<ThermionAsset>> getInstances() async {
return [];
}
@override
Future removeStencilHighlight() {
// TODO: implement removeStencilHighlight
throw UnimplementedError();
}
@override
Future setCastShadows(bool castShadows) {
// TODO: implement setCastShadows
throw UnimplementedError();
}
@override
Future setMaterialInstanceAt(covariant MaterialInstance instance) {
// TODO: implement setMaterialInstanceAt
throw UnimplementedError();
}
@override
Future setReceiveShadows(bool castShadows) {
// TODO: implement setReceiveShadows
throw UnimplementedError();
}
@override
Future setStencilHighlight(
{double r = 1.0, double g = 0.0, double b = 0.0, int? entityIndex}) {
// TODO: implement setStencilHighlight
throw UnimplementedError();
}
@override
Future setVisibilityLayer(ThermionEntity entity, VisibilityLayers layer) {
// TODO: implement setVisibilityLayer
throw UnimplementedError();
}
@override
Future addAnimationComponent(ThermionEntity entity) {
// TODO: implement addAnimationComponent
throw UnimplementedError();
}
@override
Future addBoneAnimation(BoneAnimationData animation,
{int skinIndex = 0,
double fadeInInSecs = 0.0,
double fadeOutInSecs = 0.0,
double maxDelta = 1.0}) {
// TODO: implement addBoneAnimation
throw UnimplementedError();
}
@override
Future clearMorphAnimationData(ThermionEntity entity) {
// TODO: implement clearMorphAnimationData
throw UnimplementedError();
}
@override
Future<double> getAnimationDuration(int animationIndex) {
// TODO: implement getAnimationDuration
throw UnimplementedError();
}
@override
Future<List<String>> getAnimationNames() {
// TODO: implement getAnimationNames
throw UnimplementedError();
}
@override
Future<ThermionEntity> getBone(int boneIndex, {int skinIndex = 0}) {
// TODO: implement getBone
throw UnimplementedError();
}
@override
Future<List<String>> getBoneNames({int skinIndex = 0}) {
// TODO: implement getBoneNames
throw UnimplementedError();
}
@override
Future<ThermionEntity?> getChildEntity(String childName) {
// TODO: implement getChildEntity
throw UnimplementedError();
}
@override
Future<Matrix4> getInverseBindMatrix(int boneIndex, {int skinIndex = 0}) {
// TODO: implement getInverseBindMatrix
throw UnimplementedError();
}
@override
Future<Matrix4> getLocalTransform({ThermionEntity? entity}) {
// TODO: implement getLocalTransform
throw UnimplementedError();
}
@override
Future<List<String>> getMorphTargetNames({ThermionEntity? entity}) {
// TODO: implement getMorphTargetNames
throw UnimplementedError();
}
@override
Future<Matrix4> getWorldTransform({ThermionEntity? entity}) {
// TODO: implement getWorldTransform
throw UnimplementedError();
}
@override
Future playAnimation(int index,
{bool loop = false,
bool reverse = false,
bool replaceActive = true,
double crossfade = 0.0,
double startOffset = 0.0}) {
// TODO: implement playAnimation
throw UnimplementedError();
}
@override
Future playAnimationByName(String name,
{bool loop = false,
bool reverse = false,
bool replaceActive = true,
double crossfade = 0.0}) {
// TODO: implement playAnimationByName
throw UnimplementedError();
}
@override
Future removeAnimationComponent(ThermionEntity entity) {
// TODO: implement removeAnimationComponent
throw UnimplementedError();
}
@override
Future resetBones() {
// TODO: implement resetBones
throw UnimplementedError();
}
@override
Future setBoneTransform(
ThermionEntity entity, int boneIndex, Matrix4 transform,
{int skinIndex = 0}) {
// TODO: implement setBoneTransform
throw UnimplementedError();
}
@override
Future setGltfAnimationFrame(int index, int animationFrame) {
// TODO: implement setGltfAnimationFrame
throw UnimplementedError();
}
@override
Future setMorphAnimationData(MorphAnimationData animation,
{List<String>? targetMeshNames}) {
// TODO: implement setMorphAnimationData
throw UnimplementedError();
}
@override
Future setMorphTargetWeights(ThermionEntity entity, List<double> weights) {
// TODO: implement setMorphTargetWeights
throw UnimplementedError();
}
@override
Future setTransform(Matrix4 transform, {ThermionEntity? entity}) {
// TODO: implement setTransform
throw UnimplementedError();
}
@override
Future stopAnimation(int animationIndex) {
// TODO: implement stopAnimation
throw UnimplementedError();
}
@override
Future stopAnimationByName(String name) {
// TODO: implement stopAnimationByName
throw UnimplementedError();
}
@override
Future updateBoneMatrices(ThermionEntity entity) {
// TODO: implement updateBoneMatrices
throw UnimplementedError();
}
@override
Future<List<String>> getChildEntityNames() async {
return [];
}
@override
Future<bool> isCastShadowsEnabled({ThermionEntity? entity}) async {
return false;
}
@override
Future<bool> isReceiveShadowsEnabled({ThermionEntity? entity}) async {
return false;
}
@override
Future transformToUnitCube() {
// TODO: implement transformToUnitCube
throw UnimplementedError();
}
@override
Future<MaterialInstance> getMaterialInstanceAt(
{ThermionEntity? entity, int index = 0}) {
throw UnimplementedError();
}
ThermionAsset? get boundingBoxAsset => throw UnimplementedError();
@override
Future<ThermionAsset> createBoundingBoxAsset() {
throw UnimplementedError();
}
Future<v64.Aabb3> getBoundingBox() {
throw UnimplementedError();
}
}

View File

@@ -1,129 +0,0 @@
import 'dart:async';
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_dart.g.dart';
export 'package:ffi/ffi.dart';
export 'dart:ffi';
export 'thermion_dart.g.dart';
final allocator = calloc;
void using(Pointer ptr, Future Function(Pointer ptr) function) async {
await function.call(ptr);
allocator.free(ptr);
}
Future<void> withVoidCallback2(Function() func) async {
final completer = Completer();
void Function() callback = () {
func.call();
completer.complete();
};
final nativeCallable = NativeCallable<Void Function()>.listener(callback);
RenderThread_addTask(nativeCallable.nativeFunction);
await completer.future;
nativeCallable.close();
}
Future<void> withVoidCallback(
Function(Pointer<NativeFunction<Void Function()>>) func) async {
final completer = Completer();
// ignore: prefer_function_declarations_over_variables
void Function() callback = () {
completer.complete();
};
final nativeCallable = NativeCallable<Void Function()>.listener(callback);
func.call(nativeCallable.nativeFunction);
await completer.future;
nativeCallable.close();
}
Future<Pointer<T>> withPointerCallback<T extends NativeType>(
Function(Pointer<NativeFunction<Void Function(Pointer<T>)>>) func) async {
final completer = Completer<Pointer<T>>();
// ignore: prefer_function_declarations_over_variables
void Function(Pointer<NativeType>) callback = (Pointer<NativeType> ptr) {
completer.complete(ptr.cast<T>());
};
final nativeCallable =
NativeCallable<Void Function(Pointer<NativeType>)>.listener(callback);
func.call(nativeCallable.nativeFunction);
var ptr = await completer.future;
nativeCallable.close();
return ptr;
}
Future<bool> withBoolCallback(
Function(Pointer<NativeFunction<Void Function(Bool)>>) func) async {
final completer = Completer<bool>();
// ignore: prefer_function_declarations_over_variables
void Function(bool) callback = (bool result) {
completer.complete(result);
};
final nativeCallable = NativeCallable<Void Function(Bool)>.listener(callback);
func.call(nativeCallable.nativeFunction);
await completer.future;
nativeCallable.close();
return completer.future;
}
Future<double> withFloatCallback(
Function(Pointer<NativeFunction<Void Function(Float)>>) func) async {
final completer = Completer<double>();
// ignore: prefer_function_declarations_over_variables
void Function(double) callback = (double result) {
completer.complete(result);
};
final nativeCallable = NativeCallable<Void Function(Float)>.listener(callback);
func.call(nativeCallable.nativeFunction);
await completer.future;
nativeCallable.close();
return completer.future;
}
Future<int> withIntCallback(
Function(Pointer<NativeFunction<Void Function(Int32)>>) func) async {
final completer = Completer<int>();
// ignore: prefer_function_declarations_over_variables
void Function(int) callback = (int result) {
completer.complete(result);
};
final nativeCallable =
NativeCallable<Void Function(Int32)>.listener(callback);
func.call(nativeCallable.nativeFunction);
await completer.future;
nativeCallable.close();
return completer.future;
}
Future<int> withUInt32Callback(
Function(Pointer<NativeFunction<Void Function(Uint32)>>) func) async {
final completer = Completer<int>();
// ignore: prefer_function_declarations_over_variables
void Function(int) callback = (int result) {
completer.complete(result);
};
final nativeCallable =
NativeCallable<Void Function(Uint32)>.listener(callback);
func.call(nativeCallable.nativeFunction);
await completer.future;
nativeCallable.close();
return completer.future;
}
Future<String> withCharPtrCallback(
Function(Pointer<NativeFunction<Void Function(Pointer<Char>)>>)
func) async {
final completer = Completer<String>();
// ignore: prefer_function_declarations_over_variables
void Function(Pointer<Char>) callback = (Pointer<Char> result) {
completer.complete(result.cast<Utf8>().toDartString());
};
final nativeCallable =
NativeCallable<Void Function(Pointer<Char>)>.listener(callback);
func.call(nativeCallable.nativeFunction);
await completer.future;
nativeCallable.close();
return completer.future;
}

View File

@@ -1,917 +0,0 @@
import 'dart:typed_data';
import 'package:animation_tools_dart/animation_tools_dart.dart';
import 'package:logging/logging.dart';
import 'package:thermion_dart/src/filament/src/layers.dart';
import 'package:thermion_dart/src/utils/src/matrix.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_material.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_viewer_ffi.dart';
import 'package:thermion_dart/thermion_dart.dart';
import 'package:vector_math/vector_math_64.dart' as v64;
class FFIAsset extends ThermionAsset {
///
///
///
final Pointer<TSceneAsset> asset;
///
///
///
final FFIFilamentApp app;
///
///
///
final Pointer<TAnimationManager> animationManager;
///
///
///
FFIAsset? _highlight;
///
///
///
final bool isInstance;
///
///
///
late final ThermionEntity entity;
late final _logger = Logger(this.runtimeType.toString());
///
///
///
FFIAsset(this.asset, this.app, this.animationManager,
{this.isInstance = false}) {
entity = SceneAsset_getEntity(asset);
}
///
///
///
@override
Future<List<ThermionEntity>> getChildEntities() async {
var count = SceneAsset_getChildEntityCount(asset);
var children = Int32List(count);
SceneAsset_getChildEntities(asset, children.address);
return children;
}
///
///
///
@override
Future<List<String?>> getChildEntityNames() async {
final childEntities = await getChildEntities();
var names = <String?>[];
for (final entity in childEntities) {
var name = NameComponentManager_getName(app.nameComponentManager, entity);
if (name == nullptr) {
names.add(null);
} else {
names.add(name.cast<Utf8>().toDartString());
}
}
return names;
}
///
///
///
@override
Future<ThermionEntity?> getChildEntity(String childName) async {
final childEntities = await getChildEntities();
for (final entity in childEntities) {
var name = NameComponentManager_getName(app.nameComponentManager, entity);
if (name == childName) {
return entity;
}
}
return null;
}
///
///
///
@override
Future<ThermionAsset> getInstance(int index) async {
if (isInstance) {
throw Exception(
"This is itself an instance. Call getInstance on the original asset that this instance was created from");
}
var instance = SceneAsset_getInstance(asset, index);
if (instance == nullptr) {
throw Exception("No instance available at index $index");
}
return FFIAsset(instance, app, animationManager);
}
///
///
///
@override
Future<FFIAsset> createInstance(
{covariant List<MaterialInstance>? materialInstances = null}) async {
var created = await withPointerCallback<TSceneAsset>((cb) {
var ptrList = Int64List(materialInstances?.length ?? 0);
if (materialInstances != null && materialInstances.isNotEmpty) {
ptrList.setRange(
0,
materialInstances.length,
materialInstances
.cast<FFIMaterialInstance>()
.map((mi) => mi.pointer.address)
.toList());
}
SceneAsset_createInstanceRenderThread(
asset,
ptrList.address.cast<Pointer<TMaterialInstance>>(),
materialInstances?.length ?? 0,
cb);
});
if (created == FILAMENT_ASSET_ERROR) {
throw Exception("Failed to create instance");
}
return FFIAsset(created, app, animationManager);
}
///
///
///
@override
Future<int> getInstanceCount() async {
return SceneAsset_getInstanceCount(asset);
}
///
///
///
@override
Future<List<ThermionAsset>> getInstances() async {
var count = await getInstanceCount();
final result = List<ThermionAsset>.generate(count, (i) {
return FFIAsset(SceneAsset_getInstance(asset, i), app, animationManager);
});
return result;
}
///
///
///
@override
Future removeStencilHighlight() async {
throw UnimplementedError();
// if (_highlight != null) {
// SceneManager_removeFromScene(sceneManager, _highlight!.entity);
// final childEntities = await _highlight!.getChildEntities();
// for (final child in childEntities) {
// SceneManager_removeFromScene(sceneManager, child);
// }
// }
}
///
///
///
@override
Future setStencilHighlight(
{double r = 1.0,
double g = 0.0,
double b = 0.0,
int? entityIndex}) async {
if (_highlight == null) {
var targetEntity = this.entity;
if (entityIndex != null) {
final childEntities = await this.getChildEntities();
targetEntity = childEntities[entityIndex];
}
var sourceMaterialInstance = FFIMaterialInstance(
RenderableManager_getMaterialInstanceAt(
app.renderableManager, targetEntity, 0),
app);
await sourceMaterialInstance.setStencilWriteEnabled(true);
await sourceMaterialInstance.setDepthWriteEnabled(true);
await sourceMaterialInstance
.setStencilOpDepthStencilPass(StencilOperation.REPLACE);
await sourceMaterialInstance.setStencilReferenceValue(1);
final materialInstancePtr =
await withPointerCallback<TMaterialInstance>((cb) {
final key = Struct.create<TMaterialKey>();
MaterialProvider_createMaterialInstanceRenderThread(
app.ubershaderMaterialProvider, key.address, cb);
});
final highlightMaterialInstance =
FFIMaterialInstance(materialInstancePtr, app);
await highlightMaterialInstance
.setStencilCompareFunction(SamplerCompareFunction.NE);
await highlightMaterialInstance.setStencilReferenceValue(1);
await highlightMaterialInstance.setDepthCullingEnabled(false);
await highlightMaterialInstance.setParameterFloat4(
"baseColorFactor", r, g, b, 1.0);
var highlightInstance = await this
.createInstance(materialInstances: [highlightMaterialInstance]);
_highlight = highlightInstance;
await highlightMaterialInstance.setStencilReferenceValue(1);
RenderableManager_setPriority(app.renderableManager, targetEntity, 0);
TransformManager_setParent(
app.transformManager, _highlight!.entity, entity, false);
}
var targetHighlightEntity = _highlight!.entity;
if (entityIndex != null) {
var highlightChildEntities = await _highlight!.getChildEntities();
targetHighlightEntity = highlightChildEntities[entityIndex];
}
RenderableManager_setPriority(
app.renderableManager, targetHighlightEntity, 7);
throw UnimplementedError();
}
///
///
///
ThermionAsset? boundingBoxAsset;
///
///
///
Future<v64.Aabb3> getBoundingBox() async {
final entities = <ThermionEntity>[];
if (RenderableManager_isRenderable(app.renderableManager, entity)) {
entities.add(entity);
} else {
entities.addAll(await getChildEntities());
}
var boundingBox = v64.Aabb3();
for (final entity in entities) {
final aabb3 = RenderableManager_getAabb(app.renderableManager, entity);
final entityBB = v64.Aabb3.centerAndHalfExtents(
v64.Vector3(aabb3.centerX, aabb3.centerY, aabb3.centerZ),
v64.Vector3(aabb3.halfExtentX, aabb3.halfExtentY, aabb3.halfExtentZ),
);
boundingBox.hull(entityBB);
}
return boundingBox;
}
///
///
///
Future<ThermionAsset> createBoundingBoxAsset() async {
if (boundingBoxAsset == null) {
final boundingBox = await SceneAsset_getBoundingBox(asset);
final min = [
boundingBox.centerX - boundingBox.halfExtentX,
boundingBox.centerY - boundingBox.halfExtentY,
boundingBox.centerZ - boundingBox.halfExtentZ
];
final max = [
boundingBox.centerX + boundingBox.halfExtentX,
boundingBox.centerY + boundingBox.halfExtentY,
boundingBox.centerZ + boundingBox.halfExtentZ
];
// Create vertices for the bounding box wireframe
// 8 vertices for a cube
final vertices = Float32List(8 * 3);
// Bottom vertices
vertices[0] = min[0];
vertices[1] = min[1];
vertices[2] = min[2]; // v0
vertices[3] = max[0];
vertices[4] = min[1];
vertices[5] = min[2]; // v1
vertices[6] = max[0];
vertices[7] = min[1];
vertices[8] = max[2]; // v2
vertices[9] = min[0];
vertices[10] = min[1];
vertices[11] = max[2]; // v3
// Top vertices
vertices[12] = min[0];
vertices[13] = max[1];
vertices[14] = min[2]; // v4
vertices[15] = max[0];
vertices[16] = max[1];
vertices[17] = min[2]; // v5
vertices[18] = max[0];
vertices[19] = max[1];
vertices[20] = max[2]; // v6
vertices[21] = min[0];
vertices[22] = max[1];
vertices[23] = max[2]; // v7
// Indices for lines (24 indices for 12 lines)
final indices = [
// Bottom face
0, 1, 1, 2, 2, 3, 3, 0,
// Top face
4, 5, 5, 6, 6, 7, 7, 4,
// Vertical edges
0, 4, 1, 5, 2, 6, 3, 7
];
// Create unlit material instance for the wireframe
final materialInstancePtr =
await withPointerCallback<TMaterialInstance>((cb) {
final key = Struct.create<TMaterialKey>();
MaterialProvider_createMaterialInstanceRenderThread(
app.ubershaderMaterialProvider, key.address, cb);
});
final material = FFIMaterialInstance(materialInstancePtr, app);
await material.setParameterFloat4(
"baseColorFactor", 1.0, 1.0, 0.0, 1.0); // Yellow wireframe
// Create geometry for the bounding box
final geometry = Geometry(
vertices,
indices,
primitiveType: PrimitiveType.LINES,
);
boundingBoxAsset = await FilamentApp.instance!.createGeometry(
geometry,
animationManager,
materialInstances: [material],
keepData: false,
) as FFIAsset;
await boundingBoxAsset!.setCastShadows(false);
await boundingBoxAsset!.setReceiveShadows(false);
TransformManager_setParent(Engine_getTransformManager(app.engine),
boundingBoxAsset!.entity, entity, false);
}
return boundingBoxAsset!;
}
///
///
///
@override
Future<MaterialInstance> getMaterialInstanceAt(
{ThermionEntity? entity, int index = 0}) async {
entity ??= this.entity;
var ptr = RenderableManager_getMaterialInstanceAt(
Engine_getRenderableManager(app.engine), entity, 0);
return FFIMaterialInstance(ptr, app);
}
///
///
///
@override
Future setMaterialInstanceAt(FFIMaterialInstance instance) async {
var childEntities = await getChildEntities();
final entities = <ThermionEntity>[entity, ...childEntities];
for (final entity in entities) {
RenderableManager_setMaterialInstanceAt(
Engine_getRenderableManager(app.engine), entity, 0, instance.pointer);
}
}
///
///
///
Future setCastShadows(bool castShadows) async {
RenderableManager_setCastShadows(
app.renderableManager, this.entity, castShadows);
for (final entity in await this.getChildEntities()) {
RenderableManager_setCastShadows(
app.renderableManager, entity, castShadows);
}
}
///
///
///
Future setReceiveShadows(bool receiveShadows) async {
RenderableManager_setReceiveShadows(
app.renderableManager, this.entity, receiveShadows);
for (final entity in await this.getChildEntities()) {
RenderableManager_setReceiveShadows(
app.renderableManager, entity, receiveShadows);
}
}
///
///
///
Future<bool> isCastShadowsEnabled({ThermionEntity? entity}) async {
entity ??= this.entity;
return RenderableManager_isShadowCaster(app.renderableManager, entity);
}
///
///
///
Future<bool> isReceiveShadowsEnabled({ThermionEntity? entity}) async {
entity ??= this.entity;
return RenderableManager_isShadowReceiver(app.renderableManager, entity);
}
///
///
///
Future transformToUnitCube() async {
TransformManager_transformToUnitCube(
app.transformManager, entity, SceneAsset_getBoundingBox(asset));
}
///
///
///
Future setVisibilityLayer(
ThermionEntity entity, VisibilityLayers layer) async {
RenderableManager_setVisibilityLayer(
app.renderableManager, entity, layer.value);
}
///
///
///
@override
Future setMorphTargetWeights(
ThermionEntity entity, List<double> weights) async {
if (weights.isEmpty) {
throw Exception("Weights must not be empty");
}
var weightsPtr = allocator<Float>(weights.length);
for (int i = 0; i < weights.length; i++) {
weightsPtr[i] = weights[i];
}
var success = await withBoolCallback((cb) {
AnimationManager_setMorphTargetWeightsRenderThread(
animationManager, entity, weightsPtr, weights.length, cb);
});
allocator.free(weightsPtr);
if (!success) {
throw Exception(
"Failed to set morph target weights, check logs for details");
}
}
///
///
///
@override
Future<List<String>> getMorphTargetNames({ThermionEntity? entity}) async {
var names = <String>[];
entity ??= this.entity;
var count = AnimationManager_getMorphTargetNameCount(
animationManager, asset, entity);
var outPtr = allocator<Char>(255);
for (int i = 0; i < count; i++) {
AnimationManager_getMorphTargetName(
animationManager, asset, entity, outPtr, i);
names.add(outPtr.cast<Utf8>().toDartString());
}
allocator.free(outPtr);
return names.cast<String>();
}
///
///
///
Future<List<String>> getBoneNames({int skinIndex = 0}) async {
var count =
AnimationManager_getBoneCount(animationManager, asset, skinIndex);
var out = allocator<Pointer<Char>>(count);
for (int i = 0; i < count; i++) {
out[i] = allocator<Char>(255);
}
AnimationManager_getBoneNames(animationManager, asset, out, skinIndex);
var names = <String>[];
for (int i = 0; i < count; i++) {
var namePtr = out[i];
names.add(namePtr.cast<Utf8>().toDartString());
}
return names;
}
///
///
///
@override
Future<List<String>> getAnimationNames() async {
var animationCount =
AnimationManager_getAnimationCount(animationManager, asset);
var names = <String>[];
var outPtr = allocator<Char>(255);
for (int i = 0; i < animationCount; i++) {
AnimationManager_getAnimationName(animationManager, asset, outPtr, i);
names.add(outPtr.cast<Utf8>().toDartString());
}
allocator.free(outPtr);
return names;
}
///
///
///
@override
Future<double> getAnimationDuration(int animationIndex) async {
return AnimationManager_getAnimationDuration(
animationManager, asset, animationIndex);
}
///
///
///
Future<double> getAnimationDurationByName(String name) async {
var animations = await getAnimationNames();
var index = animations.indexOf(name);
if (index == -1) {
throw Exception("Failed to find animation $name");
}
return getAnimationDuration(index);
}
///
///
///
Future clearMorphAnimationData(ThermionEntity entity) async {
if (!AnimationManager_clearMorphAnimation(animationManager, entity)) {
throw Exception("Failed to clear morph animation");
}
}
///
///
///
@override
Future setMorphAnimationData(MorphAnimationData animation,
{List<String>? targetMeshNames}) async {
var meshEntities = await getChildEntities();
var meshNames = meshEntities
.map((e) => FilamentApp.instance!.getNameForEntity(e))
.toList();
if (targetMeshNames != null) {
for (final targetMeshName in targetMeshNames) {
if (!meshNames.contains(targetMeshName)) {
throw Exception(
"Error: mesh ${targetMeshName} does not exist under the specified entity. Available meshes : ${meshNames}");
}
}
}
// Entities are not guaranteed to have the same morph targets (or share the same order),
// either from each other, or from those specified in [animation].
// We therefore set morph targets separately for each mesh.
// For each mesh, allocate enough memory to hold FxM 32-bit floats
// (where F is the number of Frames, and M is the number of morph targets in the mesh).
// we call [extract] on [animation] to return frame data only for morph targets that present in both the mesh and the animation
for (int i = 0; i < meshNames.length; i++) {
var meshName = meshNames[i];
var meshEntity = meshEntities[i];
if (targetMeshNames?.contains(meshName) == false) {
// _logger.info("Skipping $meshName, not contained in target");
continue;
}
var meshMorphTargets = await getMorphTargetNames(entity: meshEntity);
var intersection = animation.morphTargets
.toSet()
.intersection(meshMorphTargets.toSet())
.toList();
if (intersection.isEmpty) {
throw Exception(
"""No morph targets specified in animation are present on mesh $meshName.
If you weren't intending to animate every mesh, specify [targetMeshNames] when invoking this method.
Animation morph targets: ${animation.morphTargets}\n
Mesh morph targets ${meshMorphTargets}
Child meshes: ${meshNames}""");
}
var indices = Uint32List.fromList(
intersection.map((m) => meshMorphTargets.indexOf(m)).toList());
// var frameData = animation.data;
var frameData = animation.subset(intersection);
assert(
frameData.data.length == animation.numFrames * intersection.length);
var result = AnimationManager_setMorphAnimation(
animationManager,
meshEntity,
frameData.data.address,
indices.address,
indices.length,
animation.numFrames,
animation.frameLengthInMs);
if (!result) {
throw Exception("Failed to set morph animation data for ${meshName}");
}
}
}
///
/// Currently, scale is not supported.
///
@override
Future addBoneAnimation(BoneAnimationData animation,
{int skinIndex = 0,
double fadeOutInSecs = 0.0,
double fadeInInSecs = 0.0,
double maxDelta = 1.0}) async {
if (animation.space != Space.Bone &&
animation.space != Space.ParentWorldRotation) {
throw UnimplementedError("TODO - support ${animation.space}");
}
if (skinIndex != 0) {
throw UnimplementedError("TODO - support skinIndex != 0 ");
}
var boneNames = await getBoneNames();
var restLocalTransformsRaw = allocator<Float>(boneNames.length * 16);
AnimationManager_getRestLocalTransforms(animationManager, asset, skinIndex,
restLocalTransformsRaw, boneNames.length);
var restLocalTransforms = <Matrix4>[];
for (int i = 0; i < boneNames.length; i++) {
var values = <double>[];
for (int j = 0; j < 16; j++) {
values.add(restLocalTransformsRaw[(i * 16) + j]);
}
restLocalTransforms.add(Matrix4.fromList(values));
}
allocator.free(restLocalTransformsRaw);
var numFrames = animation.frameData.length;
var data = allocator<Float>(numFrames * 16);
var bones = await Future.wait(List<Future<ThermionEntity>>.generate(
boneNames.length, (i) => getBone(i)));
for (int i = 0; i < animation.bones.length; i++) {
var boneName = animation.bones[i];
var entityBoneIndex = boneNames.indexOf(boneName);
if (entityBoneIndex == -1) {
_logger.warning("Bone $boneName not found, skipping");
continue;
}
var boneEntity = bones[entityBoneIndex];
var baseTransform = restLocalTransforms[entityBoneIndex];
var world = Matrix4.identity();
// this odd use of ! is intentional, without it, the WASM optimizer gets in trouble
var parentBoneEntity =
(await FilamentApp.instance!.getParent(boneEntity))!;
while (true) {
if (!bones.contains(parentBoneEntity!)) {
break;
}
world = restLocalTransforms[bones.indexOf(parentBoneEntity!)] * world;
parentBoneEntity =
(await FilamentApp.instance!.getParent(parentBoneEntity))!;
}
world = Matrix4.identity()..setRotation(world.getRotation());
var worldInverse = Matrix4.identity()..copyInverse(world);
for (int frameNum = 0; frameNum < numFrames; frameNum++) {
var rotation = animation.frameData[frameNum][i].rotation;
var translation = animation.frameData[frameNum][i].translation;
var frameTransform =
Matrix4.compose(translation, rotation, Vector3.all(1.0));
var newLocalTransform = frameTransform.clone();
if (animation.space == Space.Bone) {
newLocalTransform = baseTransform * frameTransform;
} else if (animation.space == Space.ParentWorldRotation) {
newLocalTransform =
baseTransform * (worldInverse * frameTransform * world);
}
for (int j = 0; j < 16; j++) {
data.elementAt((frameNum * 16) + j).value =
newLocalTransform.storage[j];
}
}
AnimationManager_addBoneAnimation(
animationManager,
asset,
skinIndex,
entityBoneIndex,
data,
numFrames,
animation.frameLengthInMs,
fadeOutInSecs,
fadeInInSecs,
maxDelta);
}
allocator.free(data);
}
///
///
///
Future<Matrix4> getLocalTransform({ThermionEntity? entity}) async {
entity ??= this.entity;
return double4x4ToMatrix4(
TransformManager_getLocalTransform(app.transformManager, entity));
}
///
///
///
Future<Matrix4> getWorldTransform({ThermionEntity? entity}) async {
entity ??= this.entity;
return double4x4ToMatrix4(
TransformManager_getWorldTransform(app.transformManager, entity));
}
///
///
///
Future setTransform(Matrix4 transform, {ThermionEntity? entity}) async {
entity ??= this.entity;
TransformManager_setTransform(
app.transformManager, entity, matrix4ToDouble4x4(transform));
}
///
///
///
Future updateBoneMatrices(ThermionEntity entity) async {
throw UnimplementedError();
// var result = await withBoolCallback((cb) {
// update_bone_matrices_render_thread(_sceneManager!, entity, cb);
// });
// if (!result) {
// throw Exception("Failed to update bone matrices");
// }
}
///
///
///
Future<Matrix4> getInverseBindMatrix(int boneIndex,
{int skinIndex = 0}) async {
var matrix = Float32List(16);
AnimationManager_getInverseBindMatrix(
animationManager, asset, skinIndex, boneIndex, matrix.address);
return Matrix4.fromList(matrix);
}
///
///
///
Future<ThermionEntity> getBone(int boneIndex, {int skinIndex = 0}) async {
if (skinIndex != 0) {
throw UnimplementedError("TOOD");
}
return AnimationManager_getBone(
animationManager, asset, skinIndex, boneIndex);
}
///
///
///
@override
Future setBoneTransform(
ThermionEntity entity, int boneIndex, Matrix4 transform,
{int skinIndex = 0}) async {
if (skinIndex != 0) {
throw UnimplementedError("TOOD");
}
final ptr = allocator<Float>(16);
for (int i = 0; i < 16; i++) {
ptr[i] = transform.storage[i];
}
var result = await withBoolCallback((cb) {
AnimationManager_setBoneTransformRenderThread(
animationManager, entity, skinIndex, boneIndex, ptr, cb);
});
allocator.free(ptr);
if (!result) {
throw Exception("Failed to set bone transform");
}
}
///
///
///
///
@override
Future resetBones() async {
AnimationManager_resetToRestPose(animationManager, asset);
}
///
///
///
@override
Future playAnimation(int index,
{bool loop = false,
bool reverse = false,
bool replaceActive = true,
double crossfade = 0.0,
double startOffset = 0.0}) async {
AnimationManager_playAnimation(animationManager, asset, index, loop,
reverse, replaceActive, crossfade, startOffset);
}
///
///
///
@override
Future stopAnimation(int animationIndex) async {
AnimationManager_stopAnimation(animationManager, asset, animationIndex);
}
///
///
///
@override
Future stopAnimationByName(String name) async {
var animations = await getAnimationNames();
await stopAnimation(animations.indexOf(name));
}
///
///
///
@override
Future playAnimationByName(String name,
{bool loop = false,
bool reverse = false,
bool replaceActive = true,
double crossfade = 0.0,
bool wait = false}) async {
var animations = await getAnimationNames();
var index = animations.indexOf(name);
var duration = await getAnimationDuration(index);
await playAnimation(index,
loop: loop,
reverse: reverse,
replaceActive: replaceActive,
crossfade: crossfade);
if (wait) {
await Future.delayed(Duration(milliseconds: (duration * 1000).toInt()));
}
}
///
///
///
@override
Future setGltfAnimationFrame(int index, int animationFrame) async {
AnimationManager_setGltfAnimationFrame(
animationManager, asset, index, animationFrame);
}
///
///
///
@override
Future addAnimationComponent(ThermionEntity entity) async {
AnimationManager_addAnimationComponent(animationManager, entity);
}
///
///
///
Future removeAnimationComponent(ThermionEntity entity) async {
AnimationManager_removeAnimationComponent(animationManager, entity);
}
}

View File

@@ -1,172 +0,0 @@
import 'dart:ffi';
import 'dart:typed_data';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_dart.g.dart';
import 'package:thermion_dart/src/filament/src/layers.dart';
import 'package:thermion_dart/thermion_dart.dart';
import '../../../../utils/src/matrix.dart';
class FFICamera extends Camera {
final Pointer<TCamera> camera;
final FFIFilamentApp app;
late ThermionEntity _entity;
FFICamera(this.camera, this.app) {
_entity = Camera_getEntity(camera);
}
///
///
///
@override
Future setProjectionMatrixWithCulling(
Matrix4 projectionMatrix, double near, double far) async {
Camera_setCustomProjectionWithCulling(
camera, matrix4ToDouble4x4(projectionMatrix), near, far);
}
///
///
///
Future<Matrix4> getModelMatrix() async {
return double4x4ToMatrix4(Camera_getModelMatrix(camera));
}
///
///
///
@override
Future<Matrix4> getProjectionMatrix() async {
var matrixStruct = Camera_getProjectionMatrix(camera);
return double4x4ToMatrix4(matrixStruct);
}
///
///
///
@override
Future<Matrix4> getCullingProjectionMatrix() async {
var matrixStruct = Camera_getCullingProjectionMatrix(camera);
return double4x4ToMatrix4(matrixStruct);
}
@override
Future setTransform(Matrix4 transform) async {
var entity = Camera_getEntity(camera);
TransformManager_setTransform(
app.transformManager, entity, matrix4ToDouble4x4(transform));
}
@override
Future setLensProjection(
{double near = kNear,
double far = kFar,
double aspect = 1.0,
double focalLength = kFocalLength}) async {
Camera_setLensProjection(camera, near, far, aspect, focalLength);
}
///
///
///
@override
ThermionEntity getEntity() {
return _entity;
}
///
///
///
@override
Future setModelMatrix(Matrix4 matrix) async {
Camera_setModelMatrix(camera, matrix.storage.address);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is FFICamera &&
runtimeType == other.runtimeType &&
camera == other.camera;
@override
int get hashCode => camera.hashCode;
///
///
///
@override
Future<double> getCullingFar() async {
return Camera_getCullingFar(camera);
}
///
///
///
@override
Future<double> getNear() async {
return Camera_getNear(camera);
}
///
///
///
@override
Future<double> getFocalLength() async {
return Camera_getFocalLength(camera);
}
///
///
///
Future<Frustum> getFrustum() async {
var out = Float64List(24);
Camera_getFrustum(camera, out.address);
var frustum = Frustum();
frustum.plane0.setFromComponents(out[0], out[1], out[2], out[3]);
frustum.plane1.setFromComponents(out[4], out[5], out[6], out[7]);
frustum.plane2.setFromComponents(out[8], out[9], out[10], out[11]);
frustum.plane3.setFromComponents(out[12], out[13], out[14], out[15]);
frustum.plane4.setFromComponents(out[16], out[17], out[18], out[19]);
frustum.plane5.setFromComponents(out[20], out[21], out[22], out[23]);
return frustum;
}
@override
Future<Matrix4> getViewMatrix() async {
return double4x4ToMatrix4(Camera_getViewMatrix(camera));
}
@override
Future setProjection(Projection projection, double left, double right,
double bottom, double top, double near, double far) async {
Camera_setProjection(camera, TProjection.values[projection.index], left,
right, bottom, top, near, far);
}
Future destroy() async {
Engine_destroyCamera(app.engine, camera);
}
Future setCameraExposure(
double aperture, double shutterSpeed, double sensitivity) async {
Camera_setExposure(camera, aperture, shutterSpeed, sensitivity);
}
Future<double> getFocusDistance() async => Camera_getFocusDistance(camera);
Future setFocusDistance(double focusDistance) async =>
Camera_setFocusDistance(camera, focusDistance);
@override
Future<double> getHorizontalFieldOfView() async {
return Camera_getFov(camera, true);
}
@override
Future<double> getVerticalFieldOfView() async {
return Camera_getFov(camera, false);
}
}

View File

@@ -1,964 +0,0 @@
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:thermion_dart/src/filament/src/scene.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_gizmo.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_material.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_render_target.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_scene.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_swapchain.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_texture.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_view.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_viewer_ffi.dart';
import 'package:thermion_dart/thermion_dart.dart';
import 'package:logging/logging.dart';
typedef RenderCallback = Pointer<NativeFunction<Void Function(Pointer<Void>)>>;
class FFIFilamentConfig extends FilamentConfig<RenderCallback, Pointer<Void>> {
FFIFilamentConfig(
{required super.resourceLoader,
super.backend = Backend.DEFAULT,
super.platform = null,
super.sharedContext = null,
super.uberArchivePath = null});
}
class FFIFilamentApp extends FilamentApp<Pointer> {
final Pointer<TEngine> engine;
final Pointer<TGltfAssetLoader> gltfAssetLoader;
final Pointer<TRenderer> renderer;
final Pointer<TTransformManager> transformManager;
final Pointer<TLightManager> lightManager;
final Pointer<TRenderableManager> renderableManager;
final Pointer<TMaterialProvider> ubershaderMaterialProvider;
final Pointer<TRenderTicker> renderTicker;
final Pointer<TNameComponentManager> nameComponentManager;
final Future<Uint8List> Function(String uri) resourceLoader;
late final _logger = Logger(this.runtimeType.toString());
FFIFilamentApp(
this.engine,
this.gltfAssetLoader,
this.renderer,
this.transformManager,
this.lightManager,
this.renderableManager,
this.ubershaderMaterialProvider,
this.renderTicker,
this.nameComponentManager,
this.resourceLoader)
: super(
engine: engine,
gltfAssetLoader: gltfAssetLoader,
renderer: renderer,
transformManager: transformManager,
lightManager: lightManager,
renderableManager: renderableManager,
ubershaderMaterialProvider: ubershaderMaterialProvider) {}
static Future<Uint8List> _defaultResourceLoader(String path) {
print("Loading file $path");
return File(path).readAsBytes();
}
static Future create({FFIFilamentConfig? config}) async {
config ??= FFIFilamentConfig(resourceLoader: _defaultResourceLoader);
if (FilamentApp.instance != null) {
await FilamentApp.instance!.destroy();
}
RenderThread_destroy();
RenderThread_create();
final engine = await withPointerCallback<TEngine>((cb) =>
Engine_createRenderThread(
TBackend.values[config!.backend.index].index,
config.platform ?? nullptr,
config.sharedContext ?? nullptr,
config.stereoscopicEyeCount,
config.disableHandleUseAfterFreeCheck,
cb));
final gltfAssetLoader = await withPointerCallback<TGltfAssetLoader>(
(cb) => GltfAssetLoader_createRenderThread(engine, nullptr, cb));
final renderer = await withPointerCallback<TRenderer>(
(cb) => Engine_createRendererRenderThread(engine, cb));
final ubershaderMaterialProvider =
GltfAssetLoader_getMaterialProvider(gltfAssetLoader);
final transformManager = Engine_getTransformManager(engine);
final lightManager = Engine_getLightManager(engine);
final renderableManager = Engine_getRenderableManager(engine);
final renderTicker = RenderTicker_create(renderer);
RenderThread_setRenderTicker(renderTicker);
final nameComponentManager = NameComponentManager_create();
FilamentApp.instance = FFIFilamentApp(
engine,
gltfAssetLoader,
renderer,
transformManager,
lightManager,
renderableManager,
ubershaderMaterialProvider,
renderTicker,
nameComponentManager,
config.resourceLoader);
}
final _swapChains = <FFISwapChain, List<FFIView>>{};
final viewsPtr = calloc<Pointer<TView>>(255);
///
///
///
Future updateRenderOrder() async {
for (final swapChain in _swapChains.keys) {
final views = _swapChains[swapChain];
if (views == null) {
continue;
}
int numRenderable = 0;
for (final view in views) {
if (view.renderable) {
viewsPtr[numRenderable] = view.view;
numRenderable++;
}
}
RenderTicker_setRenderable(
renderTicker, swapChain.swapChain, viewsPtr, numRenderable);
}
}
@override
Future<SwapChain> createHeadlessSwapChain(int width, int height,
{bool hasStencilBuffer = false}) async {
var flags = TSWAP_CHAIN_CONFIG_TRANSPARENT | TSWAP_CHAIN_CONFIG_READABLE;
if (hasStencilBuffer) {
flags |= TSWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER;
}
final swapChain = await withPointerCallback<TSwapChain>((cb) =>
Engine_createHeadlessSwapChainRenderThread(
this.engine, width, height, flags, cb));
return FFISwapChain(swapChain);
}
///
///
///
@override
Future<SwapChain> createSwapChain(Pointer window,
{bool hasStencilBuffer = false}) async {
var flags = TSWAP_CHAIN_CONFIG_TRANSPARENT | TSWAP_CHAIN_CONFIG_READABLE;
if (hasStencilBuffer) {
flags |= TSWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER;
}
final swapChain = await withPointerCallback<TSwapChain>((cb) =>
Engine_createSwapChainRenderThread(
this.engine, window.cast<Void>(), flags, cb));
return FFISwapChain(swapChain);
}
///
///
///
Future<View> createView() async {
final view = await FFIView(
await withPointerCallback<TView>(
(cb) => Engine_createViewRenderThread(engine, cb)),
this);
await view.setFrustumCullingEnabled(true);
View_setBlendMode(view.view, TBlendMode.TRANSLUCENT);
View_setShadowsEnabled(view.view, false);
View_setStencilBufferEnabled(view.view, false);
View_setAntiAliasing(view.view, false, false, false);
View_setDitheringEnabled(view.view, false);
View_setRenderQuality(view.view, TQualityLevel.MEDIUM);
return view;
}
Future<Scene> createScene() async {
return FFIScene(Engine_createScene(engine));
}
///
///
///
Future destroySwapChain(SwapChain swapChain) async {
_logger.info("Destroying swapchain");
await withVoidCallback((callback) {
Engine_destroySwapChainRenderThread(
engine, (swapChain as FFISwapChain).swapChain, callback);
});
_swapChains.remove(swapChain);
_logger.info("Destroyed swapchain");
}
///
///
///
@override
Future destroy() async {
for (final swapChain in _swapChains.keys) {
if (_swapChains[swapChain] == null) {
continue;
}
for (final view in _swapChains[swapChain]!) {
await view.setRenderable(false);
}
}
for (final swapChain in _swapChains.keys.toList()) {
await destroySwapChain(swapChain);
}
await withVoidCallback((cb) async {
Engine_destroyRenderThread(engine, cb);
});
RenderThread_destroy();
RenderTicker_destroy(renderTicker);
calloc.free(viewsPtr);
FilamentApp.instance = null;
for (final callback in _onDestroy) {
await callback.call();
}
_onDestroy.clear();
}
///
///
///
Future destroyAsset(covariant FFIAsset asset) async {
await withVoidCallback(
(cb) => SceneAsset_destroyRenderThread(asset.asset, cb));
}
///
///
///
Future<RenderTarget> createRenderTarget(int width, int height,
{covariant FFITexture? color, covariant FFITexture? depth}) async {
if (color == null) {
color = await createTexture(width, height,
flags: {
TextureUsage.TEXTURE_USAGE_SAMPLEABLE,
TextureUsage.TEXTURE_USAGE_COLOR_ATTACHMENT,
TextureUsage.TEXTURE_USAGE_BLIT_SRC
},
textureFormat: TextureFormat.RGBA8) as FFITexture;
}
if (depth == null) {
depth = await createTexture(width, height,
flags: {
TextureUsage.TEXTURE_USAGE_SAMPLEABLE,
TextureUsage.TEXTURE_USAGE_DEPTH_ATTACHMENT
},
textureFormat: TextureFormat.DEPTH32F) as FFITexture;
}
final renderTarget = await withPointerCallback<TRenderTarget>((cb) {
RenderTarget_createRenderThread(
engine, width, height, color!.pointer, depth!.pointer, cb);
});
if (renderTarget == nullptr) {
throw Exception("Failed to create RenderTarget");
}
return FFIRenderTarget(renderTarget, this);
}
///
///
///
Future<Texture> createTexture(int width, int height,
{int depth = 1,
int levels = 1,
Set<TextureUsage> flags = const {TextureUsage.TEXTURE_USAGE_SAMPLEABLE},
TextureSamplerType textureSamplerType = TextureSamplerType.SAMPLER_2D,
TextureFormat textureFormat = TextureFormat.RGBA16F,
int? importedTextureHandle}) async {
var bitmask = flags.fold(0, (a, b) => a | b.value);
final texturePtr = await withPointerCallback<TTexture>((cb) {
Texture_buildRenderThread(
engine,
width,
height,
depth,
levels,
bitmask,
importedTextureHandle ?? 0,
TTextureSamplerType.values[textureSamplerType.index],
TTextureFormat.values[textureFormat.index],
cb);
});
if (texturePtr == nullptr) {
throw Exception("Failed to create texture");
}
return FFITexture(
engine,
texturePtr,
);
}
///
///
///
Future<TextureSampler> createTextureSampler(
{TextureMinFilter minFilter = TextureMinFilter.LINEAR,
TextureMagFilter magFilter = TextureMagFilter.LINEAR,
TextureWrapMode wrapS = TextureWrapMode.CLAMP_TO_EDGE,
TextureWrapMode wrapT = TextureWrapMode.CLAMP_TO_EDGE,
TextureWrapMode wrapR = TextureWrapMode.CLAMP_TO_EDGE,
double anisotropy = 0.0,
TextureCompareMode compareMode = TextureCompareMode.NONE,
TextureCompareFunc compareFunc = TextureCompareFunc.LESS_EQUAL}) async {
final samplerPtr = TextureSampler_create();
TextureSampler_setMinFilter(
samplerPtr, TSamplerMinFilter.values[minFilter.index]);
TextureSampler_setMagFilter(
samplerPtr, TSamplerMagFilter.values[magFilter.index]);
TextureSampler_setWrapModeS(
samplerPtr, TSamplerWrapMode.values[wrapS.index]);
TextureSampler_setWrapModeT(
samplerPtr, TSamplerWrapMode.values[wrapT.index]);
TextureSampler_setWrapModeR(
samplerPtr, TSamplerWrapMode.values[wrapR.index]);
if (anisotropy > 0) {
TextureSampler_setAnisotropy(samplerPtr, anisotropy);
}
TextureSampler_setCompareMode(
samplerPtr,
TSamplerCompareMode.values[compareMode.index],
TSamplerCompareFunc.values[compareFunc.index]);
return FFITextureSampler(samplerPtr);
}
///
///
///
Future<LinearImage> decodeImage(Uint8List data) async {
final name = "image";
var ptr = Image_decode(
data.address,
data.length,
name.toNativeUtf8().cast<Char>(),
);
if (ptr == nullptr) {
throw Exception("Failed to decode image");
}
return FFILinearImage(ptr);
}
///
/// Creates an (empty) imge with the given dimensions.
///
Future<LinearImage> createImage(int width, int height, int channels) async {
final ptr = Image_createEmpty(width, height, channels);
return FFILinearImage(ptr);
}
///
///
///
Future<Material> createMaterial(Uint8List data) async {
var ptr = await withPointerCallback<TMaterial>((cb) {
Engine_buildMaterialRenderThread(engine, data.address, data.length, cb);
});
return FFIMaterial(ptr, this);
}
///
///
///
Future<MaterialInstance> createUbershaderMaterialInstance(
{bool doubleSided = false,
bool unlit = false,
bool hasVertexColors = false,
bool hasBaseColorTexture = false,
bool hasNormalTexture = false,
bool hasOcclusionTexture = false,
bool hasEmissiveTexture = false,
bool useSpecularGlossiness = false,
AlphaMode alphaMode = AlphaMode.OPAQUE,
bool enableDiagnostics = false,
bool hasMetallicRoughnessTexture = false,
int metallicRoughnessUV = 0,
int baseColorUV = 0,
bool hasClearCoatTexture = false,
int clearCoatUV = 0,
bool hasClearCoatRoughnessTexture = false,
int clearCoatRoughnessUV = 0,
bool hasClearCoatNormalTexture = false,
int clearCoatNormalUV = 0,
bool hasClearCoat = false,
bool hasTransmission = false,
bool hasTextureTransforms = false,
int emissiveUV = 0,
int aoUV = 0,
int normalUV = 0,
bool hasTransmissionTexture = false,
int transmissionUV = 0,
bool hasSheenColorTexture = false,
int sheenColorUV = 0,
bool hasSheenRoughnessTexture = false,
int sheenRoughnessUV = 0,
bool hasVolumeThicknessTexture = false,
int volumeThicknessUV = 0,
bool hasSheen = false,
bool hasIOR = false,
bool hasVolume = false}) async {
final key = Struct.create<TMaterialKey>();
key.doubleSided = doubleSided;
key.unlit = unlit;
key.hasVertexColors = hasVertexColors;
key.hasBaseColorTexture = hasBaseColorTexture;
key.hasNormalTexture = hasNormalTexture;
key.hasOcclusionTexture = hasOcclusionTexture;
key.hasEmissiveTexture = hasEmissiveTexture;
key.useSpecularGlossiness = useSpecularGlossiness;
key.alphaMode = alphaMode.index;
key.enableDiagnostics = enableDiagnostics;
key.unnamed.unnamed.hasMetallicRoughnessTexture =
hasMetallicRoughnessTexture;
key.unnamed.unnamed.metallicRoughnessUV = 0;
key.baseColorUV = baseColorUV;
key.hasClearCoatTexture = hasClearCoatTexture;
key.clearCoatUV = clearCoatUV;
key.hasClearCoatRoughnessTexture = hasClearCoatRoughnessTexture;
key.clearCoatRoughnessUV = clearCoatRoughnessUV;
key.hasClearCoatNormalTexture = hasClearCoatNormalTexture;
key.clearCoatNormalUV = clearCoatNormalUV;
key.hasClearCoat = hasClearCoat;
key.hasTransmission = hasTransmission;
key.hasTextureTransforms = hasTextureTransforms;
key.emissiveUV = emissiveUV;
key.aoUV = aoUV;
key.normalUV = normalUV;
key.hasTransmissionTexture = hasTransmissionTexture;
key.transmissionUV = transmissionUV;
key.hasSheenColorTexture = hasSheenColorTexture;
key.sheenColorUV = sheenColorUV;
key.hasSheenRoughnessTexture = hasSheenRoughnessTexture;
key.sheenRoughnessUV = sheenRoughnessUV;
key.hasVolumeThicknessTexture = hasVolumeThicknessTexture;
key.volumeThicknessUV = volumeThicknessUV;
key.hasSheen = hasSheen;
key.hasIOR = hasIOR;
key.hasVolume = hasVolume;
final materialInstance = await withPointerCallback<TMaterialInstance>((cb) {
MaterialProvider_createMaterialInstanceRenderThread(
ubershaderMaterialProvider, key.address, cb);
});
if (materialInstance == nullptr) {
throw Exception("Failed to create material instance");
}
var instance = FFIMaterialInstance(materialInstance, this);
return instance;
}
///
///
///
Future<FFIMaterialInstance> createUnlitMaterialInstance() async {
final instance = await createUbershaderMaterialInstance(unlit: true);
return instance as FFIMaterialInstance;
}
FFIMaterial? _gridMaterial;
Future<FFIMaterial> get gridMaterial async {
_gridMaterial ??= FFIMaterial(Material_createGridMaterial(engine), this);
return _gridMaterial!;
}
///
///
///
Future<MaterialInstance> getMaterialInstanceAt(
ThermionEntity entity, int index) async {
final instancePtr = RenderableManager_getMaterialInstanceAt(
renderableManager, entity, index);
final instance = FFIMaterialInstance(instancePtr, this);
return instance;
}
///
///
///
@override
Future render() async {
final swapchain = _swapChains.keys.first;
final view = _swapChains[swapchain]!.first;
await withBoolCallback((cb) {
Renderer_beginFrameRenderThread(renderer, swapchain.swapChain, 0, cb);
});
await withVoidCallback((cb) {
Renderer_renderRenderThread(
renderer,
view.view,
cb,
);
});
await withVoidCallback((cb) {
Renderer_endFrameRenderThread(renderer, cb);
});
}
///
///
///
@override
Future register(
covariant FFISwapChain swapChain, covariant FFIView view) async {
if (!_swapChains.containsKey(swapChain)) {
_swapChains[swapChain] = [];
}
_swapChains[swapChain]!.add(view);
_swapChains[swapChain]!
.sort((a, b) => a.renderOrder.compareTo(b.renderOrder));
await updateRenderOrder();
}
///
///
///
@override
Future unregister(
covariant FFISwapChain swapChain, covariant FFIView view) async {
if (!_swapChains.containsKey(swapChain)) {
_swapChains[swapChain] = [];
}
_swapChains[swapChain]!.remove(view);
_swapChains[swapChain]!
.sort((a, b) => a.renderOrder.compareTo(b.renderOrder));
await updateRenderOrder();
}
final _hooks = <Future Function()>[];
///
///
///
@override
Future registerRequestFrameHook(Future Function() hook) async {
if (!_hooks.contains(hook)) {
_hooks.add(hook);
}
}
///
///
///
@override
Future unregisterRequestFrameHook(Future Function() hook) async {
if (_hooks.contains(hook)) {
_hooks.remove(hook);
}
}
///
///
///
@override
Future requestFrame() async {
for (final hook in _hooks) {
await hook.call();
}
final completer = Completer();
final callback = NativeCallable<Void Function()>.listener(() {
completer.complete(true);
});
RenderThread_requestFrame(callback.nativeFunction.cast());
try {
await completer.future.timeout(Duration(seconds: 1));
} catch (err) {
print("WARNING - render call timed out");
}
}
///
///
///
@override
Future setParent(ThermionEntity child, ThermionEntity? parent,
{bool preserveScaling = false}) async {
TransformManager_setParent(transformManager, child,
parent ?? FILAMENT_ENTITY_NULL, preserveScaling);
}
///
///
///
@override
Future<ThermionEntity?> getParent(ThermionEntity child) async {
var parent = TransformManager_getParent(transformManager, child);
if (parent == FILAMENT_ASSET_ERROR) {
return null;
}
return parent;
}
///
///
///
@override
Future<ThermionEntity?> getAncestor(ThermionEntity child) async {
var parent = TransformManager_getAncestor(transformManager, child);
if (parent == FILAMENT_ASSET_ERROR) {
return null;
}
return parent;
}
///
///
///
@override
String? getNameForEntity(ThermionEntity entity) {
final result = NameComponentManager_getName(nameComponentManager, entity);
if (result == nullptr) {
return null;
}
return result.cast<Utf8>().toDartString();
}
Material? _imageMaterial;
///
///
///
@override
Future<MaterialInstance> createImageMaterialInstance() async {
if (_imageMaterial == null) {
var ptr = await withPointerCallback<TMaterial>(
(cb) => Material_createImageMaterialRenderThread(engine, cb));
_imageMaterial =
FFIMaterial(ptr, FilamentApp.instance! as FFIFilamentApp);
}
var instance =
await _imageMaterial!.createInstance() as FFIMaterialInstance;
return instance;
}
///
///
///
Future<List<(View, Uint8List)>> capture(covariant FFISwapChain? swapChain,
{covariant FFIView? view,
bool captureRenderTarget = false,
PixelDataFormat pixelDataFormat = PixelDataFormat.RGBA,
PixelDataType pixelDataType = PixelDataType.FLOAT,
Future Function(View)? beforeRender}) async {
if (swapChain == null) {
if (_swapChains.isEmpty) {
throw Exception("No swapchains registered");
}
if (_swapChains.length > 1) {
throw Exception(
"When multiple swapchains have been registered, you must pass the swapchain you wish to capture.");
}
swapChain = _swapChains.keys.first;
}
await updateRenderOrder();
await withBoolCallback((cb) {
Renderer_beginFrameRenderThread(renderer, swapChain!.swapChain, 0, cb);
});
final views = <FFIView>[];
if (view != null) {
views.add(view);
} else {
views.addAll(_swapChains[swapChain]!);
}
final pixelBuffers = <(View, Uint8List)>[];
for (final view in views) {
beforeRender?.call(view);
final viewport = await view.getViewport();
final pixelBuffer =
Uint8List(viewport.width * viewport.height * 4 * sizeOf<Float>());
await withVoidCallback((cb) {
Renderer_renderRenderThread(
renderer,
view.view,
cb,
);
});
if (captureRenderTarget && view.renderTarget == null) {
throw Exception();
}
await withVoidCallback((cb) {
Renderer_readPixelsRenderThread(
renderer,
view.view,
view.renderTarget == null
? nullptr
: view.renderTarget!.renderTarget,
// TPixelDataFormat.PIXELDATAFORMAT_RGBA,
// TPixelDataType.PIXELDATATYPE_UBYTE,
pixelDataFormat.value,
pixelDataType.value,
pixelBuffer.address,
pixelBuffer.length,
cb);
});
pixelBuffers.add((view, pixelBuffer));
}
await withVoidCallback((cb) {
Renderer_endFrameRenderThread(renderer, cb);
});
await withVoidCallback((cb) {
Engine_flushAndWaitRenderThead(engine, cb);
});
return pixelBuffers;
}
///
///
///
Future setClearOptions(double r, double g, double b, double a,
{int clearStencil = 0, bool discard = false, bool clear = true}) async {
Renderer_setClearOptions(
renderer, r, g, b, a, clearStencil, clear, discard);
}
///
///
///
Future<ThermionAsset> loadGltfFromBuffer(
Uint8List data, Pointer animationManager,
{int numInstances = 1,
bool keepData = false,
int priority = 4,
int layer = 0,
String? relativeResourcePath,
bool loadResourcesAsync = false}) async {
if (relativeResourcePath != null && !relativeResourcePath.endsWith("/")) {
relativeResourcePath = "$relativeResourcePath/";
}
var gltfResourceLoader = await withPointerCallback<TGltfResourceLoader>(
(cb) => GltfResourceLoader_createRenderThread(engine,
relativeResourcePath?.toNativeUtf8().cast<Char>() ?? nullptr, cb));
var filamentAsset = await withPointerCallback<TFilamentAsset>((cb) =>
GltfAssetLoader_loadRenderThread(engine, gltfAssetLoader, data.address,
data.length, numInstances, cb));
if (filamentAsset == nullptr) {
throw Exception("An error occurred loading the asset");
}
var resourceUris = FilamentAsset_getResourceUris(filamentAsset);
var resourceUriCount = FilamentAsset_getResourceUriCount(filamentAsset);
final resources = <FinalizableUint8List>[];
for (int i = 0; i < resourceUriCount; i++) {
final resourceUriDart = resourceUris[i].cast<Utf8>().toDartString();
final resourceData = await resourceLoader(relativeResourcePath == null
? resourceUriDart
: "$relativeResourcePath/$resourceUriDart");
resources.add(FinalizableUint8List(resourceUris[i], resourceData));
await withVoidCallback((cb) =>
GltfResourceLoader_addResourceDataRenderThread(
gltfResourceLoader,
resourceUris[i],
resourceData.address,
resourceData.lengthInBytes,
cb));
}
if (loadResourcesAsync) {
final result = await withBoolCallback((cb) => GltfResourceLoader_asyncBeginLoadRenderThread(gltfResourceLoader, filamentAsset, cb));
if(!result) {
throw Exception("Failed to begin async loading");
}
GltfResourceLoader_asyncUpdateLoadRenderThread(gltfResourceLoader);
var progress = await withFloatCallback((cb) => GltfResourceLoader_asyncGetLoadProgressRenderThread(gltfResourceLoader, cb));
while(progress < 1.0) {
GltfResourceLoader_asyncUpdateLoadRenderThread(gltfResourceLoader);
progress = await withFloatCallback((cb) => GltfResourceLoader_asyncGetLoadProgressRenderThread(gltfResourceLoader, cb));
}
} else {
final result = await withBoolCallback((cb) =>
GltfResourceLoader_loadResourcesRenderThread(
gltfResourceLoader, filamentAsset, cb));
if (!result) {
throw Exception("Failed to load resources");
}
}
final asset = await withPointerCallback<TSceneAsset>((cb) =>
SceneAsset_createFromFilamentAssetRenderThread(
engine, gltfAssetLoader, nameComponentManager, filamentAsset, cb));
await withVoidCallback((cb) =>
GltfResourceLoader_destroyRenderThread(engine, gltfResourceLoader, cb));
return FFIAsset(asset, this, animationManager.cast<TAnimationManager>());
}
Future destroyView(covariant FFIView view) async {
View_setColorGrading(view.view, nullptr);
for (final cg in view.colorGrading.entries) {
await withVoidCallback(
(cb) => Engine_destroyColorGradingRenderThread(engine, cg.value, cb));
}
await withVoidCallback(
(cb) => Engine_destroyViewRenderThread(engine, view.view, cb));
for(final swapchain in _swapChains.keys) {
if(_swapChains[swapchain]!.contains(view)) {
_swapChains[swapchain]!.remove(view);
continue;
}
}
}
Future destroyScene(covariant FFIScene scene) async {
await withVoidCallback(
(cb) => Engine_destroySceneRenderThread(engine, scene.scene, cb));
}
Future<Pointer<TColorGrading>> createColorGrading(ToneMapper mapper) async {
return withPointerCallback<TColorGrading>((cb) =>
ColorGrading_createRenderThread(
engine, TToneMapping.values[mapper.index], cb));
}
FFIMaterial? _gizmoMaterial;
///
///
///
Future<GizmoAsset> createGizmo(covariant FFIView view,
Pointer animationManager, GizmoType gizmoType) async {
if (_gizmoMaterial == null) {
final materialPtr = await withPointerCallback<TMaterial>((cb) {
Material_createGizmoMaterialRenderThread(engine, cb);
});
_gizmoMaterial ??= FFIMaterial(materialPtr, this);
}
var gltfResourceLoader = await withPointerCallback<TGltfResourceLoader>(
(cb) => GltfResourceLoader_createRenderThread(engine, nullptr, cb));
final gizmo = await withPointerCallback<TGizmo>((cb) {
Gizmo_createRenderThread(
engine,
gltfAssetLoader,
gltfResourceLoader,
nameComponentManager,
view.view,
_gizmoMaterial!.pointer,
TGizmoType.values[gizmoType.index],
cb);
});
if (gizmo == nullptr) {
throw Exception("Failed to create gizmo");
}
final gizmoEntityCount =
SceneAsset_getChildEntityCount(gizmo.cast<TSceneAsset>());
final gizmoEntities = Int32List(gizmoEntityCount);
SceneAsset_getChildEntities(
gizmo.cast<TSceneAsset>(), gizmoEntities.address);
return FFIGizmo(gizmo.cast<TSceneAsset>(), this,
animationManager.cast<TAnimationManager>(),
view: view,
entities: gizmoEntities.toSet()
..add(SceneAsset_getEntity(gizmo.cast<TSceneAsset>())));
}
///
///
///
@override
Future<ThermionAsset> createGeometry(
Geometry geometry, Pointer animationManager,
{List<MaterialInstance>? materialInstances,
bool keepData = false,
bool addToScene = true}) async {
var assetPtr = await withPointerCallback<TSceneAsset>((callback) {
var ptrList = Int64List(materialInstances?.length ?? 0);
if (materialInstances != null && materialInstances.isNotEmpty) {
ptrList.setRange(
0,
materialInstances.length,
materialInstances
.cast<FFIMaterialInstance>()
.map((mi) => mi.pointer.address)
.toList());
}
return SceneAsset_createGeometryRenderThread(
engine,
geometry.vertices.address,
geometry.vertices.length,
geometry.normals.address,
geometry.normals.length,
geometry.uvs.address,
geometry.uvs.length,
geometry.indices.address,
geometry.indices.length,
geometry.primitiveType.index,
ptrList.address.cast<Pointer<TMaterialInstance>>(),
ptrList.length,
callback);
});
if (assetPtr == nullptr) {
throw Exception("Failed to create geometry");
}
return FFIAsset(assetPtr, this, animationManager.cast<TAnimationManager>());
}
///
///
///
Future flush() async {
await withVoidCallback((cb) => Engine_flushAndWaitRenderThead(engine, cb));
}
final _onDestroy = <Future Function()>[];
///
///
///
void onDestroy(Future Function() callback) {
_onDestroy.add(callback);
}
}
class FinalizableUint8List implements Finalizable {
final Pointer name;
final Uint8List data;
FinalizableUint8List(this.name, this.data);
}

View File

@@ -1,74 +0,0 @@
import 'dart:async';
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart';
import 'package:thermion_dart/thermion_dart.dart';
import 'ffi_view.dart';
class FFIGizmo extends FFIAsset implements GizmoAsset {
final Set<ThermionEntity> entities;
late NativeCallable<GizmoPickCallbackFunction> _nativeCallback;
void Function(GizmoPickResultType axis, Vector3 coords)? _callback;
late FFIView view;
void _onPickResult(int resultType, double x, double y, double z) {
_callback?.call(GizmoPickResultType.values[resultType], Vector3(x, y, z));
}
bool isNonPickable(ThermionEntity entity) {
throw UnimplementedError();
// return SceneManager_isGridEntity(sceneManager, entity);
}
bool isGizmoEntity(ThermionEntity entity) => entities.contains(entity);
FFIGizmo(
super.asset,
super.app,
super.animationManager,
{
required this.view,
required this.entities,
}) {
_nativeCallback =
NativeCallable<GizmoPickCallbackFunction>.listener(_onPickResult);
}
@override
Future removeStencilHighlight() async {
throw Exception("Not supported for gizmo");
}
@override
Future setStencilHighlight(
{double r = 1.0,
double g = 0.0,
double b = 0.0,
int? entityIndex}) async {
throw Exception("Not supported for gizmo");
}
@override
Future pick(int x, int y,
{Future Function(GizmoPickResultType result, Vector3 coords)?
handler}) async {
_callback = handler;
final viewport = await view.getViewport();
y = viewport.height - y;
Gizmo_pick(asset.cast<TGizmo>(), x, y, _nativeCallback.nativeFunction);
}
@override
Future highlight(Axis axis) async {
Gizmo_unhighlight(asset.cast<TGizmo>());
Gizmo_highlight(asset.cast<TGizmo>(), TGizmoAxis.values[axis.index]);
}
@override
Future unhighlight() async {
Gizmo_unhighlight(asset.cast<TGizmo>());
}
}

View File

@@ -1,208 +0,0 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_texture.dart';
import 'package:thermion_dart/thermion_dart.dart';
class FFIMaterial extends Material {
final FFIFilamentApp app;
final Pointer<TMaterial> pointer;
FFIMaterial(this.pointer, this.app);
@override
Future<MaterialInstance> createInstance() async {
var ptr = await withPointerCallback<TMaterialInstance>((cb) {
Material_createInstanceRenderThread(pointer, cb);
});
return FFIMaterialInstance(ptr, this.app);
}
Future destroy() async {
await withVoidCallback((cb) {
Engine_destroyMaterialRenderThread(app.engine, pointer, cb);
});
}
@override
Future<bool> hasParameter(String propertyName) async {
return Material_hasParameter(
pointer, propertyName.toNativeUtf8().cast<Char>());
}
}
class FFIMaterialInstance extends MaterialInstance {
final Pointer<TMaterialInstance> pointer;
final FFIFilamentApp app;
FFIMaterialInstance(this.pointer, this.app) {
if (pointer == nullptr) {
throw Exception("MaterialInstance not found");
}
}
@override
Future setDepthCullingEnabled(bool enabled) async {
MaterialInstance_setDepthCulling(this.pointer, enabled);
}
@override
Future setDepthWriteEnabled(bool enabled) async {
MaterialInstance_setDepthWrite(this.pointer, enabled);
}
@override
Future setParameterFloat(String name, double value) async {
MaterialInstance_setParameterFloat(
pointer, name.toNativeUtf8().cast<Char>(), value);
}
@override
Future setParameterFloat2(String name, double x, double y) async {
MaterialInstance_setParameterFloat2(
pointer, name.toNativeUtf8().cast<Char>(), x, y);
}
@override
Future setParameterFloat3(String name, double x, double y, double z) async {
MaterialInstance_setParameterFloat3(
pointer, name.toNativeUtf8().cast<Char>(), x, y, z);
}
@override
Future setParameterFloat3Array(String name, List<Vector3> array) async {
final ptr = name.toNativeUtf8(allocator: calloc).cast<Char>();
final data = Float64List(array.length * 3);
int i = 0;
for (final item in array) {
data[i] = item.x;
data[i + 1] = item.y;
data[i + 2] = item.z;
i += 3;
}
MaterialInstance_setParameterFloat3Array(
pointer, ptr, data.address, array.length * 3);
calloc.free(ptr);
}
@override
Future setParameterFloat4(
String name, double x, double y, double z, double w) async {
MaterialInstance_setParameterFloat4(
pointer, name.toNativeUtf8().cast<Char>(), x, y, z, w);
}
@override
Future setParameterInt(String name, int value) async {
MaterialInstance_setParameterInt(
pointer, name.toNativeUtf8().cast<Char>(), value);
}
@override
Future setDepthFunc(SamplerCompareFunction depthFunc) async {
MaterialInstance_setDepthFunc(
pointer, TSamplerCompareFunc.values[depthFunc.index]);
}
@override
Future setStencilCompareFunction(SamplerCompareFunction func,
[StencilFace face = StencilFace.FRONT_AND_BACK]) async {
MaterialInstance_setStencilCompareFunction(
pointer,
TSamplerCompareFunc.values[func.index],
TStencilFace.values[face.index]);
}
@override
Future setStencilOpDepthFail(StencilOperation op,
[StencilFace face = StencilFace.FRONT_AND_BACK]) async {
MaterialInstance_setStencilOpDepthFail(pointer,
TStencilOperation.values[op.index], TStencilFace.values[face.index]);
}
@override
Future setStencilOpDepthStencilPass(StencilOperation op,
[StencilFace face = StencilFace.FRONT_AND_BACK]) async {
MaterialInstance_setStencilOpDepthStencilPass(pointer,
TStencilOperation.values[op.index], TStencilFace.values[face.index]);
}
@override
Future setStencilOpStencilFail(StencilOperation op,
[StencilFace face = StencilFace.FRONT_AND_BACK]) async {
MaterialInstance_setStencilOpStencilFail(pointer,
TStencilOperation.values[op.index], TStencilFace.values[face.index]);
}
@override
Future setStencilReferenceValue(int value,
[StencilFace face = StencilFace.FRONT_AND_BACK]) async {
MaterialInstance_setStencilReferenceValue(
pointer, value, TStencilFace.values[face.index]);
}
@override
Future setStencilWriteEnabled(bool enabled) async {
MaterialInstance_setStencilWrite(pointer, enabled);
}
@override
Future setCullingMode(CullingMode cullingMode) async {
MaterialInstance_setCullingMode(
pointer, TCullingMode.values[cullingMode.index]);
}
@override
Future<bool> isStencilWriteEnabled() async {
return MaterialInstance_isStencilWriteEnabled(pointer);
}
@override
Future setStencilReadMask(int mask) async {
MaterialInstance_setStencilReadMask(pointer, mask);
}
@override
Future setStencilWriteMask(int mask) async {
MaterialInstance_setStencilWriteMask(pointer, mask);
}
Future destroy() async {
await withVoidCallback((cb) {
Engine_destroyMaterialInstanceRenderThread(app.engine, this.pointer, cb);
});
}
@override
Future setTransparencyMode(TransparencyMode mode) async {
MaterialInstance_setTransparencyMode(
pointer, TTransparencyMode.values[mode.index]);
}
@override
Future setParameterTexture(String name, covariant FFITexture texture,
covariant FFITextureSampler sampler) async {
MaterialInstance_setParameterTexture(pointer,
name.toNativeUtf8().cast<Char>(), texture.pointer, sampler.pointer);
}
@override
Future setParameterBool(String name, bool value) async {
MaterialInstance_setParameterBool(
pointer, name.toNativeUtf8().cast<Char>(), value);
}
@override
Future setParameterMat4(String name, Matrix4 matrix) async {
final completer = Completer();
final func = () {
MaterialInstance_setParameterMat4(pointer, name.toNativeUtf8().cast<Char>(), matrix.storage.address);
completer.complete();
};
final nativeCallable = NativeCallable<Void Function()>.listener(func);
RenderThread_addTask(nativeCallable.nativeFunction);
await completer.future;
}
}

View File

@@ -1,28 +0,0 @@
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_texture.dart';
import 'package:thermion_dart/thermion_dart.dart';
class FFIRenderTarget extends RenderTarget {
final Pointer<TRenderTarget> renderTarget;
final FFIFilamentApp app;
FFIRenderTarget(this.renderTarget, this.app);
@override
Future<Texture> getColorTexture() async {
final ptr = RenderTarget_getColorTexture(renderTarget);
return FFITexture(app.engine, ptr);
}
@override
Future<Texture> getDepthTexture() async {
final ptr = RenderTarget_getDepthTexture(renderTarget);
return FFITexture(app.engine, ptr);
}
@override
Future destroy() async {
await withVoidCallback((cb) => RenderTarget_destroyRenderThread(app.engine, renderTarget, cb));
}
}

View File

@@ -1,19 +0,0 @@
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart';
import 'package:thermion_dart/src/filament/src/scene.dart';
import 'callbacks.dart';
class FFIScene extends Scene {
final Pointer<TScene> scene;
FFIScene(this.scene);
@override
Future add(covariant FFIAsset asset) async {
SceneAsset_addToScene(asset.asset, scene);
}
@override
Future remove(covariant FFIAsset asset) async {
SceneAsset_removeFromScene(asset.asset, scene);
}
}

View File

@@ -1,8 +0,0 @@
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
import 'package:thermion_dart/thermion_dart.dart';
class FFISwapChain extends SwapChain {
final Pointer<TSwapChain> swapChain;
FFISwapChain(this.swapChain);
}

View File

@@ -1,342 +0,0 @@
import 'dart:typed_data';
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
import 'package:thermion_dart/thermion_dart.dart';
class FFITexture extends Texture {
final Pointer<TEngine> _engine;
final Pointer<TTexture> pointer;
FFITexture(this._engine, this.pointer);
Future<void> setLinearImage(covariant FFILinearImage image,
PixelDataFormat format, PixelDataType type) async {
final result = await withBoolCallback((cb) {
Texture_loadImageRenderThread(
_engine,
pointer,
image.pointer,
format.index,
type.index,
cb);
});
if (!result) {
throw Exception("Failed to set linear image");
}
}
@override
Future<void> dispose() async {
await withVoidCallback((cb) {
Engine_destroyTextureRenderThread(_engine, pointer, cb);
});
}
@override
Future<void> generateMipmaps() {
// TODO: implement generateMipmaps
throw UnimplementedError();
}
@override
Future<int> getDepth([int level = 0]) async {
return Texture_getDepth(pointer, level);
}
@override
Future<TextureFormat> getFormat() {
// TODO: implement getFormat
throw UnimplementedError();
}
@override
Future<int> getHeight([int level = 0]) async {
return Texture_getHeight(pointer, level);
}
@override
Future<int> getLevels() {
// TODO: implement getLevels
throw UnimplementedError();
}
@override
Future<TextureSamplerType> getTarget() {
// TODO: implement getTarget
throw UnimplementedError();
}
@override
Future<int> getWidth([int level = 0]) async {
return Texture_getWidth(pointer, level);
}
@override
Future<void> setExternalImage(externalImage) {
// TODO: implement setExternalImage
throw UnimplementedError();
}
@override
Future<void> setImage(int level, Uint8List buffer, int width, int height,
int channels, PixelDataFormat format, PixelDataType type) async {
final success = await withBoolCallback((cb) {
Texture_setImageRenderThread(
_engine,
pointer,
level,
buffer.address,
buffer.lengthInBytes,
width,
height,
channels,
format.index,
type.index,
cb);
});
if (!success) {
throw Exception("Failed to set image");
}
}
@override
Future<void> setImage3D(
int level,
int xOffset,
int yOffset,
int zOffset,
int width,
int height,
int channels,
int depth,
Uint8List buffer,
PixelDataFormat format,
PixelDataType type) async {
final success = await withBoolCallback((cb) {
Texture_setImageWithDepthRenderThread(
_engine,
pointer,
level,
buffer.address,
buffer.lengthInBytes,
0,
0,
zOffset,
width,
height,
channels,
depth,
format.index,
type.index,
cb);
});
if (!success) {
throw Exception("Failed to set image");
}
}
@override
Future<void> setSubImage(
int level,
int xOffset,
int yOffset,
int width,
int height,
Uint8List buffer,
PixelDataFormat format,
PixelDataType type) {
// TODO: implement setSubImage
throw UnimplementedError();
}
}
class FFILinearImage extends LinearImage {
final Pointer<TLinearImage> pointer;
FFILinearImage(this.pointer);
static Future<FFILinearImage> createEmpty(
int width, int height, int channels) async {
final imagePtr = await withPointerCallback<TLinearImage>((cb) {
Image_createEmptyRenderThread(width, height, channels, cb);
});
return FFILinearImage(imagePtr);
}
static Future<FFILinearImage> decode(Uint8List data,
[String name = "image"]) async {
final namePtr = name.toNativeUtf8();
try {
final imagePtr = await withPointerCallback<TLinearImage>((cb) {
Image_decodeRenderThread(
data.address, data.lengthInBytes, namePtr.cast(), cb);
});
return FFILinearImage(imagePtr);
} finally {
calloc.free(namePtr);
}
}
Future<void> destroy() async {
await withVoidCallback((cb) {
Image_destroyRenderThread(this.pointer, cb);
});
}
@override
Future<int> getChannels() async {
return await withUInt32Callback((cb) {
Image_getChannelsRenderThread(pointer, cb);
});
}
@override
Future<int> getHeight() async {
return await withUInt32Callback((cb) {
Image_getHeightRenderThread(pointer, cb);
});
}
@override
Future<int> getWidth() async {
return await withUInt32Callback((cb) {
Image_getWidthRenderThread(pointer, cb);
});
}
@override
Future<Float32List> getData() async {
final height = await getHeight();
final width = await getWidth();
final channels = await getChannels();
final ptr = await withPointerCallback<Float>((cb) {
Image_getBytesRenderThread(pointer, cb);
});
return ptr.asTypedList(height * width * channels);
}
}
// Add these to access TextureSampler functionality:
class FFITextureSampler extends TextureSampler {
final Pointer<TTextureSampler> pointer;
FFITextureSampler(this.pointer);
static Future<FFITextureSampler> create() async {
final samplerPtr = await withPointerCallback<TTextureSampler>((cb) {
TextureSampler_createRenderThread(cb);
});
return FFITextureSampler(samplerPtr);
}
// static Future<FFITextureSampler> createWithFiltering(
// SamplerMinFilter minFilter,
// SamplerMagFilter magFilter,
// SamplerWrapMode wrapS,
// SamplerWrapMode wrapT,
// SamplerWrapMode wrapR) async {
// final samplerPtr = await withPointerCallback<TTextureSampler>((cb) {
// TextureSampler_createWithFilteringRenderThread(
// TSamplerMinFilter.values[minFilter.index],
// TSamplerMagFilter.values[magFilter.index],
// TSamplerWrapMode.values[wrapS.index],
// TSamplerWrapMode.values[wrapT.index],
// TSamplerWrapMode.values[wrapR.index],
// cb);
// });
// return FFITextureSampler(samplerPtr);
// }
// static Future<FFITextureSampler> createWithComparison(
// SamplerCompareMode compareMode,
// SamplerCompareFunc compareFunc) async {
// final samplerPtr = await withPointerCallback<TTextureSampler>((cb) {
// TextureSampler_createWithComparisonRenderThread(
// TSamplerCompareMode.values[compareMode.index],
// TTextureSamplerCompareFunc.values[compareFunc.index],
// cb);
// });
// return FFITextureSampler(samplerPtr);
// }
// Future<void> setMinFilter(SamplerMinFilter filter) async {
// await withVoidCallback((cb) {
// TextureSampler_setMinFilterRenderThread(
// pointer,
// TSamplerMinFilter.values[filter.index],
// cb);
// });
// }
// Future<void> setMagFilter(SamplerMagFilter filter) async {
// await withVoidCallback((cb) {
// TextureSampler_setMagFilterRenderThread(
// pointer,
// TSamplerMagFilter.values[filter.index],
// cb);
// });
// }
// Future<void> setWrapModeS(SamplerWrapMode mode) async {
// await withVoidCallback((cb) {
// TextureSampler_setWrapModeSRenderThread(
// pointer,
// TSamplerWrapMode.values[mode.index],
// cb);
// });
// }
// Future<void> setWrapModeT(SamplerWrapMode mode) async {
// await withVoidCallback((cb) {
// TextureSampler_setWrapModeTRenderThread(
// pointer,
// TSamplerWrapMode.values[mode.index],
// cb);
// });
// }
// Future<void> setWrapModeR(SamplerWrapMode mode) async {
// await withVoidCallback((cb) {
// TextureSampler_setWrapModeRRenderThread(
// pointer,
// TSamplerWrapMode.values[mode.index],
// cb);
// });
// }
Future<void> setAnisotropy(double anisotropy) async {
await withVoidCallback((cb) {
TextureSampler_setAnisotropyRenderThread(pointer, anisotropy, cb);
});
}
// Future<void> setCompareMode(
// SamplerCompareMode mode, SamplerCompareFunc func) async {
// await withVoidCallback((cb) {
// TextureSampler_setCompareModeRenderThread(
// pointer,
// TSamplerCompareMode.values[mode.index],
// TTextureSamplerCompareFunc.values[func.index],
// cb);
// });
// }
@override
Future dispose() async {
await withVoidCallback((cb) {
TextureSampler_destroyRenderThread(pointer, cb);
});
}
}

View File

@@ -1,215 +0,0 @@
import 'dart:async';
import 'dart:math';
import 'package:logging/logging.dart';
import 'package:thermion_dart/src/filament/src/scene.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_render_target.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_scene.dart';
import 'package:thermion_dart/thermion_dart.dart';
import 'callbacks.dart';
import 'ffi_camera.dart';
class FFIView extends View {
late final _logger = Logger(this.runtimeType.toString());
int _renderOrder = 0;
int get renderOrder => _renderOrder;
final Pointer<TView> view;
final FFIFilamentApp app;
bool _renderable = false;
bool get renderable => _renderable;
FFIRenderTarget? renderTarget;
FFIView(this.view, this.app) {
final renderTargetPtr = View_getRenderTarget(view);
if (renderTargetPtr != nullptr) {
renderTarget = FFIRenderTarget(renderTargetPtr, app);
}
_onPickResultCallable =
NativeCallable<PickCallbackFunction>.listener(_onPickResult);
}
///
///
///
Future setRenderOrder(int order) async {
this._renderOrder = order;
await FilamentApp.instance!.updateRenderOrder();
}
///
///
///
Future setRenderable(bool renderable) async {
this._renderable = renderable;
await FilamentApp.instance!.updateRenderOrder();
}
@override
Future setViewport(int width, int height) async {
View_setViewport(view, width, height);
}
Future<RenderTarget?> getRenderTarget() async {
return renderTarget;
}
@override
Future setRenderTarget(covariant FFIRenderTarget? renderTarget) async {
if (renderTarget != null) {
View_setRenderTarget(view, renderTarget.renderTarget);
this.renderTarget = renderTarget;
} else {
View_setRenderTarget(view, nullptr);
}
}
@override
Future setCamera(FFICamera camera) async {
View_setCamera(view, camera.camera);
}
@override
Future<Viewport> getViewport() async {
TViewport vp = View_getViewport(view);
return Viewport(vp.left, vp.bottom, vp.width, vp.height);
}
@override
Future<Camera> getCamera() async {
final cameraPtr = View_getCamera(view);
return FFICamera(cameraPtr, app);
}
@override
Future setAntiAliasing(bool msaa, bool fxaa, bool taa) async {
View_setAntiAliasing(view, msaa, fxaa, taa);
}
@override
Future setPostProcessing(bool enabled) async {
View_setPostProcessing(view, enabled);
}
@override
Future setFrustumCullingEnabled(bool enabled) async {
View_setFrustumCullingEnabled(view, enabled);
}
@override
Future setBloom(bool enabled, double strength) async {
await withVoidCallback((cb) {
View_setBloomRenderThread(view, enabled, strength, cb);
});
}
final colorGrading = <ToneMapper, Pointer<TColorGrading>>{};
@override
Future setToneMapper(ToneMapper mapper) async {
if (colorGrading[mapper] == null) {
colorGrading[mapper] =
await FilamentApp.instance!.createColorGrading(mapper);
if (colorGrading[mapper] == nullptr) {
throw Exception("Failed to create color grading");
}
}
View_setColorGrading(view, colorGrading[mapper]!);
}
Future setStencilBufferEnabled(bool enabled) async {
return View_setStencilBufferEnabled(view, enabled);
}
Future<bool> isStencilBufferEnabled() async {
return View_isStencilBufferEnabled(view);
}
Future setDithering(bool enabled) async {
View_setDitheringEnabled(view, enabled);
}
Future<bool> isDitheringEnabled() async {
return View_isDitheringEnabled(view);
}
@override
Future setRenderQuality(QualityLevel quality) async {
View_setRenderQuality(view, TQualityLevel.values[quality.index]);
}
Future setScene(covariant FFIScene scene) async {
View_setScene(view, scene.scene);
}
@override
Future setLayerVisibility(VisibilityLayers layer, bool visible) async {
View_setLayerEnabled(view, layer.value, visible);
}
Future setBlendMode(BlendMode blendMode) async {
View_setBlendMode(view, TBlendMode.values[blendMode.index]);
}
@override
Future<Scene> getScene() async {
final ptr = View_getScene(view);
return FFIScene(ptr);
}
int _pickRequestId = -1;
static int kMaxPickRequests = 1024;
final _pickRequests = List<({void Function(PickResult) handler, int x, int y})?>.generate(kMaxPickRequests, (idx) => null);
late NativeCallable<PickCallbackFunction> _onPickResultCallable;
///
///
///
@override
Future pick(int x, int y, void Function(PickResult) resultHandler) async {
_pickRequestId = max(_pickRequestId + 1, kMaxPickRequests);
_pickRequests[_pickRequestId % kMaxPickRequests] =
(handler: resultHandler, x: x, y: y);
var pickRequestId = _pickRequestId;
var viewport = await getViewport();
y = viewport.height - y;
View_pick(
view, pickRequestId, x, y, _onPickResultCallable.nativeFunction);
}
void _onPickResult(int requestId, ThermionEntity entityId, double depth,
double fragX, double fragY, double fragZ) async {
final modRequestId = requestId % kMaxPickRequests;
if (_pickRequests[modRequestId] == null) {
_logger.severe(
"Warning : pick result received with no matching request ID. This indicates you're clearing the pick cache too quickly");
return;
}
final (:handler, :x, :y) = _pickRequests[modRequestId]!;
_pickRequests[modRequestId] = null;
final viewport = await getViewport();
handler.call((
entity: entityId,
x: x,
y: y,
depth: depth,
fragX: fragX,
fragY: viewport.height - fragY,
fragZ: fragZ,
));
}
}

View File

@@ -1,14 +0,0 @@
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
class GridOverlay extends FFIAsset {
GridOverlay(super.asset, super.app, super.animationManager);
static Future<GridOverlay> create(FFIFilamentApp app, Pointer<TAnimationManager> animationManager) async {
final gridMaterial = await app.gridMaterial;
final asset = SceneAsset_createGrid(app.engine, gridMaterial.pointer);
return GridOverlay(asset, app, animationManager);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,20 +1,15 @@
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:thermion_dart/src/filament/src/light_options.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/background_image.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_scene.dart';
import 'package:thermion_dart/src/filament/src/layers.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/grid_overlay.dart';
import 'package:thermion_dart/src/filament/src/implementation/background_image.dart';
import '../../../../filament/src/implementation/ffi_asset.dart';
import 'package:thermion_dart/src/filament/src/implementation/ffi_filament_app.dart';
import '../../../../filament/src/implementation/ffi_scene.dart';
import '../../../../filament/src/implementation/grid_overlay.dart';
import 'package:thermion_dart/thermion_dart.dart';
import 'package:vector_math/vector_math_64.dart' as v64;
import 'package:logging/logging.dart';
import 'callbacks.dart';
import 'ffi_camera.dart';
import 'ffi_view.dart';
import '../../../../filament/src/implementation/ffi_camera.dart';
import '../../../../filament/src/implementation/ffi_view.dart';
const FILAMENT_ASSET_ERROR = 0;
@@ -50,6 +45,7 @@ class ThermionViewerFFI extends ThermionViewer {
///
///
Future setViewport(int width, int height) async {
print("Setting viewport to ${width}x${height}");
await view.setViewport(width.toInt(), height.toInt());
for (final camera in _cameras) {
@@ -121,8 +117,15 @@ class ThermionViewerFFI extends ThermionViewer {
///
@override
Future render() async {
await withVoidCallback(
(cb) => RenderTicker_renderRenderThread(app.renderTicker, 0, cb));
await withVoidCallback((cb) =>
RenderTicker_renderRenderThread(app.renderTicker, 0.toBigInt, cb));
if (FILAMENT_SINGLE_THREADED) {
await withVoidCallback(
(cb) => Engine_executeRenderThread(app.engine, cb));
} else {
await withVoidCallback(
(cb) => Engine_flushAndWaitRenderThread(app.engine, cb));
}
}
double _msPerFrame = 1000.0 / 60.0;
@@ -248,11 +251,21 @@ class ThermionViewerFFI extends ThermionViewer {
///
@override
Future loadIbl(String lightingPath, {double intensity = 30000}) async {
late Pointer stackPtr;
if (FILAMENT_WASM) {
//stackPtr = stackSave();
}
var data = await loadAssetFromUri(lightingPath);
indirectLight = await withPointerCallback<TIndirectLight>((cb) {
Engine_buildIndirectLightRenderThread(
app.engine, data.address, data.length, intensity, cb, nullptr);
});
if (FILAMENT_WASM) {
//stackRestore(stackPtr);
data.free();
}
data.free();
Scene_setIndirectLight(scene.scene, indirectLight!);
}
@@ -264,7 +277,18 @@ class ThermionViewerFFI extends ThermionViewer {
if (indirectLight == null) {
throw Exception("No IBL loaded");
}
late Pointer stackPtr;
if (FILAMENT_WASM) {
//stackPtr = stackSave();
}
IndirectLight_setRotation(indirectLight!, rotationMatrix.storage.address);
if (FILAMENT_WASM) {
//stackRestore(stackPtr);
rotationMatrix.storage.free();
}
}
///
@@ -303,8 +327,8 @@ class ThermionViewerFFI extends ThermionViewer {
///
@override
Future<ThermionEntity> addDirectLight(DirectLight directLight) async {
var entity = LightManager_createLight(app.engine, app.lightManager,
TLightType.values[directLight.type.index]);
var entity = LightManager_createLight(
app.engine, app.lightManager, directLight.type.index);
if (entity == FILAMENT_ASSET_ERROR) {
throw Exception("Failed to add light to scene");
}
@@ -446,8 +470,6 @@ class ThermionViewerFFI extends ThermionViewer {
@override
Future setPostProcessing(bool enabled) async {
View_setPostProcessing(view.view, enabled);
await withVoidCallback(
(cb) => Engine_flushAndWaitRenderThead(app.engine, cb));
}
///
@@ -478,10 +500,9 @@ class ThermionViewerFFI extends ThermionViewer {
///
@override
Future setAntiAliasing(bool msaa, bool fxaa, bool taa) async {
if (Platform.isWindows && msaa) {
if (!FILAMENT_SINGLE_THREADED && IS_WINDOWS && msaa) {
throw Exception("MSAA is not currently supported on Windows");
}
View_setAntiAliasing(view.view, msaa, fxaa, taa);
}

View File

@@ -1,7 +1,7 @@
import 'package:thermion_dart/src/filament/src/layers.dart';
import 'package:thermion_dart/src/filament/src/light_options.dart';
import 'package:thermion_dart/src/filament/src/interface/layers.dart';
import 'package:thermion_dart/src/filament/src/interface/light_options.dart';
import '../../filament/src/shared_types.dart';
import '../../filament/src/interface/shared_types.dart';
import 'dart:typed_data';
import 'package:vector_math/vector_math_64.dart';
import 'dart:async';

View File

@@ -1,317 +0,0 @@
import 'dart:typed_data';
import 'package:thermion_dart/src/filament/src/light_options.dart';
import 'package:thermion_dart/thermion_dart.dart';
class ThermionViewerStub extends ThermionViewer {
@override
Future<ThermionEntity> addDirectLight(DirectLight light) {
// TODO: implement addDirectLight
throw UnimplementedError();
}
@override
Future addToScene(covariant ThermionAsset asset) {
// TODO: implement addToScene
throw UnimplementedError();
}
@override
Future clearBackgroundImage({bool destroy = false}) {
// TODO: implement clearBackgroundImage
throw UnimplementedError();
}
@override
Future<Camera> createCamera() {
// TODO: implement createCamera
throw UnimplementedError();
}
@override
Future<ThermionAsset> createGeometry(Geometry geometry,
{List<MaterialInstance>? materialInstances,
bool keepData = false,
bool addToScene = true}) {
// TODO: implement createGeometry
throw UnimplementedError();
}
@override
Future destroyAsset(ThermionAsset asset) {
// TODO: implement destroyAsset
throw UnimplementedError();
}
@override
Future destroyAssets() {
// TODO: implement destroyAssets
throw UnimplementedError();
}
@override
Future destroyCamera(covariant Camera camera) {
// TODO: implement destroyCamera
throw UnimplementedError();
}
@override
Future destroyLights() {
// TODO: implement destroyLights
throw UnimplementedError();
}
@override
Future dispose() {
// TODO: implement dispose
throw UnimplementedError();
}
@override
Future<Camera> getActiveCamera() {
// TODO: implement getActiveCamera
throw UnimplementedError();
}
@override
int getCameraCount() {
// TODO: implement getCameraCount
throw UnimplementedError();
}
@override
Future<GizmoAsset> getGizmo(GizmoType type) {
// TODO: implement getGizmo
throw UnimplementedError();
}
@override
Future<Aabb3> getRenderableBoundingBox(ThermionEntity entity) {
// TODO: implement getRenderableBoundingBox
throw UnimplementedError();
}
@override
Future<Aabb2> getViewportBoundingBox(ThermionEntity entity) {
// TODO: implement getViewportBoundingBox
throw UnimplementedError();
}
@override
// TODO: implement initialized
Future<bool> get initialized => throw UnimplementedError();
@override
Future<ThermionAsset> loadGltf(String path,
{bool addToScene = true,
int numInstances = 1,
bool keepData = false,
String? relativeResourcePath}) {
// TODO: implement loadGltf
throw UnimplementedError();
}
@override
Future loadIbl(String lightingPath, {double intensity = 30000}) {
// TODO: implement loadIbl
throw UnimplementedError();
}
@override
Future loadSkybox(String skyboxPath) {
// TODO: implement loadSkybox
throw UnimplementedError();
}
@override
// TODO: implement msPerFrame
double get msPerFrame => throw UnimplementedError();
@override
void onDispose(Future Function() callback) {
// TODO: implement onDispose
}
@override
Future pick(int x, int y, void Function(PickResult p1) resultHandler) {
// TODO: implement pick
throw UnimplementedError();
}
@override
Future removeFromScene(covariant ThermionAsset asset) {
// TODO: implement removeFromScene
throw UnimplementedError();
}
@override
Future removeIbl() {
// TODO: implement removeIbl
throw UnimplementedError();
}
@override
Future removeLight(ThermionEntity light) {
// TODO: implement removeLight
throw UnimplementedError();
}
@override
Future removeSkybox() {
// TODO: implement removeSkybox
throw UnimplementedError();
}
@override
Future render() {
// TODO: implement render
throw UnimplementedError();
}
@override
// TODO: implement rendering
bool get rendering => throw UnimplementedError();
@override
Future rotateIbl(Matrix3 rotation) {
// TODO: implement rotateIbl
throw UnimplementedError();
}
@override
Future setActiveCamera(covariant Camera camera) {
// TODO: implement setActiveCamera
throw UnimplementedError();
}
@override
Future setAntiAliasing(bool msaa, bool fxaa, bool taa) {
// TODO: implement setAntiAliasing
throw UnimplementedError();
}
@override
Future setBackgroundColor(double r, double g, double b, double alpha) {
// TODO: implement setBackgroundColor
throw UnimplementedError();
}
@override
Future setBackgroundImage(String path, {bool fillHeight = false}) {
// TODO: implement setBackgroundImage
throw UnimplementedError();
}
@override
Future setBackgroundImagePosition(double x, double y, {bool clamp = false}) {
// TODO: implement setBackgroundImagePosition
throw UnimplementedError();
}
@override
Future setBloom(bool enabled, double strength) {
// TODO: implement setBloom
throw UnimplementedError();
}
@override
Future setFrameRate(int framerate) {
// TODO: implement setFrameRate
throw UnimplementedError();
}
@override
Future setGridOverlayVisibility(bool visible) {
// TODO: implement setGridOverlayVisibility
throw UnimplementedError();
}
@override
Future setLightDirection(ThermionEntity lightEntity, Vector3 direction) {
// TODO: implement setLightDirection
throw UnimplementedError();
}
@override
Future setLightPosition(
ThermionEntity lightEntity, double x, double y, double z) {
// TODO: implement setLightPosition
throw UnimplementedError();
}
@override
Future setPostProcessing(bool enabled) {
// TODO: implement setPostProcessing
throw UnimplementedError();
}
@override
Future setPriority(ThermionEntity entityId, int priority) {
// TODO: implement setPriority
throw UnimplementedError();
}
@override
Future setRendering(bool render) {
// TODO: implement setRendering
throw UnimplementedError();
}
@override
Future setShadowType(ShadowType shadowType) {
// TODO: implement setShadowType
throw UnimplementedError();
}
@override
Future setShadowsEnabled(bool enabled) {
// TODO: implement setShadowsEnabled
throw UnimplementedError();
}
@override
Future setSoftShadowOptions(double penumbraScale, double penumbraRatioScale) {
// TODO: implement setSoftShadowOptions
throw UnimplementedError();
}
@override
Future setToneMapping(ToneMapper mapper) {
// TODO: implement setToneMapping
throw UnimplementedError();
}
@override
Future setViewFrustumCulling(bool enabled) {
// TODO: implement setViewFrustumCulling
throw UnimplementedError();
}
@override
Future setViewport(int width, int height) {
// TODO: implement setViewport
throw UnimplementedError();
}
@override
// TODO: implement view
View get view => throw UnimplementedError();
@override
Future setLayerVisibility(VisibilityLayers layer, bool visible) {
// TODO: implement setLayerVisibility
throw UnimplementedError();
}
@override
Future<ThermionAsset> loadGltfFromBuffer(Uint8List data,
{
String? relativeResourcePath,
int numInstances = 1,
bool keepData = false,
int priority = 4,
int layer = 0,
bool loadResourcesAsync = false,
}) {
throw UnimplementedError();
}
}

View File

@@ -3,7 +3,7 @@ library thermion_flutter_js;
import 'dart:js_interop';
import '../../../../filament/src/shared_types.dart';
import '../../../../filament/src/interface/shared_types.dart';
///
/// An extension type on [JSObject] that represents a

View File

@@ -1,77 +0,0 @@
import 'package:vector_math/vector_math_64.dart';
import '../../thermion_viewer_base.dart';
class ThermionWasmCamera extends Camera {
final int pointer;
ThermionWasmCamera(this.pointer);
@override
Future setProjectionMatrixWithCulling(
Matrix4 projectionMatrix, double near, double far) {
// TODO: implement setProjectionMatrixWithCulling
throw UnimplementedError();
}
@override
Future<Matrix4> getModelMatrix() {
// TODO: implement getModelMatrix
throw UnimplementedError();
}
@override
Future setLensProjection({double near = kNear, double far = kFar, double aspect = 1.0, double focalLength = kFocalLength}) {
// TODO: implement setLensProjection
throw UnimplementedError();
}
@override
Future setTransform(Matrix4 transform) {
// TODO: implement setTransform
throw UnimplementedError();
}
@override
ThermionEntity getEntity() {
// TODO: implement getEntity
throw UnimplementedError();
}
@override
Future setModelMatrix(Matrix4 matrix) {
// TODO: implement setModelMatrix
throw UnimplementedError();
}
@override
Future<double> getCullingFar() {
// TODO: implement getCullingFar
throw UnimplementedError();
}
@override
Future<double> getFocalLength() {
// TODO: implement getFocalLength
throw UnimplementedError();
}
@override
Future<double> getNear() {
// TODO: implement getNear
throw UnimplementedError();
}
@override
Future<Matrix4> getViewMatrix() {
// TODO: implement getViewMatrix
throw UnimplementedError();
}
@override
Future setProjection(Projection projection, double left, double right, double bottom, double top, double near, double far) {
// TODO: implement setProjection
throw UnimplementedError();
}
}

View File

@@ -1,147 +0,0 @@
import 'package:vector_math/vector_math_64.dart';
import '../../../viewer.dart';
class ThermionWasmMaterialInstance extends MaterialInstance {
final int pointer;
ThermionWasmMaterialInstance(this.pointer);
@override
Future setParameterFloat2(String name, double x, double y) {
// TODO: implement setParameterFloat2
throw UnimplementedError();
}
@override
Future setParameterFloat(String name, double x) {
// TODO: implement setParameterFloat
throw UnimplementedError();
}
@override
Future setDepthFunc(SamplerCompareFunction depthFunc) {
// TODO: implement setDepthFunc
throw UnimplementedError();
}
@override
Future setParameterFloat4(String name, double x, double y, double z, double w) {
// TODO: implement setParameterFloat4
throw UnimplementedError();
}
@override
Future setParameterInt(String name, int value) {
// TODO: implement setParameterInt
throw UnimplementedError();
}
@override
Future setDepthCullingEnabled(enabled) {
// TODO: implement setDepthCullingEnabled
throw UnimplementedError();
}
@override
Future setDepthWriteEnabled(enabled) {
// TODO: implement setDepthWriteEnabled
throw UnimplementedError();
}
@override
Future setStencilCompareFunction(SamplerCompareFunction func, [StencilFace face = StencilFace.FRONT_AND_BACK]) {
// TODO: implement setStencilCompareFunction
throw UnimplementedError();
}
@override
Future setStencilOpDepthFail(StencilOperation op, [StencilFace face = StencilFace.FRONT_AND_BACK]) {
// TODO: implement setStencilOpDepthFail
throw UnimplementedError();
}
@override
Future setStencilOpDepthStencilPass(StencilOperation op, [StencilFace face = StencilFace.FRONT_AND_BACK]) {
// TODO: implement setStencilOpDepthStencilPass
throw UnimplementedError();
}
@override
Future setStencilOpStencilFail(StencilOperation op, [StencilFace face = StencilFace.FRONT_AND_BACK]) {
// TODO: implement setStencilOpStencilFail
throw UnimplementedError();
}
@override
Future setStencilReferenceValue(int value, [StencilFace face = StencilFace.FRONT_AND_BACK]) {
// TODO: implement setStencilReferenceValue
throw UnimplementedError();
}
@override
Future<bool> isStencilWriteEnabled() {
// TODO: implement isStencilWriteEnabled
throw UnimplementedError();
}
@override
Future setCullingMode(CullingMode cullingMode) {
// TODO: implement setCullingMode
throw UnimplementedError();
}
@override
Future setStencilWriteEnabled(bool enabled) {
// TODO: implement setStencilWriteEnabled
throw UnimplementedError();
}
@override
Future dispose() {
// TODO: implement dispose
throw UnimplementedError();
}
@override
Future setParameterBool(String name, bool value) {
// TODO: implement setParameterBool
throw UnimplementedError();
}
@override
Future setParameterFloat3(String name, double x, double y, double z) {
// TODO: implement setParameterFloat3
throw UnimplementedError();
}
@override
Future setParameterFloat3Array(String name, List<Vector3> data) {
// TODO: implement setParameterFloat3Array
throw UnimplementedError();
}
@override
Future setParameterTexture(String name, covariant Texture texture, covariant TextureSampler sampler) {
// TODO: implement setParameterTexture
throw UnimplementedError();
}
@override
Future setStencilReadMask(int mask) {
// TODO: implement setStencilReadMask
throw UnimplementedError();
}
@override
Future setStencilWriteMask(int mask) {
// TODO: implement setStencilWriteMask
throw UnimplementedError();
}
@override
Future setTransparencyMode(TransparencyMode mode) {
// TODO: implement setTransparencyMode
throw UnimplementedError();
}
}

View File

@@ -19,29 +19,29 @@
// import 'camera.dart';
// import 'material_instance.dart';
// extension type _EmscriptenModule(JSObject _) implements JSObject {
// external JSAny? ccall(String name, String returnType,
// JSArray<JSString> argTypes, JSArray<JSAny?> args, JSAny? opts);
extension type _EmscriptenModule(JSObject _) implements JSObject {
external JSAny? ccall(String name, String returnType,
JSArray<JSString> argTypes, JSArray<JSAny?> args, JSAny? opts);
// external JSNumber _malloc(int numBytes);
// external void _free(JSNumber addr);
// external JSNumber stackAlloc(int numBytes);
external JSNumber _malloc(int numBytes);
external void _free(JSNumber addr);
external JSNumber stackAlloc(int numBytes);
// external JSAny getValue(JSNumber addr, String llvmType);
// external void setValue(JSNumber addr, JSNumber value, String llvmType);
external JSAny getValue(JSNumber addr, String llvmType);
external void setValue(JSNumber addr, JSNumber value, String llvmType);
// external JSString intArrayToString(JSAny ptr);
// external JSString UTF8ToString(JSAny ptr);
// external void stringToUTF8(
// JSString str, JSNumber ptr, JSNumber maxBytesToWrite);
// external void writeArrayToMemory(JSUint8Array data, JSNumber ptr);
external JSString intArrayToString(JSAny ptr);
external JSString UTF8ToString(JSAny ptr);
external void stringToUTF8(
JSString str, JSNumber ptr, JSNumber maxBytesToWrite);
external void writeArrayToMemory(JSUint8Array data, JSNumber ptr);
// external JSNumber addFunction(JSFunction f, String signature);
// external void removeFunction(JSNumber f);
// external JSAny get ALLOC_STACK;
// external JSAny get HEAPU32;
// external JSAny get HEAP32;
// }
external JSNumber addFunction(JSFunction f, String signature);
external void removeFunction(JSNumber f);
external JSAny get ALLOC_STACK;
external JSAny get HEAPU32;
external JSAny get HEAP32;
}
// typedef ThermionViewerImpl = ThermionViewerWasm;
@@ -80,7 +80,6 @@
// _module = module as _EmscriptenModule;
// }
// }
// void _setAssetPathPrefix(String assetPathPrefix) {
// _module!.ccall(
// "thermion_dart_web_set_asset_path_prefix",

View File

@@ -1,9 +1,6 @@
library thermion_viewer;
export 'src/thermion_viewer_base.dart';
export '../filament/src/filament_app.dart';
export 'src/thermion_viewer_stub.dart'
if (dart.library.io) 'src/ffi/thermion_viewer_ffi.dart'
if (dart.library.js_interop) 'src/web_wasm/thermion_viewer_web_wasm.dart';
export '../filament/src/shared_types.dart';
export '../filament/src/interface/filament_app.dart';
export 'src/ffi/src/thermion_viewer_ffi.dart';
export '../filament/src/interface/shared_types.dart';