feat! js_interop improvements
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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>());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user