feat! js_interop improvements
This commit is contained in:
1 parent
63e2dcd0ca
commit
2f16908992
159 files changed
+12989
-8377
No files matched your search
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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
-1
@@ -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
-1
@@ -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 }
|
||||
File renamed without changes.
File renamed without changes.
+2
-6
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
+3
@@ -25,4 +25,7 @@ abstract class GizmoAsset extends ThermionAsset {
|
||||
Future unhighlight();
|
||||
bool isNonPickable(ThermionEntity entity);
|
||||
bool isGizmoEntity(ThermionEntity entity);
|
||||
|
||||
Future dispose();
|
||||
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
-2
@@ -1,5 +1,3 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:thermion_dart/thermion_dart.dart';
|
||||
import 'package:vector_math/vector_math_64.dart';
|
||||
|
||||
+1
-1
@@ -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
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+6
-3
@@ -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