feat! js_interop improvements
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
export 'src/filament_app.dart';
|
||||
export 'src/engine.dart';
|
||||
export 'src/layers.dart';
|
||||
export 'src/light_options.dart';
|
||||
export 'src/interface/filament_app.dart';
|
||||
export 'src/interface/engine.dart';
|
||||
export 'src/interface/layers.dart';
|
||||
export 'src/interface/light_options.dart';
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import '../../viewer/viewer.dart';
|
||||
|
||||
class Geometry {
|
||||
final Float32List vertices;
|
||||
final Uint16List indices;
|
||||
final Float32List normals;
|
||||
final Float32List uvs;
|
||||
final PrimitiveType primitiveType;
|
||||
|
||||
Geometry(
|
||||
this.vertices,
|
||||
List<int> indices, {
|
||||
Float32List? normals,
|
||||
Float32List? uvs,
|
||||
this.primitiveType = PrimitiveType.TRIANGLES,
|
||||
}) : indices = Uint16List.fromList(indices),
|
||||
normals = normals ?? Float32List(0),
|
||||
uvs = uvs ?? Float32List(0) {
|
||||
assert(this.uvs.length == 0 || this.uvs.length == (vertices.length ~/ 3 * 2), "Expected either zero or ${indices.length * 2} UVs, got ${this.uvs.length}");
|
||||
}
|
||||
|
||||
void scale(double factor) {
|
||||
for (int i = 0; i < vertices.length; i++) {
|
||||
vertices[i] = vertices[i] * factor;
|
||||
}
|
||||
}
|
||||
|
||||
bool get hasNormals => normals.isNotEmpty;
|
||||
bool get hasUVs => uvs.isNotEmpty;
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
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/interface/layers.dart';
|
||||
import 'package:thermion_dart/src/bindings/bindings.dart';
|
||||
import 'package:thermion_dart/src/filament/src/implementation/ffi_asset.dart';
|
||||
import 'package:thermion_dart/src/filament/src/implementation/ffi_scene.dart';
|
||||
import 'package:thermion_dart/src/filament/src/implementation/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);
|
||||
var transform = Matrix4.identity();
|
||||
|
||||
await imageMaterialInstance.setParameterMat4(
|
||||
"transform", transform);
|
||||
|
||||
await 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();
|
||||
}
|
||||
}
|
||||
978
thermion_dart/lib/src/filament/src/implementation/ffi_asset.dart
Normal file
978
thermion_dart/lib/src/filament/src/implementation/ffi_asset.dart
Normal file
@@ -0,0 +1,978 @@
|
||||
import 'package:animation_tools_dart/animation_tools_dart.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:thermion_dart/src/utils/src/matrix.dart';
|
||||
import 'package:thermion_dart/src/filament/src/implementation/ffi_filament_app.dart';
|
||||
import 'package:thermion_dart/src/filament/src/implementation/ffi_material.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);
|
||||
}
|
||||
|
||||
Int32List? _childEntities;
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
@override
|
||||
Future<List<ThermionEntity>> getChildEntities() async {
|
||||
if (_childEntities == null) {
|
||||
var count = SceneAsset_getChildEntityCount(asset);
|
||||
var childEntities = Int32List(count);
|
||||
late Pointer stackPtr;
|
||||
if (FILAMENT_WASM) {
|
||||
//stackPtr = stackSave();
|
||||
}
|
||||
if (count > 0) {
|
||||
SceneAsset_getChildEntities(asset, childEntities.address);
|
||||
}
|
||||
_childEntities = Int32List.fromList(childEntities);
|
||||
childEntities.free();
|
||||
if (FILAMENT_WASM) {
|
||||
//stackRestore(stackPtr);
|
||||
}
|
||||
}
|
||||
|
||||
return _childEntities!;
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
@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 ptrList = IntPtrList(materialInstances?.length ?? 0);
|
||||
late Pointer stackPtr;
|
||||
if (FILAMENT_WASM) {
|
||||
//stackPtr = stackSave();
|
||||
}
|
||||
|
||||
if (materialInstances != null && materialInstances.isNotEmpty) {
|
||||
ptrList.setRange(
|
||||
0,
|
||||
materialInstances.length,
|
||||
materialInstances
|
||||
.cast<FFIMaterialInstance>()
|
||||
.map((mi) => mi.pointer.address)
|
||||
.toList());
|
||||
}
|
||||
|
||||
var created = await withPointerCallback<TSceneAsset>((cb) {
|
||||
SceneAsset_createInstanceRenderThread(
|
||||
asset, ptrList.address.cast(), materialInstances?.length ?? 0, cb);
|
||||
});
|
||||
|
||||
if (FILAMENT_WASM) {
|
||||
//stackRestore(stackPtr);
|
||||
ptrList.free();
|
||||
}
|
||||
|
||||
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 dispose() async {
|
||||
_childEntities?.free();
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
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 = Uint16List.fromList([
|
||||
// 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);
|
||||
geometry.uvs?.free();
|
||||
geometry.normals?.free();
|
||||
geometry.vertices.free();
|
||||
geometry.indices.free();
|
||||
}
|
||||
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 = allocate<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);
|
||||
});
|
||||
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 = allocate<Char>(255);
|
||||
for (int i = 0; i < count; i++) {
|
||||
AnimationManager_getMorphTargetName(
|
||||
animationManager, asset, entity, outPtr, i);
|
||||
names.add(outPtr.cast<Utf8>().toDartString());
|
||||
}
|
||||
free(outPtr);
|
||||
return names.cast<String>();
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
Future<List<String>> getBoneNames({int skinIndex = 0}) async {
|
||||
var count =
|
||||
AnimationManager_getBoneCount(animationManager, asset, skinIndex);
|
||||
var out = allocate<PointerClass<Char>>(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
out[i] = allocate<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());
|
||||
}
|
||||
for (int i = 0; i < count; i++) {
|
||||
free(out[i]);
|
||||
}
|
||||
free(out);
|
||||
return names;
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
@override
|
||||
Future<List<String>> getAnimationNames() async {
|
||||
var animationCount =
|
||||
AnimationManager_getAnimationCount(animationManager, asset);
|
||||
var names = <String>[];
|
||||
var outPtr = allocate<Char>(255);
|
||||
for (int i = 0; i < animationCount; i++) {
|
||||
AnimationManager_getAnimationName(animationManager, asset, outPtr, i);
|
||||
names.add(outPtr.cast<Utf8>().toDartString());
|
||||
}
|
||||
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);
|
||||
|
||||
frameData.data.free();
|
||||
indices.free();
|
||||
|
||||
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 = allocate<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));
|
||||
}
|
||||
free(restLocalTransformsRaw);
|
||||
|
||||
var numFrames = animation.frameData.length;
|
||||
|
||||
var data = allocate<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[(frameNum * 16) + j] = newLocalTransform.storage[j];
|
||||
}
|
||||
}
|
||||
|
||||
AnimationManager_addBoneAnimation(
|
||||
animationManager,
|
||||
asset,
|
||||
skinIndex,
|
||||
entityBoneIndex,
|
||||
data,
|
||||
numFrames,
|
||||
animation.frameLengthInMs,
|
||||
fadeOutInSecs,
|
||||
fadeInInSecs,
|
||||
maxDelta);
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
Future<Matrix4> getLocalTransform({ThermionEntity? entity}) async {
|
||||
late Pointer stackPtr;
|
||||
if (FILAMENT_WASM) {
|
||||
//stackPtr = stackSave();
|
||||
}
|
||||
entity ??= this.entity;
|
||||
final transform = double4x4ToMatrix4(
|
||||
TransformManager_getLocalTransform(app.transformManager, entity));
|
||||
if (FILAMENT_WASM) {
|
||||
//stackRestore(stackPtr);
|
||||
}
|
||||
return transform;
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
Future<Matrix4> getWorldTransform({ThermionEntity? entity}) async {
|
||||
late Pointer stackPtr;
|
||||
if (FILAMENT_WASM) {
|
||||
//stackPtr = stackSave();
|
||||
}
|
||||
entity ??= this.entity;
|
||||
var transform = double4x4ToMatrix4(
|
||||
TransformManager_getWorldTransform(app.transformManager, entity));
|
||||
if (FILAMENT_WASM) {
|
||||
//stackRestore(stackPtr);
|
||||
}
|
||||
return transform;
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
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 {
|
||||
late Pointer stackPtr;
|
||||
if (FILAMENT_WASM) {
|
||||
//stackPtr = stackSave();
|
||||
}
|
||||
var matrixIn = Float32List(16);
|
||||
AnimationManager_getInverseBindMatrix(
|
||||
animationManager, asset, skinIndex, boneIndex, matrixIn.address);
|
||||
var matrixOut = Matrix4.fromList(matrixIn);
|
||||
if (FILAMENT_WASM) {
|
||||
//stackRestore(stackPtr);
|
||||
matrixIn.free();
|
||||
}
|
||||
return matrixOut;
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
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 = allocate<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);
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
import 'package:thermion_dart/src/filament/src/implementation/ffi_filament_app.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 {
|
||||
late Pointer stackPtr;
|
||||
if (FILAMENT_WASM) {
|
||||
stackPtr = stackSave();
|
||||
}
|
||||
final modelMatrix = double4x4ToMatrix4(Camera_getModelMatrix(camera));
|
||||
if (FILAMENT_WASM) {
|
||||
stackRestore(stackPtr);
|
||||
}
|
||||
return modelMatrix;
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
@override
|
||||
Future<Matrix4> getProjectionMatrix() async {
|
||||
late Pointer stackPtr;
|
||||
if (FILAMENT_WASM) {
|
||||
//stackPtr = stackSave();
|
||||
}
|
||||
var matrixStruct = Camera_getProjectionMatrix(camera);
|
||||
final pMat = double4x4ToMatrix4(matrixStruct);
|
||||
if (FILAMENT_WASM) {
|
||||
//stackRestore(stackPtr);
|
||||
}
|
||||
return pMat;
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
@override
|
||||
Future<Matrix4> getCullingProjectionMatrix() async {
|
||||
late Pointer stackPtr;
|
||||
if (FILAMENT_WASM) {
|
||||
//stackPtr = stackSave();
|
||||
}
|
||||
var matrixStruct = Camera_getCullingProjectionMatrix(camera);
|
||||
final cpMat = double4x4ToMatrix4(matrixStruct);
|
||||
|
||||
if (FILAMENT_WASM) {
|
||||
//stackRestore(stackPtr);
|
||||
}
|
||||
return cpMat;
|
||||
}
|
||||
|
||||
@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 {
|
||||
late Pointer stackPtr;
|
||||
if (FILAMENT_WASM) {
|
||||
stackPtr = stackSave();
|
||||
}
|
||||
Camera_setModelMatrix(camera, matrix.storage.address);
|
||||
if (FILAMENT_WASM) {
|
||||
stackRestore(stackPtr);
|
||||
matrix.storage.free();
|
||||
}
|
||||
}
|
||||
|
||||
@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 {
|
||||
late Pointer stackPtr;
|
||||
if (FILAMENT_WASM) {
|
||||
//stackPtr = stackSave();
|
||||
}
|
||||
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]);
|
||||
if (FILAMENT_WASM) {
|
||||
//stackRestore(stackPtr);
|
||||
out.free();
|
||||
}
|
||||
return frustum;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Matrix4> getViewMatrix() async {
|
||||
late Pointer stackPtr;
|
||||
if (FILAMENT_WASM) {
|
||||
//stackPtr = stackSave();
|
||||
}
|
||||
final matrix = double4x4ToMatrix4(Camera_getViewMatrix(camera));
|
||||
if (FILAMENT_WASM) {
|
||||
//stackRestore(stackPtr);
|
||||
}
|
||||
return matrix;
|
||||
}
|
||||
|
||||
@override
|
||||
Future setProjection(Projection projection, double left, double right,
|
||||
double bottom, double top, double near, double far) async {
|
||||
Camera_setProjection(
|
||||
camera, 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);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,97 @@
|
||||
import 'dart:async';
|
||||
import 'package:thermion_dart/src/bindings/bindings.dart';
|
||||
import 'package:thermion_dart/src/filament/src/implementation/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 final CallbackHolder<GizmoPickCallbackFunction> _callbackHolder;
|
||||
|
||||
void Function(GizmoPickResultType axis, Vector3 coords)? _callback;
|
||||
|
||||
late FFIView view;
|
||||
|
||||
FFIGizmo(
|
||||
super.asset,
|
||||
super.app,
|
||||
super.animationManager,
|
||||
{
|
||||
required this.view,
|
||||
required this.entities,
|
||||
}) {
|
||||
|
||||
_callbackHolder = _onPickResult.asCallback();
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
Future dispose() async {
|
||||
_callbackHolder.dispose();
|
||||
}
|
||||
|
||||
|
||||
void _onPickResult(int resultType, double x, double y, double z) {
|
||||
|
||||
final type = switch(resultType) {
|
||||
TGizmoPickResultType.AxisX => GizmoPickResultType.AxisX,
|
||||
TGizmoPickResultType.AxisY => GizmoPickResultType.AxisY,
|
||||
TGizmoPickResultType.AxisZ => GizmoPickResultType.AxisZ,
|
||||
TGizmoPickResultType.None => GizmoPickResultType.None,
|
||||
TGizmoPickResultType.Parent => GizmoPickResultType.Parent,
|
||||
_ => throw UnsupportedError(resultType.toString())
|
||||
};
|
||||
_callback?.call(type, Vector3(x, y, z));
|
||||
}
|
||||
|
||||
bool isNonPickable(ThermionEntity entity) {
|
||||
throw UnimplementedError();
|
||||
// return SceneManager_isGridEntity(sceneManager, entity);
|
||||
}
|
||||
|
||||
bool isGizmoEntity(ThermionEntity entity) => entities.contains(entity);
|
||||
|
||||
@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, _callbackHolder.pointer);
|
||||
}
|
||||
|
||||
@override
|
||||
Future highlight(Axis axis) async {
|
||||
Gizmo_unhighlight(asset.cast<TGizmo>());
|
||||
final tAxis = switch(axis) {
|
||||
Axis.X => TGizmoAxis.X,
|
||||
Axis.Y => TGizmoAxis.Y,
|
||||
Axis.Z => TGizmoAxis.Z
|
||||
};
|
||||
Gizmo_highlight(asset.cast<TGizmo>(), tAxis);
|
||||
}
|
||||
|
||||
@override
|
||||
Future unhighlight() async {
|
||||
Gizmo_unhighlight(asset.cast<TGizmo>());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
import 'dart:async';
|
||||
import 'package:thermion_dart/src/filament/src/implementation/ffi_filament_app.dart';
|
||||
import 'package:thermion_dart/src/filament/src/implementation/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 {
|
||||
late Pointer stackPtr;
|
||||
if (FILAMENT_WASM) {
|
||||
//stackPtr = stackSave();
|
||||
}
|
||||
final ptr = name.toNativeUtf8().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);
|
||||
|
||||
if (FILAMENT_WASM) {
|
||||
//stackRestore(stackPtr);
|
||||
data.free();
|
||||
}
|
||||
}
|
||||
|
||||
@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, depthFunc.index);
|
||||
}
|
||||
|
||||
@override
|
||||
Future setStencilCompareFunction(SamplerCompareFunction func,
|
||||
[StencilFace face = StencilFace.FRONT_AND_BACK]) async {
|
||||
MaterialInstance_setStencilCompareFunction(pointer, func.index, face.index);
|
||||
}
|
||||
|
||||
@override
|
||||
Future setStencilOpDepthFail(StencilOperation op,
|
||||
[StencilFace face = StencilFace.FRONT_AND_BACK]) async {
|
||||
MaterialInstance_setStencilOpDepthFail(pointer, op.index, face.index);
|
||||
}
|
||||
|
||||
@override
|
||||
Future setStencilOpDepthStencilPass(StencilOperation op,
|
||||
[StencilFace face = StencilFace.FRONT_AND_BACK]) async {
|
||||
MaterialInstance_setStencilOpDepthStencilPass(
|
||||
pointer, op.index, face.index);
|
||||
}
|
||||
|
||||
@override
|
||||
Future setStencilOpStencilFail(StencilOperation op,
|
||||
[StencilFace face = StencilFace.FRONT_AND_BACK]) async {
|
||||
MaterialInstance_setStencilOpStencilFail(pointer, op.index, face.index);
|
||||
}
|
||||
|
||||
@override
|
||||
Future setStencilReferenceValue(int value,
|
||||
[StencilFace face = StencilFace.FRONT_AND_BACK]) async {
|
||||
MaterialInstance_setStencilReferenceValue(pointer, value, face.index);
|
||||
}
|
||||
|
||||
@override
|
||||
Future setStencilWriteEnabled(bool enabled) async {
|
||||
MaterialInstance_setStencilWrite(pointer, enabled);
|
||||
}
|
||||
|
||||
@override
|
||||
Future setCullingMode(CullingMode cullingMode) async {
|
||||
MaterialInstance_setCullingMode(pointer, 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, 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 {
|
||||
MaterialInstance_setParameterMat4(
|
||||
pointer, name.toNativeUtf8().cast<Char>(), matrix.storage.address);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import 'package:thermion_dart/src/bindings/bindings.dart';
|
||||
import 'package:thermion_dart/src/filament/src/implementation/ffi_filament_app.dart';
|
||||
import 'package:thermion_dart/src/filament/src/implementation/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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:thermion_dart/src/filament/src/implementation/ffi_asset.dart';
|
||||
import 'package:thermion_dart/src/filament/src/interface/scene.dart';
|
||||
import 'package:thermion_dart/src/bindings/bindings.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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import 'package:thermion_dart/src/bindings/bindings.dart';
|
||||
import 'package:thermion_dart/thermion_dart.dart';
|
||||
|
||||
class FFISwapChain extends SwapChain {
|
||||
final Pointer<TSwapChain> swapChain;
|
||||
|
||||
FFISwapChain(this.swapChain);
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:thermion_dart/src/bindings/bindings.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 tPixelDataFormat = format.value;
|
||||
final tPixelDataType = type.value;
|
||||
final result = await withBoolCallback((cb) {
|
||||
Texture_loadImageRenderThread(
|
||||
_engine,
|
||||
pointer,
|
||||
image.pointer,
|
||||
tPixelDataFormat,
|
||||
tPixelDataType,
|
||||
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 {
|
||||
throw UnimplementedError();
|
||||
// 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();
|
||||
|
||||
final imagePtr = await withPointerCallback<TLinearImage>((cb) {
|
||||
Image_decodeRenderThread(
|
||||
data.address, data.lengthInBytes, namePtr.cast(), cb);
|
||||
});
|
||||
|
||||
return FFILinearImage(imagePtr);
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
224
thermion_dart/lib/src/filament/src/implementation/ffi_view.dart
Normal file
224
thermion_dart/lib/src/filament/src/implementation/ffi_view.dart
Normal file
@@ -0,0 +1,224 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:thermion_dart/src/filament/src/interface/scene.dart';
|
||||
import 'package:thermion_dart/src/filament/src/implementation/ffi_filament_app.dart';
|
||||
import 'package:thermion_dart/src/filament/src/implementation/ffi_render_target.dart';
|
||||
import 'package:thermion_dart/src/filament/src/implementation/ffi_scene.dart';
|
||||
import 'package:thermion_dart/thermion_dart.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;
|
||||
|
||||
late CallbackHolder<PickCallbackFunction> _onPickResultHolder;
|
||||
|
||||
|
||||
FFIView(this.view, this.app) {
|
||||
final renderTargetPtr = View_getRenderTarget(view);
|
||||
if (renderTargetPtr != nullptr) {
|
||||
renderTarget = FFIRenderTarget(renderTargetPtr, app);
|
||||
}
|
||||
|
||||
_onPickResultHolder =
|
||||
_onPickResult.asCallback();
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
Future dispose() async {
|
||||
_onPickResultHolder.dispose();
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
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 {
|
||||
final 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, 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, 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);
|
||||
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
@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, _onPickResultHolder.pointer);
|
||||
|
||||
}
|
||||
|
||||
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,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import 'package:thermion_dart/src/bindings/bindings.dart';
|
||||
import 'package:thermion_dart/src/filament/src/implementation/ffi_asset.dart';
|
||||
import 'package:thermion_dart/src/filament/src/implementation/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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export 'resource_loader_io.dart'
|
||||
if (dart.library.io) 'resource_loader_io.dart'
|
||||
if (dart.library.js_interop) 'resource_loader_js.dart';
|
||||
@@ -0,0 +1,8 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:thermion_dart/thermion_dart.dart';
|
||||
|
||||
Future<Uint8List> defaultResourceLoader(String path) {
|
||||
print("Loading file $path");
|
||||
return File(path).readAsBytes();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import 'package:thermion_dart/thermion_dart.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
Future<Uint8List> defaultResourceLoader(String path) async {
|
||||
if(path.startsWith("file://")) {
|
||||
throw Exception("Unsupported URI : $path");
|
||||
}
|
||||
|
||||
final response = await http.get(Uri.parse(path));
|
||||
return response.bodyBytes;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
library;
|
||||
|
||||
import 'package:animation_tools_dart/animation_tools_dart.dart';
|
||||
import 'package:thermion_dart/src/filament/src/layers.dart';
|
||||
import 'package:thermion_dart/src/filament/src/interface/layers.dart';
|
||||
import 'package:thermion_dart/thermion_dart.dart';
|
||||
|
||||
export 'geometry.dart';
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:thermion_dart/src/filament/src/layers.dart';
|
||||
import 'package:thermion_dart/src/filament/src/interface/layers.dart';
|
||||
import 'package:thermion_dart/thermion_dart.dart';
|
||||
|
||||
enum Projection { Perspective, Orthographic }
|
||||
@@ -1,15 +1,11 @@
|
||||
import 'dart:typed_data';
|
||||
import 'package:thermion_dart/src/filament/src/engine.dart';
|
||||
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_material.dart';
|
||||
import 'package:thermion_dart/src/filament/src/interface/scene.dart';
|
||||
import 'package:thermion_dart/thermion_dart.dart';
|
||||
|
||||
class FilamentConfig<T, U> {
|
||||
final Backend backend;
|
||||
final T? renderCallback;
|
||||
final U? renderCallbackOwner;
|
||||
final Future<Uint8List> Function(String) resourceLoader;
|
||||
Future<Uint8List> Function(String)? resourceLoader;
|
||||
final U? platform;
|
||||
final U? sharedContext;
|
||||
final String? uberArchivePath;
|
||||
37
thermion_dart/lib/src/filament/src/interface/geometry.dart
Normal file
37
thermion_dart/lib/src/filament/src/interface/geometry.dart
Normal file
@@ -0,0 +1,37 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:thermion_dart/src/bindings/bindings.dart';
|
||||
|
||||
import '../../../viewer/viewer.dart';
|
||||
|
||||
class Geometry {
|
||||
final Float32List vertices;
|
||||
final Uint16List indices;
|
||||
late final Float32List normals;
|
||||
late final Float32List uvs;
|
||||
final PrimitiveType primitiveType;
|
||||
|
||||
Geometry(
|
||||
this.vertices,
|
||||
this.indices, {
|
||||
Float32List? normals,
|
||||
Float32List? uvs,
|
||||
this.primitiveType = PrimitiveType.TRIANGLES,
|
||||
}) {
|
||||
this.uvs = uvs ?? Float32List(0);
|
||||
this.normals = normals ?? Float32List(0);
|
||||
if (this.uvs.length != 0 && this.uvs.length != (vertices.length ~/ 3 * 2)) {
|
||||
throw Exception(
|
||||
"Expected either ${indices.length * 2} UVs, got ${this.uvs!.length}");
|
||||
}
|
||||
}
|
||||
|
||||
void scale(double factor) {
|
||||
for (int i = 0; i < vertices.length; i++) {
|
||||
vertices[i] = vertices[i] * factor;
|
||||
}
|
||||
}
|
||||
|
||||
bool get hasNormals => normals?.isNotEmpty == true;
|
||||
bool get hasUVs => uvs?.isNotEmpty == true;
|
||||
}
|
||||
@@ -25,4 +25,7 @@ abstract class GizmoAsset extends ThermionAsset {
|
||||
Future unhighlight();
|
||||
bool isNonPickable(ThermionEntity entity);
|
||||
bool isGizmoEntity(ThermionEntity entity);
|
||||
|
||||
Future dispose();
|
||||
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:thermion_dart/thermion_dart.dart';
|
||||
import 'package:vector_math/vector_math_64.dart';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import '../../viewer/viewer.dart';
|
||||
import '../../../viewer/viewer.dart';
|
||||
|
||||
/// The result of a picking operation (see [ThermionViewer.pick] for more details).
|
||||
/// [x] and [y] refer to the original screen coordinates used to call [pick]; this should
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:thermion_dart/src/filament/src/layers.dart';
|
||||
import 'package:thermion_dart/src/filament/src/scene.dart';
|
||||
import 'package:thermion_dart/src/filament/src/interface/layers.dart';
|
||||
import 'package:thermion_dart/src/filament/src/interface/scene.dart';
|
||||
import 'package:thermion_dart/thermion_dart.dart';
|
||||
|
||||
enum BlendMode {
|
||||
@@ -57,7 +57,10 @@ abstract class View {
|
||||
Future pick(int x, int y, void Function(PickResult) resultHandler);
|
||||
|
||||
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
Future dispose();
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user