refactoring
This commit is contained in:
2
thermion_dart/lib/src/filament/filament.dart
Normal file
2
thermion_dart/lib/src/filament/filament.dart
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export 'src/filament_app.dart';
|
||||||
|
export 'src/engine.dart';
|
||||||
267
thermion_dart/lib/src/filament/src/asset.dart
Normal file
267
thermion_dart/lib/src/filament/src/asset.dart
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
library;
|
||||||
|
|
||||||
|
import 'package:animation_tools_dart/animation_tools_dart.dart';
|
||||||
|
import 'package:thermion_dart/src/filament/src/layers.dart';
|
||||||
|
import 'package:thermion_dart/thermion_dart.dart';
|
||||||
|
import 'package:vector_math/vector_math_64.dart';
|
||||||
|
import 'entity.dart';
|
||||||
|
|
||||||
|
export 'geometry.dart';
|
||||||
|
export 'gltf.dart';
|
||||||
|
|
||||||
|
export 'light_options.dart';
|
||||||
|
|
||||||
|
abstract class ThermionAsset {
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
ThermionEntity get entity;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future<List<ThermionEntity>> getChildEntities();
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future setMaterialInstanceAt(covariant MaterialInstance instance);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Renders an outline around [entity] with the given color.
|
||||||
|
///
|
||||||
|
Future setStencilHighlight(
|
||||||
|
{double r = 1.0, double g = 0.0, double b = 0.0, int? entityIndex});
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Removes the outline around [entity]. Noop if there was no highlight.
|
||||||
|
///
|
||||||
|
Future removeStencilHighlight();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// When visible is [true], renders the bounding box.
|
||||||
|
///
|
||||||
|
Future setBoundingBoxVisibility(bool visible);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future<ThermionAsset> getInstance(int index);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Create a new instance of [entity].
|
||||||
|
/// Instances are not automatically added to the scene; you must
|
||||||
|
/// call [addToScene].
|
||||||
|
///
|
||||||
|
Future<ThermionAsset> createInstance(
|
||||||
|
{covariant List<MaterialInstance>? materialInstances = null});
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Returns the number of instances associated with this asset.
|
||||||
|
///
|
||||||
|
Future<int> getInstanceCount();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Returns all instances of associated with this asset.
|
||||||
|
///
|
||||||
|
Future<List<ThermionAsset>> getInstances();
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future setCastShadows(bool castShadows);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future setReceiveShadows(bool castShadows);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// All renderable entities are assigned a layer mask.
|
||||||
|
///
|
||||||
|
/// By calling [setLayerVisibility], all renderable entities allocated to
|
||||||
|
/// the given layer can be efficiently hidden/revealed.
|
||||||
|
///
|
||||||
|
/// By default, all renderable entities are assigned to layer 0 (and this
|
||||||
|
/// layer is enabled by default). Call [setVisibilityLayer] to change the
|
||||||
|
/// layer for the specified entity.
|
||||||
|
///
|
||||||
|
/// Note that we currently also assign gizmos to layer 1 (enabled by default)
|
||||||
|
/// and the world grid to layer 2 (disabled by default). We suggest you avoid
|
||||||
|
/// using these layers.
|
||||||
|
///
|
||||||
|
Future setVisibilityLayer(ThermionEntity entity, VisibilityLayers layer);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Schedules the glTF animation at [index] in [asset] to start playing on the next frame.
|
||||||
|
///
|
||||||
|
Future playAnimation(ThermionAsset asset, int index,
|
||||||
|
{bool loop = false,
|
||||||
|
bool reverse = false,
|
||||||
|
bool replaceActive = true,
|
||||||
|
double crossfade = 0.0,
|
||||||
|
double startOffset = 0.0});
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Schedules the glTF animation at [index] in [entity] to start playing on the next frame.
|
||||||
|
///
|
||||||
|
Future playAnimationByName(covariant ThermionAsset asset, String name,
|
||||||
|
{bool loop = false,
|
||||||
|
bool reverse = false,
|
||||||
|
bool replaceActive = true,
|
||||||
|
double crossfade = 0.0});
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future setGltfAnimationFrame(
|
||||||
|
covariant ThermionAsset asset, int index, int animationFrame);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future stopAnimation(covariant ThermionAsset asset, int animationIndex);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future stopAnimationByName(covariant ThermionAsset asset, String name);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Set the weights for all morph targets in [entity] to [weights].
|
||||||
|
/// Note that [weights] must contain values for ALL morph targets, but no exception will be thrown if you don't do so (you'll just get incorrect results).
|
||||||
|
/// If you only want to set one value, set all others to zero (check [getMorphTargetNames] if you need the get a list of all morph targets).
|
||||||
|
/// IMPORTANT - this accepts the actual ThermionEntity with the relevant morph targets (unlike [getMorphTargetNames], which uses the parent entity and the child mesh name).
|
||||||
|
/// Use [getChildEntityByName] if you are setting the weights for a child mesh.
|
||||||
|
///
|
||||||
|
Future setMorphTargetWeights(ThermionEntity entity, List<double> weights);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Gets the names of all morph targets for the child renderable [childEntity] under [entity].
|
||||||
|
///
|
||||||
|
Future<List<String>> getMorphTargetNames(
|
||||||
|
covariant ThermionAsset asset, ThermionEntity childEntity);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Gets the names of all bones for the armature at [skinIndex] under the specified [entity].
|
||||||
|
///
|
||||||
|
Future<List<String>> getBoneNames(covariant ThermionAsset asset,
|
||||||
|
{int skinIndex = 0});
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Gets the names of all glTF animations embedded in the specified entity.
|
||||||
|
///
|
||||||
|
Future<List<String>> getAnimationNames(covariant ThermionAsset asset);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Returns the length (in seconds) of the animation at the given index.
|
||||||
|
///
|
||||||
|
Future<double> getAnimationDuration(
|
||||||
|
covariant ThermionAsset asset, int animationIndex);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Construct animation(s) for every entity under [asset]. If [targetMeshNames] is provided, only entities with matching names will be animated.
|
||||||
|
/// [MorphTargetAnimation] for an explanation as to how to construct the animation frame data.
|
||||||
|
/// This method will check the morph target names specified in [animation] against the morph target names that actually exist exist under [meshName] in [entity],
|
||||||
|
/// throwing an exception if any cannot be found.
|
||||||
|
/// It is permissible for [animation] to omit any targets that do exist under [meshName]; these simply won't be animated.
|
||||||
|
///
|
||||||
|
Future setMorphAnimationData(
|
||||||
|
covariant ThermionAsset asset, MorphAnimationData animation,
|
||||||
|
{List<String>? targetMeshNames});
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Clear all current morph animations for [entity].
|
||||||
|
///
|
||||||
|
Future clearMorphAnimationData(ThermionEntity entity);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Resets all bones in the given entity to their rest pose.
|
||||||
|
/// This should be done before every call to addBoneAnimation.
|
||||||
|
///
|
||||||
|
Future resetBones(ThermionAsset asset);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Enqueues and plays the [animation] for the specified bone(s).
|
||||||
|
/// By default, frame data is interpreted as being in *parent* bone space;
|
||||||
|
/// a 45 degree around Y means the bone will rotate 45 degrees around the
|
||||||
|
/// Y axis of the parent bone *in its current orientation*.
|
||||||
|
/// (i.e NOT the parent bone's rest position!).
|
||||||
|
/// Currently, only [Space.ParentBone] and [Space.Model] are supported; if you want
|
||||||
|
/// to transform to another space, you will need to do so manually.
|
||||||
|
///
|
||||||
|
/// [fadeInInSecs]/[fadeOutInSecs]/[maxDelta] are used to cross-fade between
|
||||||
|
/// the current active glTF animation ("animation1") and the animation you
|
||||||
|
/// set via this method ("animation2"). The bone orientations will be
|
||||||
|
/// linearly interpolated between animation1 and animation2; at time 0,
|
||||||
|
/// the orientation will be 100% animation1, at time [fadeInInSecs], the
|
||||||
|
/// animation will be ((1 - maxDelta) * animation1) + (maxDelta * animation2).
|
||||||
|
/// This will be applied in reverse after [fadeOutInSecs].
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future addBoneAnimation(ThermionAsset asset, BoneAnimationData animation,
|
||||||
|
{int skinIndex = 0,
|
||||||
|
double fadeInInSecs = 0.0,
|
||||||
|
double fadeOutInSecs = 0.0,
|
||||||
|
double maxDelta = 1.0});
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Gets the entity representing the bone at [boneIndex]/[skinIndex].
|
||||||
|
/// The returned entity is only intended for use with [getWorldTransform].
|
||||||
|
///
|
||||||
|
Future<ThermionEntity> getBone(covariant ThermionAsset asset, int boneIndex,
|
||||||
|
{int skinIndex = 0});
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Gets the local (relative to parent) transform for [entity].
|
||||||
|
///
|
||||||
|
Future<Matrix4> getLocalTransform(ThermionEntity entity);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Gets the world transform for [entity].
|
||||||
|
///
|
||||||
|
Future<Matrix4> getWorldTransform(ThermionEntity entity);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Gets the inverse bind (pose) matrix for the bone.
|
||||||
|
/// Note that [parent] must be the ThermionEntity returned by [loadGlb/loadGltf], not any other method ([getChildEntity] etc).
|
||||||
|
/// This is because all joint information is internally stored with the parent entity.
|
||||||
|
///
|
||||||
|
Future<Matrix4> getInverseBindMatrix(
|
||||||
|
covariant ThermionAsset asset, int boneIndex,
|
||||||
|
{int skinIndex = 0});
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Sets the transform (relative to its parent) for [entity].
|
||||||
|
///
|
||||||
|
Future setTransform(ThermionEntity entity, Matrix4 transform);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Updates the bone matrices for [entity] (which must be the ThermionEntity
|
||||||
|
/// returned by [loadGlb/loadGltf]).
|
||||||
|
/// Under the hood, this just calls [updateBoneMatrices] on the Animator
|
||||||
|
/// instance of the relevant FilamentInstance (which uses the local
|
||||||
|
/// bone transform and the inverse bind matrix to set the bone matrix).
|
||||||
|
///
|
||||||
|
Future updateBoneMatrices(ThermionEntity entity);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Directly set the bone matrix for the bone at the given index.
|
||||||
|
/// Don't call this manually unless you know what you're doing.
|
||||||
|
///
|
||||||
|
Future setBoneTransform(
|
||||||
|
ThermionEntity entity, int boneIndex, Matrix4 transform,
|
||||||
|
{int skinIndex = 0});
|
||||||
|
|
||||||
|
///
|
||||||
|
/// An [entity] will only be animatable after an animation component is attached.
|
||||||
|
/// Any calls to [playAnimation]/[setBoneAnimation]/[setMorphAnimation] will have no visual effect until [addAnimationComponent] has been called on the instance.
|
||||||
|
///
|
||||||
|
Future addAnimationComponent(ThermionEntity entity);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Removes an animation component from [entity].
|
||||||
|
///
|
||||||
|
Future removeAnimationComponent(ThermionEntity entity);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import 'package:thermion_dart/src/viewer/src/shared_types/layers.dart';
|
import 'package:thermion_dart/src/filament/src/layers.dart';
|
||||||
import 'package:vector_math/vector_math_64.dart';
|
import 'package:thermion_dart/thermion_dart.dart';
|
||||||
import '../thermion_viewer_base.dart';
|
|
||||||
|
|
||||||
enum Projection { Perspective, Orthographic }
|
enum Projection { Perspective, Orthographic }
|
||||||
|
|
||||||
abstract class Camera {
|
abstract class Camera {
|
||||||
|
|
||||||
Future lookAt(Vector3 position, {Vector3? focus, Vector3? up}) async {
|
Future lookAt(Vector3 position, {Vector3? focus, Vector3? up}) async {
|
||||||
focus ??= Vector3.zero();
|
focus ??= Vector3.zero();
|
||||||
up ??= Vector3(0, 1, 0);
|
up ??= Vector3(0, 1, 0);
|
||||||
@@ -13,6 +13,12 @@ abstract class Camera {
|
|||||||
await setModelMatrix(viewMatrix);
|
await setModelMatrix(viewMatrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Sets the camera exposure.
|
||||||
|
///
|
||||||
|
Future setCameraExposure(
|
||||||
|
double aperture, double shutterSpeed, double sensitivity);
|
||||||
|
|
||||||
Future setProjection(Projection projection, double left, double right,
|
Future setProjection(Projection projection, double left, double right,
|
||||||
double bottom, double top, double near, double far);
|
double bottom, double top, double near, double far);
|
||||||
Future setProjectionMatrixWithCulling(
|
Future setProjectionMatrixWithCulling(
|
||||||
@@ -37,6 +43,8 @@ abstract class Camera {
|
|||||||
Future<double> getNear();
|
Future<double> getNear();
|
||||||
Future<double> getCullingFar();
|
Future<double> getCullingFar();
|
||||||
Future<double> getFocalLength();
|
Future<double> getFocalLength();
|
||||||
|
Future<double> getFocusDistance();
|
||||||
|
Future setFocusDistance(double focusDistance);
|
||||||
|
|
||||||
Future destroy();
|
Future destroy();
|
||||||
}
|
}
|
||||||
2
thermion_dart/lib/src/filament/src/entity.dart
Normal file
2
thermion_dart/lib/src/filament/src/entity.dart
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
typedef ThermionEntity = int;
|
||||||
|
final ThermionEntity FILAMENT_ENTITY_NULL = 0;
|
||||||
209
thermion_dart/lib/src/filament/src/filament_app.dart
Normal file
209
thermion_dart/lib/src/filament/src/filament_app.dart
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:thermion_dart/src/filament/src/engine.dart';
|
||||||
|
import 'package:thermion_dart/thermion_dart.dart';
|
||||||
|
|
||||||
|
class FilamentConfig<T, U> {
|
||||||
|
final Backend backend;
|
||||||
|
final T? renderCallback;
|
||||||
|
final U? renderCallbackOwner;
|
||||||
|
final U resourceLoader;
|
||||||
|
final U? platform;
|
||||||
|
final U? driver;
|
||||||
|
final U? sharedContext;
|
||||||
|
final String? uberArchivePath;
|
||||||
|
final int stereoscopicEyeCount;
|
||||||
|
final bool disableHandleUseAfterFreeCheck;
|
||||||
|
|
||||||
|
FilamentConfig(
|
||||||
|
{required this.backend,
|
||||||
|
required this.resourceLoader,
|
||||||
|
this.uberArchivePath,
|
||||||
|
this.renderCallback,
|
||||||
|
this.renderCallbackOwner,
|
||||||
|
this.platform,
|
||||||
|
this.driver,
|
||||||
|
this.sharedContext,
|
||||||
|
this.stereoscopicEyeCount = 1,
|
||||||
|
this.disableHandleUseAfterFreeCheck = false});
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class FilamentApp<T> {
|
||||||
|
|
||||||
|
static FilamentApp? instance;
|
||||||
|
|
||||||
|
final T engine;
|
||||||
|
final T gltfAssetLoader;
|
||||||
|
final T gltfResourceLoader;
|
||||||
|
final T renderer;
|
||||||
|
final T transformManager;
|
||||||
|
final T lightManager;
|
||||||
|
final T renderableManager;
|
||||||
|
final T ubershaderMaterialProvider;
|
||||||
|
|
||||||
|
FilamentApp(
|
||||||
|
{required this.engine,
|
||||||
|
required this.gltfAssetLoader,
|
||||||
|
required this.gltfResourceLoader,
|
||||||
|
required this.renderer,
|
||||||
|
required this.transformManager,
|
||||||
|
required this.lightManager,
|
||||||
|
required this.renderableManager,
|
||||||
|
required this.ubershaderMaterialProvider});
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future<SwapChain> createHeadlessSwapChain(int width, int height,
|
||||||
|
{bool hasStencilBuffer = false});
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future<SwapChain> createSwapChain(T handle, {bool hasStencilBuffer = false});
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future destroySwapChain(SwapChain swapChain);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future destroy();
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future<RenderTarget> createRenderTarget(int width, int height,
|
||||||
|
{covariant Texture? color, covariant Texture? depth});
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future<Texture> createTexture(int width, int height,
|
||||||
|
{int depth = 1,
|
||||||
|
int levels = 1,
|
||||||
|
Set<TextureUsage> flags = const {TextureUsage.TEXTURE_USAGE_SAMPLEABLE},
|
||||||
|
TextureSamplerType textureSamplerType = TextureSamplerType.SAMPLER_2D,
|
||||||
|
TextureFormat textureFormat = TextureFormat.RGBA16F,
|
||||||
|
int? importedTextureHandle});
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future<TextureSampler> createTextureSampler(
|
||||||
|
{TextureMinFilter minFilter = TextureMinFilter.LINEAR,
|
||||||
|
TextureMagFilter magFilter = TextureMagFilter.LINEAR,
|
||||||
|
TextureWrapMode wrapS = TextureWrapMode.CLAMP_TO_EDGE,
|
||||||
|
TextureWrapMode wrapT = TextureWrapMode.CLAMP_TO_EDGE,
|
||||||
|
TextureWrapMode wrapR = TextureWrapMode.CLAMP_TO_EDGE,
|
||||||
|
double anisotropy = 0.0,
|
||||||
|
TextureCompareMode compareMode = TextureCompareMode.NONE,
|
||||||
|
TextureCompareFunc compareFunc = TextureCompareFunc.LESS_EQUAL});
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Decodes the specified image data.
|
||||||
|
///
|
||||||
|
Future<LinearImage> decodeImage(Uint8List data);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Creates an (empty) imge with the given dimensions.
|
||||||
|
///
|
||||||
|
Future<LinearImage> createImage(int width, int height, int channels);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future<Material> createMaterial(Uint8List data);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future<MaterialInstance> createUbershaderMaterialInstance({
|
||||||
|
bool doubleSided = false,
|
||||||
|
bool unlit = false,
|
||||||
|
bool hasVertexColors = false,
|
||||||
|
bool hasBaseColorTexture = false,
|
||||||
|
bool hasNormalTexture = false,
|
||||||
|
bool hasOcclusionTexture = false,
|
||||||
|
bool hasEmissiveTexture = false,
|
||||||
|
bool useSpecularGlossiness = false,
|
||||||
|
AlphaMode alphaMode = AlphaMode.OPAQUE,
|
||||||
|
bool enableDiagnostics = false,
|
||||||
|
bool hasMetallicRoughnessTexture = false,
|
||||||
|
int metallicRoughnessUV = -1,
|
||||||
|
int baseColorUV = -1,
|
||||||
|
bool hasClearCoatTexture = false,
|
||||||
|
int clearCoatUV = -1,
|
||||||
|
bool hasClearCoatRoughnessTexture = false,
|
||||||
|
int clearCoatRoughnessUV = -1,
|
||||||
|
bool hasClearCoatNormalTexture = false,
|
||||||
|
int clearCoatNormalUV = -1,
|
||||||
|
bool hasClearCoat = false,
|
||||||
|
bool hasTransmission = false,
|
||||||
|
bool hasTextureTransforms = false,
|
||||||
|
int emissiveUV = -1,
|
||||||
|
int aoUV = -1,
|
||||||
|
int normalUV = -1,
|
||||||
|
bool hasTransmissionTexture = false,
|
||||||
|
int transmissionUV = -1,
|
||||||
|
bool hasSheenColorTexture = false,
|
||||||
|
int sheenColorUV = -1,
|
||||||
|
bool hasSheenRoughnessTexture = false,
|
||||||
|
int sheenRoughnessUV = -1,
|
||||||
|
bool hasVolumeThicknessTexture = false,
|
||||||
|
int volumeThicknessUV = -1,
|
||||||
|
bool hasSheen = false,
|
||||||
|
bool hasIOR = false,
|
||||||
|
bool hasVolume = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future<MaterialInstance> createUnlitMaterialInstance();
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future<MaterialInstance> getMaterialInstanceAt(
|
||||||
|
ThermionEntity entity, int index);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
Future<ThermionAsset> createGeometry(Geometry geometry,
|
||||||
|
{List<MaterialInstance>? materialInstances, bool keepData = false});
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future setRenderable(covariant View view, bool renderable);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future register(covariant SwapChain swapChain, covariant View view);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future render();
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future requestFrame();
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future registerRequestFrameHook(Future Function() hook);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future unregisterRequestFrameHook(Future Function() hook);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import '../../viewer.dart';
|
import '../../viewer/viewer.dart';
|
||||||
|
|
||||||
class Geometry {
|
class Geometry {
|
||||||
final Float32List vertices;
|
final Float32List vertices;
|
||||||
28
thermion_dart/lib/src/filament/src/gizmo.dart
Normal file
28
thermion_dart/lib/src/filament/src/gizmo.dart
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import 'entity.dart';
|
||||||
|
import 'package:thermion_dart/thermion_dart.dart';
|
||||||
|
import 'package:vector_math/vector_math_64.dart';
|
||||||
|
|
||||||
|
enum Axis {
|
||||||
|
X(const [1.0, 0.0, 0.0]),
|
||||||
|
Y(const [0.0, 1.0, 0.0]),
|
||||||
|
Z(const [0.0, 0.0, 1.0]);
|
||||||
|
|
||||||
|
const Axis(this.vector);
|
||||||
|
|
||||||
|
final List<double> vector;
|
||||||
|
|
||||||
|
Vector3 asVector() => Vector3(vector[0], vector[1], vector[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum GizmoPickResultType { AxisX, AxisY, AxisZ, Parent, None }
|
||||||
|
|
||||||
|
enum GizmoType { translation, rotation }
|
||||||
|
|
||||||
|
abstract class GizmoAsset extends ThermionAsset {
|
||||||
|
Future pick(int x, int y,
|
||||||
|
{Future Function(GizmoPickResultType axis, Vector3 coords)? handler});
|
||||||
|
Future highlight(Axis axis);
|
||||||
|
Future unhighlight();
|
||||||
|
bool isNonPickable(ThermionEntity entity);
|
||||||
|
bool isGizmoEntity(ThermionEntity entity);
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import '../../viewer.dart';
|
import '../../viewer/viewer.dart';
|
||||||
|
|
||||||
/// The result of a picking operation (see [ThermionViewer.pick] for more details).
|
/// 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
|
/// [x] and [y] refer to the original screen coordinates used to call [pick]; this should
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:thermion_dart/src/viewer/src/thermion_viewer_base.dart';
|
import 'package:thermion_dart/thermion_dart.dart';
|
||||||
|
|
||||||
abstract class Scene {
|
abstract class Scene {
|
||||||
Future add(covariant ThermionAsset asset);
|
Future add(covariant ThermionAsset asset);
|
||||||
@@ -6,7 +6,9 @@ export 'render_target.dart';
|
|||||||
export 'camera.dart';
|
export 'camera.dart';
|
||||||
export 'material.dart';
|
export 'material.dart';
|
||||||
export 'texture.dart';
|
export 'texture.dart';
|
||||||
export 'entities.dart';
|
export 'asset.dart';
|
||||||
|
export 'entity.dart';
|
||||||
|
export 'gizmo.dart';
|
||||||
export 'light.dart';
|
export 'light.dart';
|
||||||
export 'shadow.dart';
|
export 'shadow.dart';
|
||||||
export 'manipulator.dart';
|
export 'manipulator.dart';
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:thermion_dart/src/viewer/src/shared_types/layers.dart';
|
import 'package:thermion_dart/src/filament/src/layers.dart';
|
||||||
import 'package:thermion_dart/thermion_dart.dart';
|
import 'package:thermion_dart/thermion_dart.dart';
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -26,7 +26,6 @@ abstract class View {
|
|||||||
Future<Camera> getCamera();
|
Future<Camera> getCamera();
|
||||||
Future setPostProcessing(bool enabled);
|
Future setPostProcessing(bool enabled);
|
||||||
Future setAntiAliasing(bool msaa, bool fxaa, bool taa);
|
Future setAntiAliasing(bool msaa, bool fxaa, bool taa);
|
||||||
Future setRenderable(bool renderable, covariant SwapChain swapChain);
|
|
||||||
Future setFrustumCullingEnabled(bool enabled);
|
Future setFrustumCullingEnabled(bool enabled);
|
||||||
Future setToneMapper(ToneMapper mapper);
|
Future setToneMapper(ToneMapper mapper);
|
||||||
Future setStencilBufferEnabled(bool enabled);
|
Future setStencilBufferEnabled(bool enabled);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:vector_math/vector_math_64.dart';
|
import 'package:vector_math/vector_math_64.dart';
|
||||||
import '../../../viewer/src/shared_types/camera.dart';
|
import '../../../filament/src/camera.dart';
|
||||||
import '../../../viewer/viewer.dart';
|
import '../../../viewer/viewer.dart';
|
||||||
import '../../input.dart';
|
import '../../input.dart';
|
||||||
import '../input_handler.dart';
|
import '../input_handler.dart';
|
||||||
|
|||||||
@@ -6,6 +6,13 @@ import 'package:vector_math/vector_math_64.dart';
|
|||||||
import '../../../thermion_dart.dart';
|
import '../../../thermion_dart.dart';
|
||||||
|
|
||||||
class GeometryHelper {
|
class GeometryHelper {
|
||||||
|
static Geometry fullscreenQuad() {
|
||||||
|
final vertices = Float32List.fromList(
|
||||||
|
[-1.0, -1.0, 1.0, 1.0, 3.0, -1.0, 1.0, 1.0, -1.0, 3.0, 1.0, 1.0]);
|
||||||
|
final indices = [0, 1, 2];
|
||||||
|
return Geometry(vertices, indices);
|
||||||
|
}
|
||||||
|
|
||||||
static Geometry sphere({bool normals = true, bool uvs = true}) {
|
static Geometry sphere({bool normals = true, bool uvs = true}) {
|
||||||
int latitudeBands = 20;
|
int latitudeBands = 20;
|
||||||
int longitudeBands = 20;
|
int longitudeBands = 20;
|
||||||
@@ -459,13 +466,18 @@ class GeometryHelper {
|
|||||||
])
|
])
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
Float32List? _uvs =
|
Float32List? _uvs = uvs
|
||||||
uvs ? Float32List.fromList([
|
? Float32List.fromList([
|
||||||
0, 0,
|
0,
|
||||||
1, 0,
|
0,
|
||||||
1, 1,
|
1,
|
||||||
0, 1,
|
0,
|
||||||
]) : null;
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
])
|
||||||
|
: null;
|
||||||
|
|
||||||
List<int> indices = [
|
List<int> indices = [
|
||||||
0,
|
0,
|
||||||
|
|||||||
262
thermion_dart/lib/src/viewer/src/ffi/src/background_image.dart
Normal file
262
thermion_dart/lib/src/viewer/src/ffi/src/background_image.dart
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:animation_tools_dart/src/bone_animation_data.dart';
|
||||||
|
import 'package:animation_tools_dart/src/morph_animation_data.dart';
|
||||||
|
import 'package:thermion_dart/src/filament/src/layers.dart';
|
||||||
|
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
|
||||||
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart';
|
||||||
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
|
||||||
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_material.dart';
|
||||||
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_scene.dart';
|
||||||
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_texture.dart';
|
||||||
|
import 'package:thermion_dart/thermion_dart.dart';
|
||||||
|
|
||||||
|
class BackgroundImage extends ThermionAsset {
|
||||||
|
final ThermionAsset asset;
|
||||||
|
ThermionEntity get entity => asset.entity;
|
||||||
|
|
||||||
|
Texture? _backgroundImageTexture;
|
||||||
|
FFIMaterial? _imageMaterial;
|
||||||
|
FFITextureSampler? _imageSampler;
|
||||||
|
|
||||||
|
final FFIScene scene;
|
||||||
|
final FilamentApp app;
|
||||||
|
|
||||||
|
BackgroundImage._(this.asset, this.scene, this.app,
|
||||||
|
this._backgroundImageTexture, this._imageMaterial, this._imageSampler);
|
||||||
|
|
||||||
|
Future destroy() async {
|
||||||
|
Scene_removeEntity(scene.scene, entity);
|
||||||
|
await _backgroundImageTexture!.dispose();
|
||||||
|
await _imageSampler!.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<BackgroundImage> create(
|
||||||
|
FFIFilamentApp app, FFIScene scene, Uint8List imageData) async {
|
||||||
|
final image = await app.decodeImage(imageData);
|
||||||
|
var backgroundImageTexture = await app.createTexture(
|
||||||
|
await image.getWidth(), await image.getHeight());
|
||||||
|
var imageMaterial = FFIMaterial(Material_createImageMaterial(), app);
|
||||||
|
var imageSampler = await app.createTextureSampler() as FFITextureSampler;
|
||||||
|
|
||||||
|
var imageMaterialInstance =
|
||||||
|
await imageMaterial!.createInstance() as FFIMaterialInstance;
|
||||||
|
await imageMaterialInstance.setParameterTexture(
|
||||||
|
"image",
|
||||||
|
backgroundImageTexture as FFITexture,
|
||||||
|
imageSampler as FFITextureSampler);
|
||||||
|
var backgroundImage =
|
||||||
|
await app.createGeometry(GeometryHelper.fullscreenQuad()) as FFIAsset;
|
||||||
|
backgroundImage.setMaterialInstanceAt(imageMaterialInstance);
|
||||||
|
await scene.add(backgroundImage);
|
||||||
|
return BackgroundImage._(backgroundImage, scene, app,
|
||||||
|
backgroundImageTexture, imageMaterial, imageSampler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 setBoundingBoxVisibility(bool visible) {
|
||||||
|
// TODO: implement setBoundingBoxVisibility
|
||||||
|
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(ThermionAsset asset, 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(covariant ThermionAsset asset, int animationIndex) {
|
||||||
|
// TODO: implement getAnimationDuration
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<String>> getAnimationNames(covariant ThermionAsset asset) {
|
||||||
|
// TODO: implement getAnimationNames
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<ThermionEntity> getBone(covariant ThermionAsset asset, int boneIndex, {int skinIndex = 0}) {
|
||||||
|
// TODO: implement getBone
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<String>> getBoneNames(covariant ThermionAsset asset, {int skinIndex = 0}) {
|
||||||
|
// TODO: implement getBoneNames
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Matrix4> getInverseBindMatrix(covariant ThermionAsset asset, 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(covariant ThermionAsset asset, ThermionEntity childEntity) {
|
||||||
|
// TODO: implement getMorphTargetNames
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Matrix4> getWorldTransform(ThermionEntity entity) {
|
||||||
|
// TODO: implement getWorldTransform
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future playAnimation(ThermionAsset asset, 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(covariant ThermionAsset asset, 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(ThermionAsset asset) {
|
||||||
|
// 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(covariant ThermionAsset asset, int index, int animationFrame) {
|
||||||
|
// TODO: implement setGltfAnimationFrame
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future setMorphAnimationData(covariant ThermionAsset asset, 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(ThermionEntity entity, Matrix4 transform) {
|
||||||
|
// TODO: implement setTransform
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future stopAnimation(covariant ThermionAsset asset, int animationIndex) {
|
||||||
|
// TODO: implement stopAnimation
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future stopAnimationByName(covariant ThermionAsset asset, String name) {
|
||||||
|
// TODO: implement stopAnimationByName
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future updateBoneMatrices(ThermionEntity entity) {
|
||||||
|
// TODO: implement updateBoneMatrices
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,17 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:thermion_dart/src/filament/src/layers.dart';
|
||||||
|
import 'package:thermion_dart/src/utils/src/matrix.dart';
|
||||||
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
|
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
|
||||||
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
|
||||||
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_material.dart';
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_material.dart';
|
||||||
import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_viewer_ffi.dart';
|
import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_viewer_ffi.dart';
|
||||||
import 'package:thermion_dart/thermion_dart.dart';
|
import 'package:thermion_dart/thermion_dart.dart';
|
||||||
import 'package:vector_math/vector_math_64.dart' as v64;
|
import 'package:vector_math/vector_math_64.dart' as v64;
|
||||||
|
import 'package:vector_math/vector_math_64.dart';
|
||||||
|
|
||||||
class FFIAsset extends ThermionAsset {
|
class FFIAsset extends ThermionAsset {
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
@@ -389,7 +393,477 @@ class FFIAsset extends ThermionAsset {
|
|||||||
return RenderableManager_isShadowReceiver(app.renderableManager, entity);
|
return RenderableManager_isShadowReceiver(app.renderableManager, entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
Future transformToUnitCube() async {
|
Future transformToUnitCube() async {
|
||||||
SceneAsset_transformToUnitCube(asset);
|
TransformManager_transformToUnitCube(app.transformManager, entity, SceneAsset_getBoundingBox(asset));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future setVisibilityLayer(
|
||||||
|
ThermionEntity entity, VisibilityLayers layer) async {
|
||||||
|
RenderableManager_setVisibilityLayer(
|
||||||
|
app.renderableManager, entity, layer.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
Future setMorphTargetWeights(
|
||||||
|
ThermionEntity entity, List<double> weights) async {
|
||||||
|
if (weights.isEmpty) {
|
||||||
|
throw Exception("Weights must not be empty");
|
||||||
|
}
|
||||||
|
var weightsPtr = allocator<Float>(weights.length);
|
||||||
|
|
||||||
|
for (int i = 0; i < weights.length; i++) {
|
||||||
|
weightsPtr[i] = weights[i];
|
||||||
|
}
|
||||||
|
var success = await withBoolCallback((cb) {
|
||||||
|
AnimationManager_setMorphTargetWeightsRenderThread(
|
||||||
|
animationManager, entity, weightsPtr, weights.length, cb);
|
||||||
|
});
|
||||||
|
allocator.free(weightsPtr);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
throw Exception(
|
||||||
|
"Failed to set morph target weights, check logs for details");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
Future<List<String>> getMorphTargetNames(
|
||||||
|
covariant FFIAsset asset, ThermionEntity childEntity) async {
|
||||||
|
var names = <String>[];
|
||||||
|
|
||||||
|
var count = AnimationManager_getMorphTargetNameCount(
|
||||||
|
animationManager, asset.asset, childEntity);
|
||||||
|
var outPtr = allocator<Char>(255);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
AnimationManager_getMorphTargetName(
|
||||||
|
animationManager, asset.asset, childEntity, outPtr, i);
|
||||||
|
names.add(outPtr.cast<Utf8>().toDartString());
|
||||||
|
}
|
||||||
|
allocator.free(outPtr);
|
||||||
|
return names.cast<String>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<String>> getBoneNames(covariant FFIAsset asset,
|
||||||
|
{int skinIndex = 0}) async {
|
||||||
|
var count =
|
||||||
|
AnimationManager_getBoneCount(animationManager, asset.asset, skinIndex);
|
||||||
|
var out = allocator<Pointer<Char>>(count);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
out[i] = allocator<Char>(255);
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimationManager_getBoneNames(
|
||||||
|
animationManager, asset.asset, out, skinIndex);
|
||||||
|
var names = <String>[];
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
var namePtr = out[i];
|
||||||
|
names.add(namePtr.cast<Utf8>().toDartString());
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
Future<List<String>> getAnimationNames(covariant FFIAsset asset) async {
|
||||||
|
var animationCount =
|
||||||
|
AnimationManager_getAnimationCount(animationManager, asset.asset);
|
||||||
|
var names = <String>[];
|
||||||
|
var outPtr = allocator<Char>(255);
|
||||||
|
for (int i = 0; i < animationCount; i++) {
|
||||||
|
AnimationManager_getAnimationName(
|
||||||
|
animationManager, asset.asset, outPtr, i);
|
||||||
|
names.add(outPtr.cast<Utf8>().toDartString());
|
||||||
|
}
|
||||||
|
allocator.free(outPtr);
|
||||||
|
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
Future<double> getAnimationDuration(
|
||||||
|
FFIAsset asset, int animationIndex) async {
|
||||||
|
return AnimationManager_getAnimationDuration(
|
||||||
|
animationManager, asset.asset, animationIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future<double> getAnimationDurationByName(FFIAsset asset, String name) async {
|
||||||
|
var animations = await getAnimationNames(asset);
|
||||||
|
var index = animations.indexOf(name);
|
||||||
|
if (index == -1) {
|
||||||
|
throw Exception("Failed to find animation $name");
|
||||||
|
}
|
||||||
|
return getAnimationDuration(asset, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future clearMorphAnimationData(ThermionEntity entity) async {
|
||||||
|
if (!AnimationManager_clearMorphAnimation(animationManager, entity)) {
|
||||||
|
throw Exception("Failed to clear morph animation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
Future setMorphAnimationData(FFIAsset asset, MorphAnimationData animation,
|
||||||
|
{List<String>? targetMeshNames}) async {
|
||||||
|
var meshEntities = await getChildEntities(asset);
|
||||||
|
|
||||||
|
var meshNames = meshEntities.map((e) => 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(asset, meshEntity);
|
||||||
|
|
||||||
|
var intersection = animation.morphTargets
|
||||||
|
.toSet()
|
||||||
|
.intersection(meshMorphTargets.toSet())
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (intersection.isEmpty) {
|
||||||
|
throw Exception(
|
||||||
|
"""No morph targets specified in animation are present on mesh $meshName.
|
||||||
|
If you weren't intending to animate every mesh, specify [targetMeshNames] when invoking this method.
|
||||||
|
Animation morph targets: ${animation.morphTargets}\n
|
||||||
|
Mesh morph targets ${meshMorphTargets}
|
||||||
|
Child meshes: ${meshNames}""");
|
||||||
|
}
|
||||||
|
|
||||||
|
var indices = Uint32List.fromList(
|
||||||
|
intersection.map((m) => meshMorphTargets.indexOf(m)).toList());
|
||||||
|
|
||||||
|
// var frameData = animation.data;
|
||||||
|
var frameData = animation.subset(intersection);
|
||||||
|
|
||||||
|
assert(
|
||||||
|
frameData.data.length == animation.numFrames * intersection.length);
|
||||||
|
|
||||||
|
var result = AnimationManager_setMorphAnimation(
|
||||||
|
animationManager,
|
||||||
|
meshEntity,
|
||||||
|
frameData.data.address,
|
||||||
|
indices.address,
|
||||||
|
indices.length,
|
||||||
|
animation.numFrames,
|
||||||
|
animation.frameLengthInMs);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
throw Exception("Failed to set morph animation data for ${meshName}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Currently, scale is not supported.
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
Future addBoneAnimation(covariant FFIAsset asset, 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(asset);
|
||||||
|
var restLocalTransformsRaw = allocator<Float>(boneNames.length * 16);
|
||||||
|
AnimationManager_getRestLocalTransforms(animationManager, asset.asset,
|
||||||
|
skinIndex, restLocalTransformsRaw, boneNames.length);
|
||||||
|
|
||||||
|
var restLocalTransforms = <Matrix4>[];
|
||||||
|
for (int i = 0; i < boneNames.length; i++) {
|
||||||
|
var values = <double>[];
|
||||||
|
for (int j = 0; j < 16; j++) {
|
||||||
|
values.add(restLocalTransformsRaw[(i * 16) + j]);
|
||||||
|
}
|
||||||
|
restLocalTransforms.add(Matrix4.fromList(values));
|
||||||
|
}
|
||||||
|
allocator.free(restLocalTransformsRaw);
|
||||||
|
|
||||||
|
var numFrames = animation.frameData.length;
|
||||||
|
|
||||||
|
var data = allocator<Float>(numFrames * 16);
|
||||||
|
|
||||||
|
var bones = await Future.wait(List<Future<ThermionEntity>>.generate(
|
||||||
|
boneNames.length, (i) => getBone(asset, 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 getParent(boneEntity))!;
|
||||||
|
while (true) {
|
||||||
|
if (!bones.contains(parentBoneEntity!)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
world = restLocalTransforms[bones.indexOf(parentBoneEntity!)] * world;
|
||||||
|
parentBoneEntity = (await getParent(parentBoneEntity))!;
|
||||||
|
}
|
||||||
|
|
||||||
|
world = Matrix4.identity()..setRotation(world.getRotation());
|
||||||
|
var worldInverse = Matrix4.identity()..copyInverse(world);
|
||||||
|
|
||||||
|
for (int frameNum = 0; frameNum < numFrames; frameNum++) {
|
||||||
|
var rotation = animation.frameData[frameNum][i].rotation;
|
||||||
|
var translation = animation.frameData[frameNum][i].translation;
|
||||||
|
var frameTransform =
|
||||||
|
Matrix4.compose(translation, rotation, Vector3.all(1.0));
|
||||||
|
var newLocalTransform = frameTransform.clone();
|
||||||
|
if (animation.space == Space.Bone) {
|
||||||
|
newLocalTransform = baseTransform * frameTransform;
|
||||||
|
} else if (animation.space == Space.ParentWorldRotation) {
|
||||||
|
newLocalTransform =
|
||||||
|
baseTransform * (worldInverse * frameTransform * world);
|
||||||
|
}
|
||||||
|
for (int j = 0; j < 16; j++) {
|
||||||
|
data.elementAt((frameNum * 16) + j).value =
|
||||||
|
newLocalTransform.storage[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimationManager_addBoneAnimation(
|
||||||
|
animationManager,
|
||||||
|
asset.asset,
|
||||||
|
skinIndex,
|
||||||
|
entityBoneIndex,
|
||||||
|
data,
|
||||||
|
numFrames,
|
||||||
|
animation.frameLengthInMs,
|
||||||
|
fadeOutInSecs,
|
||||||
|
fadeInInSecs,
|
||||||
|
maxDelta);
|
||||||
|
}
|
||||||
|
allocator.free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future<Matrix4> getLocalTransform(ThermionEntity entity) async {
|
||||||
|
return double4x4ToMatrix4(
|
||||||
|
TransformManager_getLocalTransform(app.transformManager, entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future<Matrix4> getWorldTransform(ThermionEntity entity) async {
|
||||||
|
return double4x4ToMatrix4(
|
||||||
|
TransformManager_getWorldTransform(app.transformManager, entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future setTransform(ThermionEntity entity, Matrix4 transform) async {
|
||||||
|
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(FFIAsset asset, int boneIndex,
|
||||||
|
{int skinIndex = 0}) async {
|
||||||
|
var matrix = Float32List(16);
|
||||||
|
AnimationManager_getInverseBindMatrix(
|
||||||
|
animationManager, asset.asset, skinIndex, boneIndex, matrix.address);
|
||||||
|
return Matrix4.fromList(matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future<ThermionEntity> getBone(FFIAsset asset, int boneIndex,
|
||||||
|
{int skinIndex = 0}) async {
|
||||||
|
if (skinIndex != 0) {
|
||||||
|
throw UnimplementedError("TOOD");
|
||||||
|
}
|
||||||
|
return AnimationManager_getBone(
|
||||||
|
animationManager, asset.asset, skinIndex, boneIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
Future setBoneTransform(
|
||||||
|
ThermionEntity entity, int boneIndex, Matrix4 transform,
|
||||||
|
{int skinIndex = 0}) async {
|
||||||
|
if (skinIndex != 0) {
|
||||||
|
throw UnimplementedError("TOOD");
|
||||||
|
}
|
||||||
|
final ptr = allocator<Float>(16);
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
ptr[i] = transform.storage[i];
|
||||||
|
}
|
||||||
|
var result = await withBoolCallback((cb) {
|
||||||
|
AnimationManager_setBoneTransformRenderThread(
|
||||||
|
animationManager, entity, skinIndex, boneIndex, ptr, cb);
|
||||||
|
});
|
||||||
|
|
||||||
|
allocator.free(ptr);
|
||||||
|
if (!result) {
|
||||||
|
throw Exception("Failed to set bone transform");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
Future resetBones(covariant FFIAsset asset) async {
|
||||||
|
AnimationManager_resetToRestPose(animationManager, asset.asset);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
Future playAnimation(covariant FFIAsset asset, int index,
|
||||||
|
{bool loop = false,
|
||||||
|
bool reverse = false,
|
||||||
|
bool replaceActive = true,
|
||||||
|
double crossfade = 0.0,
|
||||||
|
double startOffset = 0.0}) async {
|
||||||
|
AnimationManager_playAnimation(animationManager, asset.asset, index, loop,
|
||||||
|
reverse, replaceActive, crossfade, startOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
Future stopAnimation(FFIAsset asset, int animationIndex) async {
|
||||||
|
AnimationManager_stopAnimation(
|
||||||
|
animationManager, asset.asset, animationIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
Future stopAnimationByName(FFIAsset asset, String name) async {
|
||||||
|
var animations = await getAnimationNames(asset);
|
||||||
|
await stopAnimation(asset, animations.indexOf(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
Future playAnimationByName(FFIAsset asset, String name,
|
||||||
|
{bool loop = false,
|
||||||
|
bool reverse = false,
|
||||||
|
bool replaceActive = true,
|
||||||
|
double crossfade = 0.0,
|
||||||
|
bool wait = false}) async {
|
||||||
|
var animations = await getAnimationNames(asset);
|
||||||
|
var index = animations.indexOf(name);
|
||||||
|
var duration = await getAnimationDuration(asset, index);
|
||||||
|
await playAnimation(asset, index,
|
||||||
|
loop: loop,
|
||||||
|
reverse: reverse,
|
||||||
|
replaceActive: replaceActive,
|
||||||
|
crossfade: crossfade);
|
||||||
|
if (wait) {
|
||||||
|
await Future.delayed(Duration(milliseconds: (duration * 1000).toInt()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
Future setGltfAnimationFrame(
|
||||||
|
FFIAsset asset, int index, int animationFrame) async {
|
||||||
|
AnimationManager_setGltfAnimationFrame(
|
||||||
|
animationManager, asset.asset, index, animationFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
Future addAnimationComponent(ThermionEntity entity) async {
|
||||||
|
AnimationManager_addAnimationComponent(animationManager, entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future removeAnimationComponent(ThermionEntity entity) async {
|
||||||
|
AnimationManager_removeAnimationComponent(animationManager, entity);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import 'dart:typed_data';
|
|||||||
|
|
||||||
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
|
||||||
import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_dart.g.dart';
|
import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_dart.g.dart';
|
||||||
import 'package:thermion_dart/src/viewer/src/shared_types/layers.dart';
|
import 'package:thermion_dart/src/filament/src/layers.dart';
|
||||||
|
import 'package:thermion_dart/thermion_dart.dart';
|
||||||
import 'package:vector_math/vector_math_64.dart';
|
import 'package:vector_math/vector_math_64.dart';
|
||||||
import '../../../../utils/src/matrix.dart';
|
import '../../../../utils/src/matrix.dart';
|
||||||
import '../../thermion_viewer_base.dart';
|
|
||||||
|
|
||||||
class FFICamera extends Camera {
|
class FFICamera extends Camera {
|
||||||
final Pointer<TCamera> camera;
|
final Pointer<TCamera> camera;
|
||||||
@@ -150,4 +150,12 @@ class FFICamera extends Camera {
|
|||||||
Future destroy() async {
|
Future destroy() async {
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
|
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
|
||||||
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart';
|
||||||
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_material.dart';
|
||||||
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_render_target.dart';
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_render_target.dart';
|
||||||
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_swapchain.dart';
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_swapchain.dart';
|
||||||
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_texture.dart';
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_texture.dart';
|
||||||
import 'package:thermion_dart/src/viewer/src/filament/filament.dart';
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_view.dart';
|
||||||
import 'package:thermion_dart/thermion_dart.dart';
|
import 'package:thermion_dart/thermion_dart.dart';
|
||||||
|
|
||||||
typedef RenderCallback = Pointer<NativeFunction<Void Function(Pointer<Void>)>>;
|
typedef RenderCallback = Pointer<NativeFunction<Void Function(Pointer<Void>)>>;
|
||||||
@@ -11,12 +16,13 @@ class FFIFilamentConfig extends FilamentConfig<RenderCallback, Pointer<Void>> {
|
|||||||
FFIFilamentConfig(
|
FFIFilamentConfig(
|
||||||
{required super.backend,
|
{required super.backend,
|
||||||
required super.resourceLoader,
|
required super.resourceLoader,
|
||||||
|
required super.driver,
|
||||||
|
required super.platform,
|
||||||
|
required super.sharedContext,
|
||||||
required super.uberArchivePath});
|
required super.uberArchivePath});
|
||||||
}
|
}
|
||||||
|
|
||||||
class FFIFilamentApp extends FilamentApp<Pointer> {
|
class FFIFilamentApp extends FilamentApp<Pointer> {
|
||||||
static FFIFilamentApp? _instance;
|
|
||||||
|
|
||||||
final Pointer<TEngine> engine;
|
final Pointer<TEngine> engine;
|
||||||
final Pointer<TGltfAssetLoader> gltfAssetLoader;
|
final Pointer<TGltfAssetLoader> gltfAssetLoader;
|
||||||
final Pointer<TGltfResourceLoader> gltfResourceLoader;
|
final Pointer<TGltfResourceLoader> gltfResourceLoader;
|
||||||
@@ -47,53 +53,82 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
|
|||||||
transformManager: transformManager,
|
transformManager: transformManager,
|
||||||
lightManager: lightManager,
|
lightManager: lightManager,
|
||||||
renderableManager: renderableManager,
|
renderableManager: renderableManager,
|
||||||
ubershaderMaterialProvider: ubershaderMaterialProvider);
|
ubershaderMaterialProvider: ubershaderMaterialProvider) {}
|
||||||
|
|
||||||
Future<FFIFilamentApp> create(FFIFilamentConfig config) async {
|
static Future create(FFIFilamentConfig config) async {
|
||||||
if (_instance == null) {
|
if (FilamentApp.instance != null) {
|
||||||
RenderLoop_destroy();
|
await FilamentApp.instance!.destroy();
|
||||||
RenderLoop_create();
|
|
||||||
|
|
||||||
final engine = await withPointerCallback<TEngine>((cb) =>
|
|
||||||
Engine_createRenderThread(
|
|
||||||
TBackend.values[config.backend.index].index,
|
|
||||||
config.platform ?? nullptr,
|
|
||||||
config.sharedContext ?? nullptr,
|
|
||||||
config.stereoscopicEyeCount,
|
|
||||||
config.disableHandleUseAfterFreeCheck,
|
|
||||||
cb));
|
|
||||||
|
|
||||||
final gltfResourceLoader = await withPointerCallback<TGltfResourceLoader>(
|
|
||||||
(cb) => GltfResourceLoader_createRenderThread(engine, cb));
|
|
||||||
final gltfAssetLoader = await withPointerCallback<TGltfAssetLoader>(
|
|
||||||
(cb) => GltfAssetLoader_createRenderThread(engine, nullptr, cb));
|
|
||||||
final renderer = await withPointerCallback<TRenderer>(
|
|
||||||
(cb) => Engine_createRendererRenderThread(engine, cb));
|
|
||||||
final ubershaderMaterialProvider =
|
|
||||||
await withPointerCallback<TMaterialProvider>(
|
|
||||||
(cb) => GltfAssetLoader_getMaterialProvider(gltfAssetLoader));
|
|
||||||
|
|
||||||
final transformManager = Engine_getTransformManager(engine);
|
|
||||||
final lightManager = Engine_getLightManager(engine);
|
|
||||||
final renderableManager = Engine_getRenderableManager(engine);
|
|
||||||
|
|
||||||
final renderTicker = await withPointerCallback<TRenderTicker>(
|
|
||||||
(cb) => RenderTicker_create(renderer));
|
|
||||||
|
|
||||||
final nameComponentManager = NameComponentManager_create();
|
|
||||||
|
|
||||||
_instance = FFIFilamentApp(
|
|
||||||
engine,
|
|
||||||
gltfAssetLoader,
|
|
||||||
gltfResourceLoader,
|
|
||||||
renderer,
|
|
||||||
transformManager,
|
|
||||||
lightManager,
|
|
||||||
renderableManager,
|
|
||||||
ubershaderMaterialProvider,
|
|
||||||
renderTicker, nameComponentManager);
|
|
||||||
}
|
}
|
||||||
return _instance!;
|
|
||||||
|
RenderLoop_destroy();
|
||||||
|
RenderLoop_create();
|
||||||
|
|
||||||
|
final engine = await withPointerCallback<TEngine>((cb) =>
|
||||||
|
Engine_createRenderThread(
|
||||||
|
TBackend.values[config.backend.index].index,
|
||||||
|
config.platform ?? nullptr,
|
||||||
|
config.sharedContext ?? nullptr,
|
||||||
|
config.stereoscopicEyeCount,
|
||||||
|
config.disableHandleUseAfterFreeCheck,
|
||||||
|
cb));
|
||||||
|
|
||||||
|
final gltfResourceLoader = await withPointerCallback<TGltfResourceLoader>(
|
||||||
|
(cb) => GltfResourceLoader_createRenderThread(engine, cb));
|
||||||
|
final gltfAssetLoader = await withPointerCallback<TGltfAssetLoader>(
|
||||||
|
(cb) => GltfAssetLoader_createRenderThread(engine, nullptr, cb));
|
||||||
|
final renderer = await withPointerCallback<TRenderer>(
|
||||||
|
(cb) => Engine_createRendererRenderThread(engine, cb));
|
||||||
|
final ubershaderMaterialProvider =
|
||||||
|
await withPointerCallback<TMaterialProvider>(
|
||||||
|
(cb) => GltfAssetLoader_getMaterialProvider(gltfAssetLoader));
|
||||||
|
|
||||||
|
final transformManager = Engine_getTransformManager(engine);
|
||||||
|
final lightManager = Engine_getLightManager(engine);
|
||||||
|
final renderableManager = Engine_getRenderableManager(engine);
|
||||||
|
|
||||||
|
final renderTicker = await withPointerCallback<TRenderTicker>(
|
||||||
|
(cb) => RenderTicker_create(renderer));
|
||||||
|
|
||||||
|
final nameComponentManager = NameComponentManager_create();
|
||||||
|
|
||||||
|
FilamentApp.instance = FFIFilamentApp(
|
||||||
|
engine,
|
||||||
|
gltfAssetLoader,
|
||||||
|
gltfResourceLoader,
|
||||||
|
renderer,
|
||||||
|
transformManager,
|
||||||
|
lightManager,
|
||||||
|
renderableManager,
|
||||||
|
ubershaderMaterialProvider,
|
||||||
|
renderTicker,
|
||||||
|
nameComponentManager);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
final _views = <FFISwapChain, List<FFIView>>{};
|
||||||
|
final _viewMappings = <FFIView, FFISwapChain>{};
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future setRenderable(covariant FFIView view, bool renderable) async {
|
||||||
|
final swapChain = _viewMappings[view]!;
|
||||||
|
if (renderable && !_views[swapChain]!.contains(view)) {
|
||||||
|
_views[swapChain]!.add(view);
|
||||||
|
} else if (!renderable && _views[swapChain]!.contains(view)) {
|
||||||
|
_views[swapChain]!.remove(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
final views = calloc<Pointer<TView>>(255);
|
||||||
|
for (final swapChain in _views.keys) {
|
||||||
|
var numViews = _views[swapChain]!.length;
|
||||||
|
for (int i = 0; i < numViews; i++) {
|
||||||
|
views[i] = _views[swapChain]![i].view;
|
||||||
|
}
|
||||||
|
RenderTicker_setRenderable(
|
||||||
|
renderTicker, swapChain.swapChain, views, numViews);
|
||||||
|
}
|
||||||
|
calloc.free(views);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -136,8 +171,12 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future destroy() {
|
Future destroy() async {
|
||||||
throw UnimplementedError();
|
for (final swapChain in _views.keys) {
|
||||||
|
for (final view in _views[swapChain]!) {
|
||||||
|
await setRenderable(view, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -153,16 +192,316 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
|
|||||||
return FFIRenderTarget(renderTarget, this);
|
return FFIRenderTarget(renderTarget, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ///
|
///
|
||||||
// ///
|
///
|
||||||
// ///
|
///
|
||||||
// Future<RenderTarget> createRenderTarget(int width, int height,
|
Future<Texture> createTexture(int width, int height,
|
||||||
// {covariant FFITexture? color, covariant FFITexture? depth}) async {
|
{int depth = 1,
|
||||||
// final renderTarget = await withPointerCallback<TRenderTarget>((cb) {
|
int levels = 1,
|
||||||
// RenderTarget_createRenderThread(app.engine, width, height,
|
Set<TextureUsage> flags = const {TextureUsage.TEXTURE_USAGE_SAMPLEABLE},
|
||||||
// color?.pointer ?? nullptr, depth?.pointer ?? nullptr, cb);
|
TextureSamplerType textureSamplerType = TextureSamplerType.SAMPLER_2D,
|
||||||
// });
|
TextureFormat textureFormat = TextureFormat.RGBA16F,
|
||||||
|
int? importedTextureHandle}) async {
|
||||||
|
var bitmask = flags.fold(0, (a, b) => a | b.index);
|
||||||
|
final texturePtr = await withPointerCallback<TTexture>((cb) {
|
||||||
|
Texture_buildRenderThread(
|
||||||
|
engine!,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
depth,
|
||||||
|
levels,
|
||||||
|
importedTextureHandle ?? 0,
|
||||||
|
bitmask,
|
||||||
|
TTextureSamplerType.values[textureSamplerType.index],
|
||||||
|
TTextureFormat.values[textureFormat.index],
|
||||||
|
cb);
|
||||||
|
});
|
||||||
|
if (texturePtr == nullptr) {
|
||||||
|
throw Exception("Failed to create texture");
|
||||||
|
}
|
||||||
|
return FFITexture(
|
||||||
|
engine!,
|
||||||
|
texturePtr,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// return FFIRenderTarget(renderTarget, app);
|
Future<TextureSampler> createTextureSampler(
|
||||||
// }
|
{TextureMinFilter minFilter = TextureMinFilter.LINEAR,
|
||||||
|
TextureMagFilter magFilter = TextureMagFilter.LINEAR,
|
||||||
|
TextureWrapMode wrapS = TextureWrapMode.CLAMP_TO_EDGE,
|
||||||
|
TextureWrapMode wrapT = TextureWrapMode.CLAMP_TO_EDGE,
|
||||||
|
TextureWrapMode wrapR = TextureWrapMode.CLAMP_TO_EDGE,
|
||||||
|
double anisotropy = 0.0,
|
||||||
|
TextureCompareMode compareMode = TextureCompareMode.NONE,
|
||||||
|
TextureCompareFunc compareFunc = TextureCompareFunc.LESS_EQUAL}) async {
|
||||||
|
final samplerPtr = TextureSampler_create();
|
||||||
|
TextureSampler_setMinFilter(
|
||||||
|
samplerPtr, TSamplerMinFilter.values[minFilter.index]);
|
||||||
|
TextureSampler_setMagFilter(
|
||||||
|
samplerPtr, TSamplerMagFilter.values[magFilter.index]);
|
||||||
|
TextureSampler_setWrapModeS(
|
||||||
|
samplerPtr, TSamplerWrapMode.values[wrapS.index]);
|
||||||
|
TextureSampler_setWrapModeT(
|
||||||
|
samplerPtr, TSamplerWrapMode.values[wrapT.index]);
|
||||||
|
TextureSampler_setWrapModeR(
|
||||||
|
samplerPtr, TSamplerWrapMode.values[wrapR.index]);
|
||||||
|
if (anisotropy > 0) {
|
||||||
|
TextureSampler_setAnisotropy(samplerPtr, anisotropy);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureSampler_setCompareMode(
|
||||||
|
samplerPtr,
|
||||||
|
TSamplerCompareMode.values[compareMode.index],
|
||||||
|
TSamplerCompareFunc.values[compareFunc.index]);
|
||||||
|
|
||||||
|
return FFITextureSampler(samplerPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future<LinearImage> decodeImage(Uint8List data) async {
|
||||||
|
final name = "image";
|
||||||
|
var ptr = Image_decode(
|
||||||
|
data.address,
|
||||||
|
data.length,
|
||||||
|
name.toNativeUtf8().cast<Char>(),
|
||||||
|
);
|
||||||
|
if (ptr == nullptr) {
|
||||||
|
throw Exception("Failed to decode image");
|
||||||
|
}
|
||||||
|
return FFILinearImage(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Creates an (empty) imge with the given dimensions.
|
||||||
|
///
|
||||||
|
Future<LinearImage> createImage(int width, int height, int channels) async {
|
||||||
|
final ptr = Image_createEmpty(width, height, channels);
|
||||||
|
return FFILinearImage(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future<Material> createMaterial(Uint8List data) async {
|
||||||
|
var ptr = await withPointerCallback<TMaterial>((cb) {
|
||||||
|
Engine_buildMaterialRenderThread(engine!, data.address, data.length, cb);
|
||||||
|
});
|
||||||
|
return FFIMaterial(ptr, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future<MaterialInstance> createUbershaderMaterialInstance(
|
||||||
|
{bool doubleSided = false,
|
||||||
|
bool unlit = false,
|
||||||
|
bool hasVertexColors = false,
|
||||||
|
bool hasBaseColorTexture = false,
|
||||||
|
bool hasNormalTexture = false,
|
||||||
|
bool hasOcclusionTexture = false,
|
||||||
|
bool hasEmissiveTexture = false,
|
||||||
|
bool useSpecularGlossiness = false,
|
||||||
|
AlphaMode alphaMode = AlphaMode.OPAQUE,
|
||||||
|
bool enableDiagnostics = false,
|
||||||
|
bool hasMetallicRoughnessTexture = false,
|
||||||
|
int metallicRoughnessUV = 0,
|
||||||
|
int baseColorUV = 0,
|
||||||
|
bool hasClearCoatTexture = false,
|
||||||
|
int clearCoatUV = 0,
|
||||||
|
bool hasClearCoatRoughnessTexture = false,
|
||||||
|
int clearCoatRoughnessUV = 0,
|
||||||
|
bool hasClearCoatNormalTexture = false,
|
||||||
|
int clearCoatNormalUV = 0,
|
||||||
|
bool hasClearCoat = false,
|
||||||
|
bool hasTransmission = false,
|
||||||
|
bool hasTextureTransforms = false,
|
||||||
|
int emissiveUV = 0,
|
||||||
|
int aoUV = 0,
|
||||||
|
int normalUV = 0,
|
||||||
|
bool hasTransmissionTexture = false,
|
||||||
|
int transmissionUV = 0,
|
||||||
|
bool hasSheenColorTexture = false,
|
||||||
|
int sheenColorUV = 0,
|
||||||
|
bool hasSheenRoughnessTexture = false,
|
||||||
|
int sheenRoughnessUV = 0,
|
||||||
|
bool hasVolumeThicknessTexture = false,
|
||||||
|
int volumeThicknessUV = 0,
|
||||||
|
bool hasSheen = false,
|
||||||
|
bool hasIOR = false,
|
||||||
|
bool hasVolume = false}) async {
|
||||||
|
final key = Struct.create<TMaterialKey>();
|
||||||
|
|
||||||
|
key.doubleSided = doubleSided;
|
||||||
|
key.unlit = unlit;
|
||||||
|
key.hasVertexColors = hasVertexColors;
|
||||||
|
key.hasBaseColorTexture = hasBaseColorTexture;
|
||||||
|
key.hasNormalTexture = hasNormalTexture;
|
||||||
|
key.hasOcclusionTexture = hasOcclusionTexture;
|
||||||
|
key.hasEmissiveTexture = hasEmissiveTexture;
|
||||||
|
key.useSpecularGlossiness = useSpecularGlossiness;
|
||||||
|
key.alphaMode = alphaMode.index;
|
||||||
|
key.enableDiagnostics = enableDiagnostics;
|
||||||
|
key.unnamed.unnamed.hasMetallicRoughnessTexture =
|
||||||
|
hasMetallicRoughnessTexture;
|
||||||
|
key.unnamed.unnamed.metallicRoughnessUV = 0;
|
||||||
|
key.baseColorUV = baseColorUV;
|
||||||
|
key.hasClearCoatTexture = hasClearCoatTexture;
|
||||||
|
key.clearCoatUV = clearCoatUV;
|
||||||
|
key.hasClearCoatRoughnessTexture = hasClearCoatRoughnessTexture;
|
||||||
|
key.clearCoatRoughnessUV = clearCoatRoughnessUV;
|
||||||
|
key.hasClearCoatNormalTexture = hasClearCoatNormalTexture;
|
||||||
|
key.clearCoatNormalUV = clearCoatNormalUV;
|
||||||
|
key.hasClearCoat = hasClearCoat;
|
||||||
|
key.hasTransmission = hasTransmission;
|
||||||
|
key.hasTextureTransforms = hasTextureTransforms;
|
||||||
|
key.emissiveUV = emissiveUV;
|
||||||
|
key.aoUV = aoUV;
|
||||||
|
key.normalUV = normalUV;
|
||||||
|
key.hasTransmissionTexture = hasTransmissionTexture;
|
||||||
|
key.transmissionUV = transmissionUV;
|
||||||
|
key.hasSheenColorTexture = hasSheenColorTexture;
|
||||||
|
key.sheenColorUV = sheenColorUV;
|
||||||
|
key.hasSheenRoughnessTexture = hasSheenRoughnessTexture;
|
||||||
|
key.sheenRoughnessUV = sheenRoughnessUV;
|
||||||
|
key.hasVolumeThicknessTexture = hasVolumeThicknessTexture;
|
||||||
|
key.volumeThicknessUV = volumeThicknessUV;
|
||||||
|
key.hasSheen = hasSheen;
|
||||||
|
key.hasIOR = hasIOR;
|
||||||
|
key.hasVolume = hasVolume;
|
||||||
|
|
||||||
|
final materialInstance = await withPointerCallback<TMaterialInstance>((cb) {
|
||||||
|
MaterialProvider_createMaterialInstanceRenderThread(
|
||||||
|
ubershaderMaterialProvider, key.address, cb);
|
||||||
|
});
|
||||||
|
if (materialInstance == nullptr) {
|
||||||
|
throw Exception("Failed to create material instance");
|
||||||
|
}
|
||||||
|
|
||||||
|
var instance = FFIMaterialInstance(materialInstance, this);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future<FFIMaterialInstance> createUnlitMaterialInstance() async {
|
||||||
|
final instance = await createUbershaderMaterialInstance(unlit: true);
|
||||||
|
return instance as FFIMaterialInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
FFIMaterial? _gridMaterial;
|
||||||
|
Future<FFIMaterial> get gridMaterial async {
|
||||||
|
_gridMaterial ??= FFIMaterial(Material_createGridMaterial(), this);
|
||||||
|
return _gridMaterial!;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
Future<ThermionAsset> createGeometry(Geometry geometry,
|
||||||
|
{List<MaterialInstance>? materialInstances,
|
||||||
|
bool keepData = false}) async {
|
||||||
|
var assetPtr = await withPointerCallback<TSceneAsset>((callback) {
|
||||||
|
var ptrList = Int64List(materialInstances?.length ?? 0);
|
||||||
|
if (materialInstances != null && materialInstances.isNotEmpty) {
|
||||||
|
ptrList.setRange(
|
||||||
|
0,
|
||||||
|
materialInstances.length,
|
||||||
|
materialInstances
|
||||||
|
.cast<FFIMaterialInstance>()
|
||||||
|
.map((mi) => mi.pointer.address)
|
||||||
|
.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
return SceneAsset_createGeometryRenderThread(
|
||||||
|
engine,
|
||||||
|
geometry.vertices.address,
|
||||||
|
geometry.vertices.length,
|
||||||
|
geometry.normals.address,
|
||||||
|
geometry.normals.length,
|
||||||
|
geometry.uvs.address,
|
||||||
|
geometry.uvs.length,
|
||||||
|
geometry.indices.address,
|
||||||
|
geometry.indices.length,
|
||||||
|
geometry.primitiveType.index,
|
||||||
|
ptrList.address.cast<Pointer<TMaterialInstance>>(),
|
||||||
|
ptrList.length,
|
||||||
|
callback);
|
||||||
|
});
|
||||||
|
if (assetPtr == nullptr) {
|
||||||
|
throw Exception("Failed to create geometry");
|
||||||
|
}
|
||||||
|
|
||||||
|
var asset = FFIAsset(assetPtr, this);
|
||||||
|
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Future<MaterialInstance> getMaterialInstanceAt(
|
||||||
|
ThermionEntity entity, int index) async {
|
||||||
|
final instancePtr = RenderableManager_getMaterialInstanceAt(
|
||||||
|
renderableManager, entity, index);
|
||||||
|
|
||||||
|
final instance = FFIMaterialInstance(instancePtr, this);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
Future render() async {
|
||||||
|
RenderTicker_renderRenderThread(renderTicker, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future register(
|
||||||
|
covariant FFISwapChain swapChain, covariant FFIView view) async {
|
||||||
|
_viewMappings[view] = swapChain;
|
||||||
|
}
|
||||||
|
|
||||||
|
final _hooks = <Future Function()>[];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future registerRequestFrameHook(Future Function() hook) async {
|
||||||
|
if (!_hooks.contains(hook)) {
|
||||||
|
_hooks.add(hook);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future unregisterRequestFrameHook(Future Function() hook) async {
|
||||||
|
if (_hooks.contains(hook)) {
|
||||||
|
_hooks.remove(hook);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
@override
|
||||||
|
Future requestFrame() async {
|
||||||
|
for (final hook in _hooks) {
|
||||||
|
await hook.call();
|
||||||
|
}
|
||||||
|
final completer = Completer();
|
||||||
|
|
||||||
|
final callback = NativeCallable<Void Function()>.listener(() {
|
||||||
|
completer.complete(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
RenderLoop_requestAnimationFrame(callback.nativeFunction.cast());
|
||||||
|
|
||||||
|
try {
|
||||||
|
await completer.future.timeout(Duration(seconds: 1));
|
||||||
|
} catch (err) {
|
||||||
|
print("WARNING - render call timed out");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
|
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
|
||||||
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart';
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart';
|
||||||
import 'package:thermion_dart/src/viewer/src/shared_types/entities.dart';
|
|
||||||
import 'thermion_dart.g.dart';
|
|
||||||
import 'package:thermion_dart/thermion_dart.dart';
|
import 'package:thermion_dart/thermion_dart.dart';
|
||||||
import 'package:vector_math/vector_math_64.dart';
|
|
||||||
|
|
||||||
import 'ffi_view.dart';
|
import 'ffi_view.dart';
|
||||||
|
|
||||||
class FFIGizmo extends FFIAsset implements GizmoAsset {
|
class FFIGizmo extends FFIAsset implements GizmoAsset {
|
||||||
|
|
||||||
final Set<ThermionEntity> gizmoEntities;
|
final Set<ThermionEntity> gizmoEntities;
|
||||||
late NativeCallable<GizmoPickCallbackFunction> _nativeCallback;
|
late NativeCallable<GizmoPickCallbackFunction> _nativeCallback;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart';
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart';
|
||||||
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
|
||||||
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_render_target.dart';
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_render_target.dart';
|
||||||
import 'package:thermion_dart/src/viewer/src/shared_types/scene.dart';
|
import 'package:thermion_dart/src/filament/src/scene.dart';
|
||||||
import 'callbacks.dart';
|
import 'callbacks.dart';
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
|
|||||||
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_render_target.dart';
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_render_target.dart';
|
||||||
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_scene.dart';
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_scene.dart';
|
||||||
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_swapchain.dart';
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_swapchain.dart';
|
||||||
import 'package:thermion_dart/src/viewer/src/shared_types/layers.dart';
|
import 'package:thermion_dart/src/filament/src/layers.dart';
|
||||||
import 'package:thermion_dart/src/viewer/src/shared_types/shared_types.dart';
|
import 'package:thermion_dart/src/filament/src/shared_types.dart';
|
||||||
import 'callbacks.dart';
|
import 'callbacks.dart';
|
||||||
import 'ffi_camera.dart';
|
import 'ffi_camera.dart';
|
||||||
|
|
||||||
@@ -66,10 +66,6 @@ class FFIView extends View {
|
|||||||
View_setPostProcessing(view, enabled);
|
View_setPostProcessing(view, enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future setRenderable(bool renderable, FFISwapChain swapChain) async {
|
|
||||||
throw UnimplementedError();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future setFrustumCullingEnabled(bool enabled) async {
|
Future setFrustumCullingEnabled(bool enabled) async {
|
||||||
View_setFrustumCullingEnabled(view, enabled);
|
View_setFrustumCullingEnabled(view, enabled);
|
||||||
@@ -84,7 +80,8 @@ class FFIView extends View {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future setToneMapper(ToneMapper mapper) async {
|
Future setToneMapper(ToneMapper mapper) async {
|
||||||
View_setToneMappingRenderThread(view, app.engine, TToneMapping.values[mapper.index].value);
|
View_setToneMappingRenderThread(
|
||||||
|
view, app.engine, TToneMapping.values[mapper.index].value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future setStencilBufferEnabled(bool enabled) async {
|
Future setStencilBufferEnabled(bool enabled) async {
|
||||||
@@ -111,7 +108,7 @@ class FFIView extends View {
|
|||||||
Future setScene(covariant FFIScene scene) async {
|
Future setScene(covariant FFIScene scene) async {
|
||||||
await withVoidCallback((cb) => View_setScene(view, scene.scene));
|
await withVoidCallback((cb) => View_setScene(view, scene.scene));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future setLayerVisibility(VisibilityLayers layer, bool visible) async {
|
Future setLayerVisibility(VisibilityLayers layer, bool visible) async {
|
||||||
View_setLayerEnabled(view, layer.value, visible);
|
View_setLayerEnabled(view, layer.value, visible);
|
||||||
|
|||||||
13
thermion_dart/lib/src/viewer/src/ffi/src/grid_overlay.dart
Normal file
13
thermion_dart/lib/src/viewer/src/ffi/src/grid_overlay.dart
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
|
||||||
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart';
|
||||||
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
|
||||||
|
|
||||||
|
class GridOverlay extends FFIAsset {
|
||||||
|
GridOverlay(super.asset, super.app);
|
||||||
|
|
||||||
|
static Future<GridOverlay> create(FFIFilamentApp app) async {
|
||||||
|
final gridMaterial = await app.gridMaterial;
|
||||||
|
final asset = SceneAsset_createGrid(app.engine, gridMaterial.pointer);
|
||||||
|
return GridOverlay(asset, app);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,6 +25,12 @@ external ffi.Pointer<TMaterialInstance> Material_createInstance(
|
|||||||
ffi.Pointer<TMaterial> tMaterial,
|
ffi.Pointer<TMaterial> tMaterial,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ffi.Native<ffi.Pointer<TMaterial> Function()>(isLeaf: true)
|
||||||
|
external ffi.Pointer<TMaterial> Material_createImageMaterial();
|
||||||
|
|
||||||
|
@ffi.Native<ffi.Pointer<TMaterial> Function()>(isLeaf: true)
|
||||||
|
external ffi.Pointer<TMaterial> Material_createGridMaterial();
|
||||||
|
|
||||||
@ffi.Native<ffi.Bool Function(ffi.Pointer<TMaterial>, ffi.Pointer<ffi.Char>)>(
|
@ffi.Native<ffi.Bool Function(ffi.Pointer<TMaterial>, ffi.Pointer<ffi.Char>)>(
|
||||||
isLeaf: true)
|
isLeaf: true)
|
||||||
external bool Material_hasParameter(
|
external bool Material_hasParameter(
|
||||||
@@ -1227,6 +1233,11 @@ external double Camera_getFov(
|
|||||||
bool horizontal,
|
bool horizontal,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ffi.Native<ffi.Double Function(ffi.Pointer<TCamera>)>(isLeaf: true)
|
||||||
|
external double Camera_getFocusDistance(
|
||||||
|
ffi.Pointer<TCamera> camera,
|
||||||
|
);
|
||||||
|
|
||||||
@ffi.Native<ffi.Void Function(ffi.Pointer<TCamera>, ffi.Float)>(isLeaf: true)
|
@ffi.Native<ffi.Void Function(ffi.Pointer<TCamera>, ffi.Float)>(isLeaf: true)
|
||||||
external void Camera_setFocusDistance(
|
external void Camera_setFocusDistance(
|
||||||
ffi.Pointer<TCamera> camera,
|
ffi.Pointer<TCamera> camera,
|
||||||
@@ -1324,11 +1335,12 @@ external void TransformManager_setTransform(
|
|||||||
double4x4 transform,
|
double4x4 transform,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ffi.Native<ffi.Void Function(ffi.Pointer<TTransformManager>, EntityId)>(
|
@ffi.Native<ffi.Void Function(ffi.Pointer<TTransformManager>, EntityId, Aabb3)>(
|
||||||
isLeaf: true)
|
isLeaf: true)
|
||||||
external void TransformManager_transformToUnitCube(
|
external void TransformManager_transformToUnitCube(
|
||||||
ffi.Pointer<TTransformManager> tTransformManager,
|
ffi.Pointer<TTransformManager> tTransformManager,
|
||||||
int entityId,
|
int entityId,
|
||||||
|
Aabb3 boundingBox,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ffi.Native<
|
@ffi.Native<
|
||||||
@@ -1458,7 +1470,7 @@ external void RenderTicker_render(
|
|||||||
ffi.Void Function(ffi.Pointer<TRenderTicker>, ffi.Pointer<TSwapChain>,
|
ffi.Void Function(ffi.Pointer<TRenderTicker>, ffi.Pointer<TSwapChain>,
|
||||||
ffi.Pointer<ffi.Pointer<TView>>, ffi.Uint8)>(isLeaf: true)
|
ffi.Pointer<ffi.Pointer<TView>>, ffi.Uint8)>(isLeaf: true)
|
||||||
external void RenderTicker_setRenderable(
|
external void RenderTicker_setRenderable(
|
||||||
ffi.Pointer<TRenderTicker> tFilamentRender,
|
ffi.Pointer<TRenderTicker> tRenderTicker,
|
||||||
ffi.Pointer<TSwapChain> swapChain,
|
ffi.Pointer<TSwapChain> swapChain,
|
||||||
ffi.Pointer<ffi.Pointer<TView>> views,
|
ffi.Pointer<ffi.Pointer<TView>> views,
|
||||||
int numViews,
|
int numViews,
|
||||||
@@ -2719,15 +2731,15 @@ external void TextureSampler_destroyRenderThread(
|
|||||||
|
|
||||||
@ffi.Native<
|
@ffi.Native<
|
||||||
ffi.Void Function(
|
ffi.Void Function(
|
||||||
ffi.Pointer<TSceneManager>,
|
ffi.Pointer<TAnimationManager>,
|
||||||
EntityId,
|
EntityId,
|
||||||
ffi.Int,
|
ffi.Int,
|
||||||
ffi.Int,
|
ffi.Int,
|
||||||
ffi.Pointer<ffi.Float>,
|
ffi.Pointer<ffi.Float>,
|
||||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Bool)>>)>(
|
ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Bool)>>)>(
|
||||||
isLeaf: true)
|
isLeaf: true)
|
||||||
external void set_bone_transform_render_thread(
|
external void AnimationManager_setBoneTransformRenderThread(
|
||||||
ffi.Pointer<TSceneManager> sceneManager,
|
ffi.Pointer<TAnimationManager> tAnimationManager,
|
||||||
int asset,
|
int asset,
|
||||||
int skinIndex,
|
int skinIndex,
|
||||||
int boneIndex,
|
int boneIndex,
|
||||||
@@ -2736,10 +2748,10 @@ external void set_bone_transform_render_thread(
|
|||||||
);
|
);
|
||||||
|
|
||||||
@ffi.Native<
|
@ffi.Native<
|
||||||
ffi.Void Function(ffi.Pointer<TSceneManager>, EntityId,
|
ffi.Void Function(ffi.Pointer<TAnimationManager>, EntityId,
|
||||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
|
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
|
||||||
external void reset_to_rest_pose_render_thread(
|
external void AnimationManager_resetToRestPoseRenderThread(
|
||||||
ffi.Pointer<TSceneManager> sceneManager,
|
ffi.Pointer<TAnimationManager> tAnimationManager,
|
||||||
int entityId,
|
int entityId,
|
||||||
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> callback,
|
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> callback,
|
||||||
);
|
);
|
||||||
@@ -3181,6 +3193,14 @@ external ffi.Pointer<TSceneAsset> SceneAsset_loadGltf(
|
|||||||
int numInstances,
|
int numInstances,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ffi.Native<
|
||||||
|
ffi.Pointer<TSceneAsset> Function(
|
||||||
|
ffi.Pointer<TEngine>, ffi.Pointer<TMaterial>)>(isLeaf: true)
|
||||||
|
external ffi.Pointer<TSceneAsset> SceneAsset_createGrid(
|
||||||
|
ffi.Pointer<TEngine> tEngine,
|
||||||
|
ffi.Pointer<TMaterial> tMaterial,
|
||||||
|
);
|
||||||
|
|
||||||
@ffi.Native<ffi.Void Function(ffi.Pointer<TSceneAsset>)>(isLeaf: true)
|
@ffi.Native<ffi.Void Function(ffi.Pointer<TSceneAsset>)>(isLeaf: true)
|
||||||
external void SceneAsset_destroy(
|
external void SceneAsset_destroy(
|
||||||
ffi.Pointer<TSceneAsset> tSceneAsset,
|
ffi.Pointer<TSceneAsset> tSceneAsset,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,4 @@
|
|||||||
library;
|
library;
|
||||||
|
|
||||||
export 'src/thermion_viewer_ffi.dart' show ThermionViewerFFI;
|
export 'src/thermion_viewer_ffi.dart' show ThermionViewerFFI;
|
||||||
|
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
import 'package:thermion_dart/src/viewer/src/shared_types/engine.dart';
|
|
||||||
import 'package:thermion_dart/thermion_dart.dart';
|
|
||||||
|
|
||||||
class FilamentConfig<T, U> {
|
|
||||||
final Backend backend;
|
|
||||||
final T? renderCallback;
|
|
||||||
final U? renderCallbackOwner;
|
|
||||||
final U resourceLoader;
|
|
||||||
final U? platform;
|
|
||||||
final U? driver;
|
|
||||||
final U? sharedContext;
|
|
||||||
final String uberArchivePath;
|
|
||||||
final int stereoscopicEyeCount;
|
|
||||||
final bool disableHandleUseAfterFreeCheck;
|
|
||||||
|
|
||||||
FilamentConfig(
|
|
||||||
{required this.backend,
|
|
||||||
required this.resourceLoader,
|
|
||||||
required this.uberArchivePath,
|
|
||||||
this.renderCallback,
|
|
||||||
this.renderCallbackOwner,
|
|
||||||
this.platform,
|
|
||||||
this.driver,
|
|
||||||
this.sharedContext,
|
|
||||||
this.stereoscopicEyeCount = 1,
|
|
||||||
this.disableHandleUseAfterFreeCheck = false});
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class FilamentApp<T> {
|
|
||||||
final T engine;
|
|
||||||
final T gltfAssetLoader;
|
|
||||||
final T gltfResourceLoader;
|
|
||||||
final T renderer;
|
|
||||||
final T transformManager;
|
|
||||||
final T lightManager;
|
|
||||||
final T renderableManager;
|
|
||||||
final T ubershaderMaterialProvider;
|
|
||||||
|
|
||||||
FilamentApp(
|
|
||||||
{required this.engine,
|
|
||||||
required this.gltfAssetLoader,
|
|
||||||
required this.gltfResourceLoader,
|
|
||||||
required this.renderer,
|
|
||||||
required this.transformManager,
|
|
||||||
required this.lightManager,
|
|
||||||
required this.renderableManager,
|
|
||||||
required this.ubershaderMaterialProvider
|
|
||||||
});
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future<SwapChain> createHeadlessSwapChain(int width, int height,
|
|
||||||
{bool hasStencilBuffer = false});
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future<SwapChain> createSwapChain(T handle, {bool hasStencilBuffer = false});
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future destroySwapChain(SwapChain swapChain);
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future destroy();
|
|
||||||
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future<RenderTarget> createRenderTarget(
|
|
||||||
int width, int height, { covariant Texture? color, covariant Texture? depth });
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
library;
|
|
||||||
|
|
||||||
import 'package:thermion_dart/thermion_dart.dart';
|
|
||||||
import 'package:vector_math/vector_math_64.dart';
|
|
||||||
|
|
||||||
export 'geometry.dart';
|
|
||||||
export 'gltf.dart';
|
|
||||||
|
|
||||||
export 'light_options.dart';
|
|
||||||
|
|
||||||
// handle manipulate an Entity
|
|
||||||
typedef ThermionEntity = int;
|
|
||||||
|
|
||||||
abstract class ThermionAsset {
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
ThermionEntity get entity;
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future<List<ThermionEntity>> getChildEntities();
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future setMaterialInstanceAt(covariant MaterialInstance instance);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Renders an outline around [entity] with the given color.
|
|
||||||
///
|
|
||||||
Future setStencilHighlight(
|
|
||||||
{double r = 1.0, double g = 0.0, double b = 0.0, int? entityIndex});
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Removes the outline around [entity]. Noop if there was no highlight.
|
|
||||||
///
|
|
||||||
Future removeStencilHighlight();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// When visible is [true], renders the bounding box.
|
|
||||||
///
|
|
||||||
Future setBoundingBoxVisibility(bool visible);
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future<ThermionAsset> getInstance(int index);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Create a new instance of [entity].
|
|
||||||
/// Instances are not automatically added to the scene; you must
|
|
||||||
/// call [addToScene].
|
|
||||||
///
|
|
||||||
Future<ThermionAsset> createInstance(
|
|
||||||
{covariant List<MaterialInstance>? materialInstances = null});
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns the number of instances associated with this asset.
|
|
||||||
///
|
|
||||||
Future<int> getInstanceCount();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns all instances of associated with this asset.
|
|
||||||
///
|
|
||||||
Future<List<ThermionAsset>> getInstances();
|
|
||||||
|
|
||||||
Future setCastShadows(bool castShadows);
|
|
||||||
Future setReceiveShadows(bool castShadows);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Axis {
|
|
||||||
X(const [1.0, 0.0, 0.0]),
|
|
||||||
Y(const [0.0, 1.0, 0.0]),
|
|
||||||
Z(const [0.0, 0.0, 1.0]);
|
|
||||||
|
|
||||||
const Axis(this.vector);
|
|
||||||
|
|
||||||
final List<double> vector;
|
|
||||||
|
|
||||||
Vector3 asVector() => Vector3(vector[0], vector[1], vector[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum GizmoPickResultType { AxisX, AxisY, AxisZ, Parent, None }
|
|
||||||
|
|
||||||
enum GizmoType { translation, rotation }
|
|
||||||
|
|
||||||
abstract class GizmoAsset extends ThermionAsset {
|
|
||||||
Future pick(int x, int y,
|
|
||||||
{Future Function(GizmoPickResultType axis, Vector3 coords)? handler});
|
|
||||||
Future highlight(Axis axis);
|
|
||||||
Future unhighlight();
|
|
||||||
bool isNonPickable(ThermionEntity entity);
|
|
||||||
bool isGizmoEntity(ThermionEntity entity);
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import 'package:thermion_dart/thermion_dart.dart';
|
|
||||||
|
|
||||||
final ThermionEntity FILAMENT_ENTITY_NULL = 0;
|
|
||||||
@@ -1,23 +1,28 @@
|
|||||||
import 'package:thermion_dart/src/viewer/src/shared_types/layers.dart';
|
import 'package:thermion_dart/src/filament/src/filament_app.dart';
|
||||||
|
import '../../filament/src/shared_types.dart';
|
||||||
import '../../utils/src/gizmo.dart';
|
|
||||||
import 'shared_types/shared_types.dart';
|
|
||||||
export 'shared_types/shared_types.dart';
|
|
||||||
|
|
||||||
import 'dart:math';
|
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:vector_math/vector_math_64.dart';
|
import 'package:vector_math/vector_math_64.dart';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:animation_tools_dart/animation_tools_dart.dart';
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// A high-level interface for interacting with a 3D scene.
|
/// A (high-level) interface for a 3D scene.
|
||||||
/// This broadly maps to a single scene/view
|
///
|
||||||
|
/// Use this to add/remove assets, lights and cameras.
|
||||||
|
///
|
||||||
|
/// Multiple instances can be created; each will correspond
|
||||||
|
/// broadly to a single Filament Scene/View.
|
||||||
|
///
|
||||||
|
/// If you know yhat you are doing, you can use a lower level interface by
|
||||||
|
/// using the methods directly via FilamentApp.instance;
|
||||||
///
|
///
|
||||||
abstract class ThermionViewer {
|
abstract class ThermionViewer {
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Whether the controller is currently rendering at [framerate].
|
///
|
||||||
|
///
|
||||||
|
View get view;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// If [true], this Viewer should render itself
|
||||||
///
|
///
|
||||||
bool get rendering;
|
bool get rendering;
|
||||||
|
|
||||||
@@ -31,29 +36,6 @@ abstract class ThermionViewer {
|
|||||||
///
|
///
|
||||||
Future render();
|
Future render();
|
||||||
|
|
||||||
///
|
|
||||||
/// Requests a single frame to be rendered. This is only intended to be used internally.
|
|
||||||
///
|
|
||||||
Future requestFrame();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Render a single frame and return the captured image as a pixel buffer.
|
|
||||||
///
|
|
||||||
Future<List<Uint8List>> capture(
|
|
||||||
covariant List<
|
|
||||||
({View view, SwapChain? swapChain, RenderTarget? renderTarget})>
|
|
||||||
targets);
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future<View> createView();
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future<View> getViewAt(int index);
|
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
@@ -119,32 +101,6 @@ abstract class ThermionViewer {
|
|||||||
///
|
///
|
||||||
Future removeIbl();
|
Future removeIbl();
|
||||||
|
|
||||||
///
|
|
||||||
/// Add a light to the scene.
|
|
||||||
/// See LightManager.h for details
|
|
||||||
/// Note that [sunAngularRadius] is in degrees,
|
|
||||||
/// whereas [spotLightConeInner] and [spotLightConeOuter] are in radians
|
|
||||||
///
|
|
||||||
@Deprecated(
|
|
||||||
"This will be removed in future versions. Use addDirectLight instead.")
|
|
||||||
Future<ThermionEntity> addLight(
|
|
||||||
LightType type,
|
|
||||||
double colour,
|
|
||||||
double intensity,
|
|
||||||
double posX,
|
|
||||||
double posY,
|
|
||||||
double posZ,
|
|
||||||
double dirX,
|
|
||||||
double dirY,
|
|
||||||
double dirZ,
|
|
||||||
{double falloffRadius = 1.0,
|
|
||||||
double spotLightConeInner = pi / 8,
|
|
||||||
double spotLightConeOuter = pi / 4,
|
|
||||||
double sunAngularRadius = 0.545,
|
|
||||||
double sunHaloSize = 10.0,
|
|
||||||
double sunHaloFallof = 80.0,
|
|
||||||
bool castShadows = true});
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Adds a direct light to the scene.
|
/// Adds a direct light to the scene.
|
||||||
/// See LightManager.h for details
|
/// See LightManager.h for details
|
||||||
@@ -198,139 +154,6 @@ abstract class ThermionViewer {
|
|||||||
Future<ThermionAsset> loadGltf(String path, String relativeResourcePath,
|
Future<ThermionAsset> loadGltf(String path, String relativeResourcePath,
|
||||||
{bool keepData = false});
|
{bool keepData = false});
|
||||||
|
|
||||||
///
|
|
||||||
/// Set the weights for all morph targets in [entity] to [weights].
|
|
||||||
/// Note that [weights] must contain values for ALL morph targets, but no exception will be thrown if you don't do so (you'll just get incorrect results).
|
|
||||||
/// If you only want to set one value, set all others to zero (check [getMorphTargetNames] if you need the get a list of all morph targets).
|
|
||||||
/// IMPORTANT - this accepts the actual ThermionEntity with the relevant morph targets (unlike [getMorphTargetNames], which uses the parent entity and the child mesh name).
|
|
||||||
/// Use [getChildEntityByName] if you are setting the weights for a child mesh.
|
|
||||||
///
|
|
||||||
Future setMorphTargetWeights(ThermionEntity entity, List<double> weights);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Gets the names of all morph targets for the child renderable [childEntity] under [entity].
|
|
||||||
///
|
|
||||||
Future<List<String>> getMorphTargetNames(
|
|
||||||
covariant ThermionAsset asset, ThermionEntity childEntity);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Gets the names of all bones for the armature at [skinIndex] under the specified [entity].
|
|
||||||
///
|
|
||||||
Future<List<String>> getBoneNames(covariant ThermionAsset asset,
|
|
||||||
{int skinIndex = 0});
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Gets the names of all glTF animations embedded in the specified entity.
|
|
||||||
///
|
|
||||||
Future<List<String>> getAnimationNames(covariant ThermionAsset asset);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns the length (in seconds) of the animation at the given index.
|
|
||||||
///
|
|
||||||
Future<double> getAnimationDuration(
|
|
||||||
covariant ThermionAsset asset, int animationIndex);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Construct animation(s) for every entity under [asset]. If [targetMeshNames] is provided, only entities with matching names will be animated.
|
|
||||||
/// [MorphTargetAnimation] for an explanation as to how to construct the animation frame data.
|
|
||||||
/// This method will check the morph target names specified in [animation] against the morph target names that actually exist exist under [meshName] in [entity],
|
|
||||||
/// throwing an exception if any cannot be found.
|
|
||||||
/// It is permissible for [animation] to omit any targets that do exist under [meshName]; these simply won't be animated.
|
|
||||||
///
|
|
||||||
Future setMorphAnimationData(
|
|
||||||
covariant ThermionAsset asset, MorphAnimationData animation,
|
|
||||||
{List<String>? targetMeshNames});
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Clear all current morph animations for [entity].
|
|
||||||
///
|
|
||||||
Future clearMorphAnimationData(ThermionEntity entity);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Resets all bones in the given entity to their rest pose.
|
|
||||||
/// This should be done before every call to addBoneAnimation.
|
|
||||||
///
|
|
||||||
Future resetBones(ThermionAsset asset);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Enqueues and plays the [animation] for the specified bone(s).
|
|
||||||
/// By default, frame data is interpreted as being in *parent* bone space;
|
|
||||||
/// a 45 degree around Y means the bone will rotate 45 degrees around the
|
|
||||||
/// Y axis of the parent bone *in its current orientation*.
|
|
||||||
/// (i.e NOT the parent bone's rest position!).
|
|
||||||
/// Currently, only [Space.ParentBone] and [Space.Model] are supported; if you want
|
|
||||||
/// to transform to another space, you will need to do so manually.
|
|
||||||
///
|
|
||||||
/// [fadeInInSecs]/[fadeOutInSecs]/[maxDelta] are used to cross-fade between
|
|
||||||
/// the current active glTF animation ("animation1") and the animation you
|
|
||||||
/// set via this method ("animation2"). The bone orientations will be
|
|
||||||
/// linearly interpolated between animation1 and animation2; at time 0,
|
|
||||||
/// the orientation will be 100% animation1, at time [fadeInInSecs], the
|
|
||||||
/// animation will be ((1 - maxDelta) * animation1) + (maxDelta * animation2).
|
|
||||||
/// This will be applied in reverse after [fadeOutInSecs].
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future addBoneAnimation(ThermionAsset asset, BoneAnimationData animation,
|
|
||||||
{int skinIndex = 0,
|
|
||||||
double fadeInInSecs = 0.0,
|
|
||||||
double fadeOutInSecs = 0.0,
|
|
||||||
double maxDelta = 1.0});
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Gets the entity representing the bone at [boneIndex]/[skinIndex].
|
|
||||||
/// The returned entity is only intended for use with [getWorldTransform].
|
|
||||||
///
|
|
||||||
Future<ThermionEntity> getBone(covariant ThermionAsset asset, int boneIndex,
|
|
||||||
{int skinIndex = 0});
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Gets the local (relative to parent) transform for [entity].
|
|
||||||
///
|
|
||||||
Future<Matrix4> getLocalTransform(ThermionEntity entity);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Gets the world transform for [entity].
|
|
||||||
///
|
|
||||||
Future<Matrix4> getWorldTransform(ThermionEntity entity);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Gets the inverse bind (pose) matrix for the bone.
|
|
||||||
/// Note that [parent] must be the ThermionEntity returned by [loadGlb/loadGltf], not any other method ([getChildEntity] etc).
|
|
||||||
/// This is because all joint information is internally stored with the parent entity.
|
|
||||||
///
|
|
||||||
Future<Matrix4> getInverseBindMatrix(
|
|
||||||
covariant ThermionAsset asset, int boneIndex,
|
|
||||||
{int skinIndex = 0});
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Sets the transform (relative to its parent) for [entity].
|
|
||||||
///
|
|
||||||
Future setTransform(ThermionEntity entity, Matrix4 transform);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Sets multiple transforms (relative to parent) simultaneously for [entity].
|
|
||||||
/// Uses mutex to ensure that transform updates aren't split across frames.
|
|
||||||
///
|
|
||||||
Future queueTransformUpdates(
|
|
||||||
List<ThermionEntity> entities, List<Matrix4> transforms);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Updates the bone matrices for [entity] (which must be the ThermionEntity
|
|
||||||
/// returned by [loadGlb/loadGltf]).
|
|
||||||
/// Under the hood, this just calls [updateBoneMatrices] on the Animator
|
|
||||||
/// instance of the relevant FilamentInstance (which uses the local
|
|
||||||
/// bone transform and the inverse bind matrix to set the bone matrix).
|
|
||||||
///
|
|
||||||
Future updateBoneMatrices(ThermionEntity entity);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Directly set the bone matrix for the bone at the given index.
|
|
||||||
/// Don't call this manually unless you know what you're doing.
|
|
||||||
///
|
|
||||||
Future setBoneTransform(
|
|
||||||
ThermionEntity entity, int boneIndex, Matrix4 transform,
|
|
||||||
{int skinIndex = 0});
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Destroys [asset] and all underlying resources
|
/// Destroys [asset] and all underlying resources
|
||||||
/// (including instances, but excluding any manually created material instances).
|
/// (including instances, but excluding any manually created material instances).
|
||||||
@@ -343,72 +166,6 @@ abstract class ThermionViewer {
|
|||||||
///
|
///
|
||||||
Future destroyAssets();
|
Future destroyAssets();
|
||||||
|
|
||||||
///
|
|
||||||
/// Schedules the glTF animation at [index] in [asset] to start playing on the next frame.
|
|
||||||
///
|
|
||||||
Future playAnimation(ThermionAsset asset, int index,
|
|
||||||
{bool loop = false,
|
|
||||||
bool reverse = false,
|
|
||||||
bool replaceActive = true,
|
|
||||||
double crossfade = 0.0,
|
|
||||||
double startOffset = 0.0});
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Schedules the glTF animation at [index] in [entity] to start playing on the next frame.
|
|
||||||
///
|
|
||||||
Future playAnimationByName(covariant ThermionAsset asset, String name,
|
|
||||||
{bool loop = false,
|
|
||||||
bool reverse = false,
|
|
||||||
bool replaceActive = true,
|
|
||||||
double crossfade = 0.0});
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future setGltfAnimationFrame(
|
|
||||||
covariant ThermionAsset asset, int index, int animationFrame);
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future stopAnimation(covariant ThermionAsset asset, int animationIndex);
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future stopAnimationByName(covariant ThermionAsset asset, String name);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Sets the current scene camera to the glTF camera under [name] in [entity].
|
|
||||||
///
|
|
||||||
Future setCamera(ThermionEntity entity, String? name);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Sets the current scene camera to the main camera (which is always available and added to every scene by default).
|
|
||||||
///
|
|
||||||
Future setMainCamera();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns the entity associated with the main camera. You probably never need this; use getMainCamera instead.
|
|
||||||
///
|
|
||||||
Future<ThermionEntity> getMainCameraEntity();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns the Camera instance for the main camera.
|
|
||||||
///
|
|
||||||
Future<Camera> getMainCamera();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Sets the horizontal field of view (if [horizontal] is true) or vertical field of view for the currently active camera to [degrees].
|
|
||||||
/// The aspect ratio of the current viewport is used.
|
|
||||||
///
|
|
||||||
Future setCameraFov(double degrees, {bool horizontal = true});
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Gets the field of view (in degrees).
|
|
||||||
///
|
|
||||||
Future<double> getCameraFov(bool horizontal);
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Sets the tone mapping (requires postprocessing).
|
/// Sets the tone mapping (requires postprocessing).
|
||||||
///
|
///
|
||||||
@@ -419,115 +176,11 @@ abstract class ThermionViewer {
|
|||||||
///
|
///
|
||||||
Future setBloom(bool enabled, double strength);
|
Future setBloom(bool enabled, double strength);
|
||||||
|
|
||||||
///
|
|
||||||
/// Sets the focal length of the camera. Default value is 28.0.
|
|
||||||
///
|
|
||||||
Future setCameraFocalLength(double focalLength);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Sets the distance (in world units) to the near/far planes for the active camera. Default values are 0.05/1000.0. See Camera.h for details.
|
|
||||||
///
|
|
||||||
Future setCameraCulling(double near, double far);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Get the distance (in world units) to the near plane for the active camera.
|
|
||||||
///
|
|
||||||
@Deprecated("Use getCameraNear")
|
|
||||||
Future<double> getCameraCullingNear();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Get the distance (in world units) to the near plane for the active camera.
|
|
||||||
///
|
|
||||||
Future<double> getCameraNear();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Get the distance (in world units) to the far culling plane for the active camera.
|
|
||||||
///
|
|
||||||
Future<double> getCameraCullingFar();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Sets the focus distance for the camera.
|
|
||||||
///
|
|
||||||
Future setCameraFocusDistance(double focusDistance);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Get the camera position in world space.
|
|
||||||
///
|
|
||||||
Future<Vector3> getCameraPosition();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Get the camera's model matrix.
|
|
||||||
///
|
|
||||||
Future<Matrix4> getCameraModelMatrix();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Get the camera's view matrix. See Camera.h for more details.
|
|
||||||
///
|
|
||||||
Future<Matrix4> getCameraViewMatrix();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Get the camera's projection matrix. See Camera.h for more details.
|
|
||||||
///
|
|
||||||
Future<Matrix4> getCameraProjectionMatrix();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Get the camera's culling projection matrix. See Camera.h for more details.
|
|
||||||
///
|
|
||||||
Future<Matrix4> getCameraCullingProjectionMatrix();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Get the camera's culling frustum in world space. Returns a (vector_math) [Frustum] instance where plane0-plane6 define the left, right, bottom, top, far and near planes respectively.
|
|
||||||
/// See Camera.h and (filament) Frustum.h for more details.
|
|
||||||
///
|
|
||||||
Future<Frustum> getCameraFrustum();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Set the camera position in world space. Note this is not persistent - any viewport navigation will reset the camera transform.
|
|
||||||
///
|
|
||||||
Future setCameraPosition(double x, double y, double z);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Get the camera rotation matrix.
|
|
||||||
///
|
|
||||||
Future<Matrix3> getCameraRotation();
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Repositions the camera to the last vertex of the bounding box of [entity], looking at the penultimate vertex.
|
|
||||||
///
|
|
||||||
Future moveCameraToAsset(ThermionEntity entity);
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Enables/disables frustum culling.
|
/// Enables/disables frustum culling.
|
||||||
///
|
///
|
||||||
Future setViewFrustumCulling(bool enabled);
|
Future setViewFrustumCulling(bool enabled);
|
||||||
|
|
||||||
///
|
|
||||||
/// Sets the camera exposure.
|
|
||||||
///
|
|
||||||
Future setCameraExposure(
|
|
||||||
double aperture, double shutterSpeed, double sensitivity);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Rotate the camera by [rads] around the given axis.
|
|
||||||
///
|
|
||||||
Future setCameraRotation(Quaternion quaternion);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Sets the camera model matrix.
|
|
||||||
///
|
|
||||||
@Deprecated("Will be superseded by setCameraModelMatrix4")
|
|
||||||
Future setCameraModelMatrix(List<double> matrix);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Sets the camera model matrix.
|
|
||||||
///
|
|
||||||
Future setCameraModelMatrix4(Matrix4 matrix);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Scale [entity] to fit within the unit cube.
|
|
||||||
///
|
|
||||||
Future transformToUnitCube(ThermionEntity entity);
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Set the world space position for [lightEntity] to the given coordinates.
|
/// Set the world space position for [lightEntity] to the given coordinates.
|
||||||
///
|
///
|
||||||
@@ -539,18 +192,6 @@ abstract class ThermionViewer {
|
|||||||
///
|
///
|
||||||
Future setLightDirection(ThermionEntity lightEntity, Vector3 direction);
|
Future setLightDirection(ThermionEntity lightEntity, Vector3 direction);
|
||||||
|
|
||||||
///
|
|
||||||
/// TODO
|
|
||||||
///
|
|
||||||
Future queuePositionUpdateFromViewportCoords(
|
|
||||||
ThermionEntity entity, double x, double y);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// TODO
|
|
||||||
///
|
|
||||||
Future queueRelativePositionUpdateWorldAxis(ThermionEntity entity,
|
|
||||||
double viewportX, double viewportY, double x, double y, double z);
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Enable/disable postprocessing effects (anti-aliasing, tone mapping, bloom). Disabled by default.
|
/// Enable/disable postprocessing effects (anti-aliasing, tone mapping, bloom). Disabled by default.
|
||||||
///
|
///
|
||||||
@@ -576,16 +217,6 @@ abstract class ThermionViewer {
|
|||||||
///
|
///
|
||||||
Future setAntiAliasing(bool msaa, bool fxaa, bool taa);
|
Future setAntiAliasing(bool msaa, bool fxaa, bool taa);
|
||||||
|
|
||||||
///
|
|
||||||
/// Adds a single [entity] to the scene.
|
|
||||||
///
|
|
||||||
Future addEntityToScene(ThermionEntity entity);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Removes a single [entity] from the scene.
|
|
||||||
///
|
|
||||||
Future removeAssetFromScene(ThermionEntity entity);
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Hit test the viewport at the given coordinates. If the coordinates intersect
|
/// Hit test the viewport at the given coordinates. If the coordinates intersect
|
||||||
/// with a renderable entity, [resultHandler] will be called.
|
/// with a renderable entity, [resultHandler] will be called.
|
||||||
@@ -599,37 +230,6 @@ abstract class ThermionViewer {
|
|||||||
///
|
///
|
||||||
String? getNameForEntity(ThermionEntity entity);
|
String? getNameForEntity(ThermionEntity entity);
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns all child entities under [asset].
|
|
||||||
///
|
|
||||||
Future<List<ThermionEntity>> getChildEntities(covariant ThermionAsset asset);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Finds the child entity named [childName] associated with the given parent.
|
|
||||||
/// Usually, [parent] will be the return value from [loadGlb]/[loadGltf] and [childName] will be the name of a node/mesh.
|
|
||||||
///
|
|
||||||
Future<ThermionEntity?> getChildEntity(
|
|
||||||
covariant ThermionAsset asset, String childName);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// An [entity] will only be animatable after an animation component is attached.
|
|
||||||
/// Any calls to [playAnimation]/[setBoneAnimation]/[setMorphAnimation] will have no visual effect until [addAnimationComponent] has been called on the instance.
|
|
||||||
///
|
|
||||||
Future addAnimationComponent(ThermionEntity entity);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Removes an animation component from [entity].
|
|
||||||
///
|
|
||||||
Future removeAnimationComponent(ThermionEntity entity);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Creates a (renderable) entity with the specified geometry and adds to the scene.
|
|
||||||
/// If [keepData] is true, the source data will not be released.
|
|
||||||
///
|
|
||||||
Future<ThermionAsset> createGeometry(Geometry geometry,
|
|
||||||
{covariant List<MaterialInstance>? materialInstances,
|
|
||||||
bool keepData = false});
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Gets the parent entity of [entity]. Returns null if the entity has no parent.
|
/// Gets the parent entity of [entity]. Returns null if the entity has no parent.
|
||||||
///
|
///
|
||||||
@@ -672,131 +272,15 @@ abstract class ThermionViewer {
|
|||||||
Future<Aabb2> getViewportBoundingBox(ThermionEntity entity);
|
Future<Aabb2> getViewportBoundingBox(ThermionEntity entity);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Toggles the visibility of the respective layer.
|
|
||||||
///
|
|
||||||
Future setLayerVisibility(VisibilityLayers layer, bool visible);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// All renderable entities are assigned a layer mask.
|
|
||||||
///
|
|
||||||
/// By calling [setLayerVisibility], all renderable entities allocated to
|
|
||||||
/// the given layer can be efficiently hidden/revealed.
|
|
||||||
///
|
|
||||||
/// By default, all renderable entities are assigned to layer 0 (and this
|
|
||||||
/// layer is enabled by default). Call [setVisibilityLayer] to change the
|
|
||||||
/// layer for the specified entity.
|
|
||||||
///
|
|
||||||
/// Note that we currently also assign gizmos to layer 1 (enabled by default)
|
|
||||||
/// and the world grid to layer 2 (disabled by default). We suggest you avoid
|
|
||||||
/// using these layers.
|
|
||||||
///
|
|
||||||
Future setVisibilityLayer(ThermionEntity entity, VisibilityLayers layer);
|
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
Future showGridOverlay();
|
||||||
Future showGridOverlay({covariant Material? material});
|
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
Future removeGridOverlay();
|
Future removeGridOverlay();
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future<Texture> createTexture(int width, int height,
|
|
||||||
{int depth = 1,
|
|
||||||
int levels = 1,
|
|
||||||
TextureSamplerType textureSamplerType = TextureSamplerType.SAMPLER_2D,
|
|
||||||
TextureFormat textureFormat = TextureFormat.RGBA32F});
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future<TextureSampler> createTextureSampler(
|
|
||||||
{TextureMinFilter minFilter = TextureMinFilter.LINEAR,
|
|
||||||
TextureMagFilter magFilter = TextureMagFilter.LINEAR,
|
|
||||||
TextureWrapMode wrapS = TextureWrapMode.CLAMP_TO_EDGE,
|
|
||||||
TextureWrapMode wrapT = TextureWrapMode.CLAMP_TO_EDGE,
|
|
||||||
TextureWrapMode wrapR = TextureWrapMode.CLAMP_TO_EDGE,
|
|
||||||
double anisotropy = 0.0,
|
|
||||||
TextureCompareMode compareMode = TextureCompareMode.NONE,
|
|
||||||
TextureCompareFunc compareFunc = TextureCompareFunc.LESS_EQUAL});
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Decodes the specified image data.
|
|
||||||
///
|
|
||||||
Future<LinearImage> decodeImage(Uint8List data);
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Creates an (empty) imge with the given dimensions.
|
|
||||||
///
|
|
||||||
Future<LinearImage> createImage(int width, int height, int channels);
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future<Material> createMaterial(Uint8List data);
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future<MaterialInstance> createUbershaderMaterialInstance({
|
|
||||||
bool doubleSided = false,
|
|
||||||
bool unlit = false,
|
|
||||||
bool hasVertexColors = false,
|
|
||||||
bool hasBaseColorTexture = false,
|
|
||||||
bool hasNormalTexture = false,
|
|
||||||
bool hasOcclusionTexture = false,
|
|
||||||
bool hasEmissiveTexture = false,
|
|
||||||
bool useSpecularGlossiness = false,
|
|
||||||
AlphaMode alphaMode = AlphaMode.OPAQUE,
|
|
||||||
bool enableDiagnostics = false,
|
|
||||||
bool hasMetallicRoughnessTexture = false,
|
|
||||||
int metallicRoughnessUV = -1,
|
|
||||||
int baseColorUV = -1,
|
|
||||||
bool hasClearCoatTexture = false,
|
|
||||||
int clearCoatUV = -1,
|
|
||||||
bool hasClearCoatRoughnessTexture = false,
|
|
||||||
int clearCoatRoughnessUV = -1,
|
|
||||||
bool hasClearCoatNormalTexture = false,
|
|
||||||
int clearCoatNormalUV = -1,
|
|
||||||
bool hasClearCoat = false,
|
|
||||||
bool hasTransmission = false,
|
|
||||||
bool hasTextureTransforms = false,
|
|
||||||
int emissiveUV = -1,
|
|
||||||
int aoUV = -1,
|
|
||||||
int normalUV = -1,
|
|
||||||
bool hasTransmissionTexture = false,
|
|
||||||
int transmissionUV = -1,
|
|
||||||
bool hasSheenColorTexture = false,
|
|
||||||
int sheenColorUV = -1,
|
|
||||||
bool hasSheenRoughnessTexture = false,
|
|
||||||
int sheenRoughnessUV = -1,
|
|
||||||
bool hasVolumeThicknessTexture = false,
|
|
||||||
int volumeThicknessUV = -1,
|
|
||||||
bool hasSheen = false,
|
|
||||||
bool hasIOR = false,
|
|
||||||
bool hasVolume = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future destroyMaterialInstance(covariant MaterialInstance materialInstance);
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future<MaterialInstance> createUnlitMaterialInstance();
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future<MaterialInstance> getMaterialInstanceAt(
|
|
||||||
ThermionEntity entity, int index);
|
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
@@ -817,52 +301,8 @@ abstract class ThermionViewer {
|
|||||||
///
|
///
|
||||||
Future<Camera> getActiveCamera();
|
Future<Camera> getActiveCamera();
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future registerRequestFrameHook(Future Function() hook);
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future unregisterRequestFrameHook(Future Function() hook);
|
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
int getCameraCount();
|
int getCameraCount();
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns the camera specified by the given index. Note that the camera at
|
|
||||||
/// index 0 is always the main camera; this cannot be destroyed.
|
|
||||||
///
|
|
||||||
/// Throws an exception if the index is out-of-bounds.
|
|
||||||
///
|
|
||||||
Camera getCameraAt(int index);
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future setCastShadows(ThermionEntity entity, bool castShadows);
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future<bool> isCastShadowsEnabled(ThermionEntity entity);
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future setReceiveShadows(ThermionEntity entity, bool receiveShadows);
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future<bool> isReceiveShadowsEnabled(ThermionEntity entity);
|
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
Future setClearOptions(
|
|
||||||
Vector4 clearColor, int clearStencil, bool clear, bool discard);
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@ library thermion_flutter_js;
|
|||||||
|
|
||||||
import 'dart:js_interop';
|
import 'dart:js_interop';
|
||||||
|
|
||||||
import '../../shared_types/shared_types.dart';
|
import '../../../../filament/src/shared_types.dart';
|
||||||
|
|
||||||
///
|
///
|
||||||
/// An extension type on [JSObject] that represents a
|
/// An extension type on [JSObject] that represents a
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
library thermion_viewer;
|
library thermion_viewer;
|
||||||
|
|
||||||
export 'src/thermion_viewer_base.dart';
|
export 'src/thermion_viewer_base.dart';
|
||||||
|
export '../filament/src/filament_app.dart';
|
||||||
export 'src/thermion_viewer_stub.dart'
|
export 'src/thermion_viewer_stub.dart'
|
||||||
if (dart.library.io) 'src/ffi/thermion_viewer_ffi.dart'
|
if (dart.library.io) 'src/ffi/thermion_viewer_ffi.dart'
|
||||||
if (dart.library.js_interop) 'src/web_wasm/thermion_viewer_web_wasm.dart';
|
if (dart.library.js_interop) 'src/web_wasm/thermion_viewer_web_wasm.dart';
|
||||||
export 'src/shared_types/shared_types.dart';
|
export '../filament/src/shared_types.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
library filament_dart;
|
library filament_dart;
|
||||||
|
|
||||||
|
export 'package:vector_math/vector_math_64.dart' hide Colors;
|
||||||
export 'src/viewer/viewer.dart';
|
export 'src/viewer/viewer.dart';
|
||||||
export 'src/input/input.dart';
|
export 'src/input/input.dart';
|
||||||
export 'src/utils/utils.dart';
|
export 'src/utils/utils.dart';
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <utility> // for std::pair
|
||||||
|
|
||||||
#include <filament/Renderer.h>
|
#include <filament/Renderer.h>
|
||||||
#include <filament/SwapChain.h>
|
#include <filament/SwapChain.h>
|
||||||
@@ -49,10 +49,7 @@ namespace thermion
|
|||||||
std::mutex mMutex;
|
std::mutex mMutex;
|
||||||
filament::Renderer *mRenderer = nullptr;
|
filament::Renderer *mRenderer = nullptr;
|
||||||
std::vector<AnimationManager*> mAnimationManagers;
|
std::vector<AnimationManager*> mAnimationManagers;
|
||||||
std::vector<filament::SwapChain*> mSwapChains;
|
std::vector<std::pair<filament::SwapChain*, std::vector<filament::View*>>> mRenderable;
|
||||||
std::map<filament::SwapChain*, std::vector<filament::View*>> mRenderable;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -32,6 +32,7 @@ EMSCRIPTEN_KEEPALIVE void Camera_lookAt(TCamera* camera, double3 eye, double3 fo
|
|||||||
EMSCRIPTEN_KEEPALIVE double Camera_getNear(TCamera *camera);
|
EMSCRIPTEN_KEEPALIVE double Camera_getNear(TCamera *camera);
|
||||||
EMSCRIPTEN_KEEPALIVE double Camera_getCullingFar(TCamera *camera);
|
EMSCRIPTEN_KEEPALIVE double Camera_getCullingFar(TCamera *camera);
|
||||||
EMSCRIPTEN_KEEPALIVE float Camera_getFov(TCamera *camera, bool horizontal);
|
EMSCRIPTEN_KEEPALIVE float Camera_getFov(TCamera *camera, bool horizontal);
|
||||||
|
EMSCRIPTEN_KEEPALIVE double Camera_getFocusDistance(TCamera *camera);
|
||||||
EMSCRIPTEN_KEEPALIVE void Camera_setFocusDistance(TCamera *camera, float focusDistance);
|
EMSCRIPTEN_KEEPALIVE void Camera_setFocusDistance(TCamera *camera, float focusDistance);
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE void Camera_setCustomProjectionWithCulling(
|
EMSCRIPTEN_KEEPALIVE void Camera_setCustomProjectionWithCulling(
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ extern "C"
|
|||||||
};
|
};
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE TMaterialInstance *Material_createInstance(TMaterial *tMaterial);
|
EMSCRIPTEN_KEEPALIVE TMaterialInstance *Material_createInstance(TMaterial *tMaterial);
|
||||||
|
EMSCRIPTEN_KEEPALIVE TMaterial *Material_createImageMaterial();
|
||||||
|
EMSCRIPTEN_KEEPALIVE TMaterial *Material_createGridMaterial();
|
||||||
EMSCRIPTEN_KEEPALIVE bool Material_hasParameter(TMaterial *tMaterial, const char *propertyName);
|
EMSCRIPTEN_KEEPALIVE bool Material_hasParameter(TMaterial *tMaterial, const char *propertyName);
|
||||||
EMSCRIPTEN_KEEPALIVE bool MaterialInstance_isStencilWriteEnabled(TMaterialInstance *materialInstance);
|
EMSCRIPTEN_KEEPALIVE bool MaterialInstance_isStencilWriteEnabled(TMaterialInstance *materialInstance);
|
||||||
EMSCRIPTEN_KEEPALIVE void MaterialInstance_setStencilWrite(TMaterialInstance *materialInstance, bool enabled);
|
EMSCRIPTEN_KEEPALIVE void MaterialInstance_setStencilWrite(TMaterialInstance *materialInstance, bool enabled);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ extern "C"
|
|||||||
EMSCRIPTEN_KEEPALIVE void RenderTicker_removeAnimationManager(TRenderTicker *tRenderTicker, TAnimationManager *tAnimationManager);
|
EMSCRIPTEN_KEEPALIVE void RenderTicker_removeAnimationManager(TRenderTicker *tRenderTicker, TAnimationManager *tAnimationManager);
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE void RenderTicker_render(TRenderTicker *tRenderTicker, uint64_t frameTimeInNanos);
|
EMSCRIPTEN_KEEPALIVE void RenderTicker_render(TRenderTicker *tRenderTicker, uint64_t frameTimeInNanos);
|
||||||
EMSCRIPTEN_KEEPALIVE void RenderTicker_setRenderable(TRenderTicker *tFilamentRender, TSwapChain *swapChain, TView **views, uint8_t numViews);
|
EMSCRIPTEN_KEEPALIVE void RenderTicker_setRenderable(TRenderTicker *tRenderTicker, TSwapChain *swapChain, TView **views, uint8_t numViews);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ extern "C"
|
|||||||
size_t numInstances
|
size_t numInstances
|
||||||
);
|
);
|
||||||
|
|
||||||
|
EMSCRIPTEN_KEEPALIVE TSceneAsset *SceneAsset_createGrid(TEngine *tEngine, TMaterial * tMaterial);
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE void SceneAsset_destroy(TSceneAsset *tSceneAsset);
|
EMSCRIPTEN_KEEPALIVE void SceneAsset_destroy(TSceneAsset *tSceneAsset);
|
||||||
EMSCRIPTEN_KEEPALIVE void SceneAsset_addToScene(TSceneAsset *tSceneAsset, TScene *tScene);
|
EMSCRIPTEN_KEEPALIVE void SceneAsset_addToScene(TSceneAsset *tSceneAsset, TScene *tScene);
|
||||||
EMSCRIPTEN_KEEPALIVE void SceneAsset_removeFromScene(TSceneAsset *tSceneAsset, TScene *tScene);
|
EMSCRIPTEN_KEEPALIVE void SceneAsset_removeFromScene(TSceneAsset *tSceneAsset, TScene *tScene);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ extern "C"
|
|||||||
EMSCRIPTEN_KEEPALIVE double4x4 TransformManager_getLocalTransform(TTransformManager *tTransformManager, EntityId entityId);
|
EMSCRIPTEN_KEEPALIVE double4x4 TransformManager_getLocalTransform(TTransformManager *tTransformManager, EntityId entityId);
|
||||||
EMSCRIPTEN_KEEPALIVE double4x4 TransformManager_getWorldTransform(TTransformManager *tTransformManager, EntityId entityId);
|
EMSCRIPTEN_KEEPALIVE double4x4 TransformManager_getWorldTransform(TTransformManager *tTransformManager, EntityId entityId);
|
||||||
EMSCRIPTEN_KEEPALIVE void TransformManager_setTransform(TTransformManager *tTransformManager, EntityId entityId, double4x4 transform);
|
EMSCRIPTEN_KEEPALIVE void TransformManager_setTransform(TTransformManager *tTransformManager, EntityId entityId, double4x4 transform);
|
||||||
EMSCRIPTEN_KEEPALIVE void TransformManager_transformToUnitCube(TTransformManager *tTransformManager, EntityId entityId);
|
EMSCRIPTEN_KEEPALIVE void TransformManager_transformToUnitCube(TTransformManager *tTransformManager, EntityId entityId, Aabb3 boundingBox);
|
||||||
EMSCRIPTEN_KEEPALIVE void TransformManager_setParent(TTransformManager *tTransformManager, EntityId child, EntityId parent, bool preserveScaling);
|
EMSCRIPTEN_KEEPALIVE void TransformManager_setParent(TTransformManager *tTransformManager, EntityId child, EntityId parent, bool preserveScaling);
|
||||||
EMSCRIPTEN_KEEPALIVE EntityId TransformManager_getParent(TTransformManager *tTransformManager, EntityId child);
|
EMSCRIPTEN_KEEPALIVE EntityId TransformManager_getParent(TTransformManager *tTransformManager, EntityId child);
|
||||||
EMSCRIPTEN_KEEPALIVE EntityId TransformManager_getAncestor(TTransformManager *tTransformManager, EntityId childEntityId);
|
EMSCRIPTEN_KEEPALIVE EntityId TransformManager_getAncestor(TTransformManager *tTransformManager, EntityId childEntityId);
|
||||||
|
|||||||
@@ -294,15 +294,15 @@ namespace thermion
|
|||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE void AnimationManager_updateBoneMatricesRenderThread(TSceneManager *sceneManager,
|
EMSCRIPTEN_KEEPALIVE void AnimationManager_updateBoneMatricesRenderThread(TSceneManager *sceneManager,
|
||||||
EntityId asset, void (*callback)(bool));
|
EntityId asset, void (*callback)(bool));
|
||||||
EMSCRIPTEN_KEEPALIVE void set_bone_transform_render_thread(
|
EMSCRIPTEN_KEEPALIVE void AnimationManager_setBoneTransformRenderThread(
|
||||||
TSceneManager *sceneManager,
|
TAnimationManager *tAnimationManager,
|
||||||
EntityId asset,
|
EntityId asset,
|
||||||
int skinIndex,
|
int skinIndex,
|
||||||
int boneIndex,
|
int boneIndex,
|
||||||
const float *const transform,
|
const float *const transform,
|
||||||
void (*callback)(bool));
|
void (*callback)(bool));
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE void reset_to_rest_pose_render_thread(TSceneManager *sceneManager, EntityId entityId, void (*callback)());
|
EMSCRIPTEN_KEEPALIVE void AnimationManager_resetToRestPoseRenderThread(TAnimationManager *tAnimationManager, EntityId entityId, void (*callback)());
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE void GltfAssetLoader_createRenderThread(TEngine *tEngine, TMaterialProvider *tMaterialProvider, void (*callback)(TGltfAssetLoader *));
|
EMSCRIPTEN_KEEPALIVE void GltfAssetLoader_createRenderThread(TEngine *tEngine, TMaterialProvider *tMaterialProvider, void (*callback)(TGltfAssetLoader *));
|
||||||
EMSCRIPTEN_KEEPALIVE void GltfResourceLoader_createRenderThread(TEngine *tEngine, void (*callback)(TGltfResourceLoader *));
|
EMSCRIPTEN_KEEPALIVE void GltfResourceLoader_createRenderThread(TEngine *tEngine, void (*callback)(TGltfResourceLoader *));
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ namespace thermion
|
|||||||
Scene *scene);
|
Scene *scene);
|
||||||
~AnimationManager();
|
~AnimationManager();
|
||||||
|
|
||||||
void update();
|
void update(uint64_t frameTimeInNanos);
|
||||||
|
|
||||||
/// @brief
|
/// @brief
|
||||||
/// @param asset
|
/// @param asset
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
#if __APPLE__
|
#if __APPLE__
|
||||||
#include "TargetConditionals.h"
|
#include "TargetConditionals.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -48,57 +46,39 @@ namespace thermion
|
|||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
|
|
||||||
static constexpr filament::math::float4 sFullScreenTriangleVertices[3] = {
|
|
||||||
{-1.0f, -1.0f, 1.0f, 1.0f},
|
|
||||||
{3.0f, -1.0f, 1.0f, 1.0f},
|
|
||||||
{-1.0f, 3.0f, 1.0f, 1.0f}};
|
|
||||||
|
|
||||||
static const uint16_t sFullScreenTriangleIndices[3] = {0, 1, 2};
|
|
||||||
|
|
||||||
void RenderTicker::setRenderable(SwapChain *swapChain, View **views, uint8_t numViews) {
|
void RenderTicker::setRenderable(SwapChain *swapChain, View **views, uint8_t numViews) {
|
||||||
{
|
|
||||||
|
|
||||||
std::lock_guard lock(mMutex);
|
std::lock_guard lock(mMutex);
|
||||||
|
|
||||||
auto swapChainViews = mRenderable[swapChain];
|
// Find if this swapChain already exists in our collection
|
||||||
|
auto it = std::find_if(mRenderable.begin(), mRenderable.end(),
|
||||||
|
[swapChain](const auto& pair) { return pair.first == swapChain; });
|
||||||
|
|
||||||
swapChainViews.clear();
|
// Prepare the vector of views
|
||||||
|
std::vector<View*> swapChainViews;
|
||||||
for(int i = 0; i < numViews; i++) {
|
for(int i = 0; i < numViews; i++) {
|
||||||
swapChainViews.push_back(views[i]);
|
swapChainViews.push_back(views[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
mRenderable[swapChain] = swapChainViews;
|
if (it != mRenderable.end()) {
|
||||||
|
// Update existing entry
|
||||||
// Keep track of the swapchains, so we can iterate them in the render method.
|
it->second = swapChainViews;
|
||||||
bool found = false;
|
} else {
|
||||||
for (auto existingSwapChain : mSwapChains) {
|
// Add new entry
|
||||||
if (existingSwapChain == swapChain) {
|
mRenderable.emplace_back(swapChain, swapChainViews);
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
mSwapChains.push_back(swapChain);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void RenderTicker::render(uint64_t frameTimeInNanos)
|
void RenderTicker::render(uint64_t frameTimeInNanos)
|
||||||
{
|
{
|
||||||
std::lock_guard lock(mMutex);
|
std::lock_guard lock(mMutex);
|
||||||
|
|
||||||
// Update all animation managers
|
|
||||||
for (auto animationManager : mAnimationManagers) {
|
for (auto animationManager : mAnimationManagers) {
|
||||||
if (animationManager) { // Check for nullptr just in case
|
animationManager->update(frameTimeInNanos * 1e-9);
|
||||||
animationManager->update(frameTimeInNanos * 1e-9);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto& [swapChain, views] : mRenderable)
|
||||||
for (auto swapChain : mSwapChains)
|
|
||||||
{
|
{
|
||||||
auto views = mRenderable[swapChain];
|
if (!views.empty())
|
||||||
if (views.size() > 0)
|
|
||||||
{
|
{
|
||||||
bool beginFrame = mRenderer->beginFrame(swapChain, frameTimeInNanos);
|
bool beginFrame = mRenderer->beginFrame(swapChain, frameTimeInNanos);
|
||||||
if (beginFrame)
|
if (beginFrame)
|
||||||
@@ -116,19 +96,19 @@ namespace thermion
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderTicker::addAnimationManager(AnimationManager* animationManager) {
|
void RenderTicker::addAnimationManager(AnimationManager* animationManager) {
|
||||||
std::lock_guard<std::mutex> lock(mMutex);
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
mAnimationManagers.push_back(animationManager);
|
mAnimationManagers.push_back(animationManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderTicker::removeAnimationManager(AnimationManager* animationManager) {
|
void RenderTicker::removeAnimationManager(AnimationManager* animationManager) {
|
||||||
std::lock_guard<std::mutex> lock(mMutex);
|
std::lock_guard<std::mutex> lock(mMutex);
|
||||||
auto it = std::find(mAnimationManagers.begin(), mAnimationManagers.end(), animationManager);
|
auto it = std::find(mAnimationManagers.begin(), mAnimationManagers.end(), animationManager);
|
||||||
if (it != mAnimationManagers.end()) {
|
if (it != mAnimationManagers.end()) {
|
||||||
mAnimationManagers.erase(it);
|
mAnimationManagers.erase(it);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RenderTicker::~RenderTicker() {}
|
RenderTicker::~RenderTicker() {}
|
||||||
|
|
||||||
} // namespace thermion
|
} // namespace thermion
|
||||||
@@ -47,6 +47,16 @@ namespace thermion
|
|||||||
camera->setCustomProjection(convert_double4x4_to_mat4(projectionMatrix), near, far);
|
camera->setCustomProjection(convert_double4x4_to_mat4(projectionMatrix), near, far);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EMSCRIPTEN_KEEPALIVE double Camera_getFocusDistance(TCamera *camera) {
|
||||||
|
auto *camera = reinterpret_cast<Camera *>(tCamera);
|
||||||
|
return camera->getFocusDistance();
|
||||||
|
}
|
||||||
|
|
||||||
|
EMSCRIPTEN_KEEPALIVE void Camera_setFocusDistance(TCamera *camera, float distance) {
|
||||||
|
auto *camera = reinterpret_cast<Camera *>(tCamera);
|
||||||
|
return camera->setFocusDistance(distance);
|
||||||
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE double4x4 Camera_getModelMatrix(TCamera *tCamera)
|
EMSCRIPTEN_KEEPALIVE double4x4 Camera_getModelMatrix(TCamera *tCamera)
|
||||||
{
|
{
|
||||||
auto *camera = reinterpret_cast<Camera *>(tCamera);
|
auto *camera = reinterpret_cast<Camera *>(tCamera);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <math/vec2.h>
|
#include <math/vec2.h>
|
||||||
|
|
||||||
#include "Log.hpp"
|
#include "Log.hpp"
|
||||||
|
#include "materials/image.h"
|
||||||
#include "c_api/TMaterialInstance.h"
|
#include "c_api/TMaterialInstance.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@@ -23,6 +24,26 @@ namespace thermion
|
|||||||
return reinterpret_cast<TMaterialInstance *>(instance);
|
return reinterpret_cast<TMaterialInstance *>(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EMSCRIPTEN_KEEPALIVE TMaterial *Material_createImageMaterial(TEngine *tEngine) {
|
||||||
|
auto *engine = reinterpret_cast<filament::Engine *>(tEngine);
|
||||||
|
auto *material = Material::Builder()
|
||||||
|
.package(IMAGE_IMAGE_DATA, IMAGE_IMAGE_SIZE)
|
||||||
|
.build(*engine);
|
||||||
|
|
||||||
|
return reinterpret_cast<TMaterial *>(material);
|
||||||
|
}
|
||||||
|
|
||||||
|
EMSCRIPTEN_KEEPALIVE TMaterial *Material_createGridMaterial(TEngine *tEngine) {
|
||||||
|
auto *engine = reinterpret_cast<filament::Engine *>(tEngine);
|
||||||
|
auto *material = Material::Builder()
|
||||||
|
.package(GRID_GRID_DATA, GRID_GRID_SIZE)
|
||||||
|
.build(*engine);
|
||||||
|
|
||||||
|
return reinterpret_cast<TMaterial *>(material);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE bool Material_hasParameter(TMaterial *tMaterial, const char *propertyName) {
|
EMSCRIPTEN_KEEPALIVE bool Material_hasParameter(TMaterial *tMaterial, const char *propertyName) {
|
||||||
auto *material = reinterpret_cast<filament::Material *>(tMaterial);
|
auto *material = reinterpret_cast<filament::Material *>(tMaterial);
|
||||||
return material->hasParameter(propertyName);
|
return material->hasParameter(propertyName);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "c_api/TGltfAssetLoader.h"
|
#include "c_api/TGltfAssetLoader.h"
|
||||||
#include "c_api/TSceneAsset.h"
|
#include "c_api/TSceneAsset.h"
|
||||||
|
#include "scene/GridOverlay.hpp"
|
||||||
#include "scene/SceneAsset.hpp"
|
#include "scene/SceneAsset.hpp"
|
||||||
#include "scene/GltfSceneAsset.hpp"
|
#include "scene/GltfSceneAsset.hpp"
|
||||||
#include "scene/GeometrySceneAssetBuilder.hpp"
|
#include "scene/GeometrySceneAssetBuilder.hpp"
|
||||||
@@ -89,6 +90,13 @@ extern "C"
|
|||||||
return reinterpret_cast<TSceneAsset *>(sceneAsset);
|
return reinterpret_cast<TSceneAsset *>(sceneAsset);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EMSCRIPTEN_KEEPALIVE TSceneAsset *SceneAsset_createGrid(TEngine *tEngine, TMaterial* tMaterial) {
|
||||||
|
auto *engine = reinterpret_cast<filament::Engine *>(tEngine);
|
||||||
|
auto *material = reinterpret_cast<filament::Material *>(tMaterial);
|
||||||
|
auto *asset = new GridOverlay(*engine, material);
|
||||||
|
return reinterpret_cast<TSceneAsset *>(asset);
|
||||||
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_KEEPALIVE void SceneAsset_destroy(TSceneAsset *tSceneAsset) {
|
EMSCRIPTEN_KEEPALIVE void SceneAsset_destroy(TSceneAsset *tSceneAsset) {
|
||||||
auto *asset = reinterpret_cast<SceneAsset*>(tSceneAsset);
|
auto *asset = reinterpret_cast<SceneAsset*>(tSceneAsset);
|
||||||
|
|||||||
@@ -67,8 +67,8 @@ extern "C"
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto center = aabb.center();
|
auto center = filament::math::float3 { boundingBox.centerX, boundingBox.centerY, boundingBox.centerZ };
|
||||||
auto halfExtent = aabb.extent();
|
auto halfExtent = filament::math::float3 { boundingBox.halfExtentX, boundingBox.halfExtentY, boundingBox.halfExtentZ };
|
||||||
auto maxExtent = max(halfExtent) * 2;
|
auto maxExtent = max(halfExtent) * 2;
|
||||||
auto scaleFactor = 2.0f / maxExtent;
|
auto scaleFactor = 2.0f / maxExtent;
|
||||||
auto transform = math::mat4f::scaling(scaleFactor) * math::mat4f::translation(-center);
|
auto transform = math::mat4f::scaling(scaleFactor) * math::mat4f::translation(-center);
|
||||||
|
|||||||
@@ -1,21 +1,16 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart' hide View;
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:thermion_dart/src/viewer/src/shared_types/view.dart' as t;
|
|
||||||
import 'package:thermion_flutter/src/widgets/src/resize_observer.dart';
|
import 'package:thermion_flutter/src/widgets/src/resize_observer.dart';
|
||||||
import 'package:thermion_flutter/thermion_flutter.dart' hide Texture;
|
import 'package:thermion_flutter/thermion_flutter.dart' hide Texture;
|
||||||
|
|
||||||
class ThermionTextureWidget extends StatefulWidget {
|
class ThermionTextureWidget extends StatefulWidget {
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
final ThermionViewer viewer;
|
final ThermionViewer viewer;
|
||||||
|
|
||||||
///
|
|
||||||
///
|
|
||||||
///
|
|
||||||
final t.View view;
|
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
@@ -24,7 +19,7 @@ class ThermionTextureWidget extends StatefulWidget {
|
|||||||
///
|
///
|
||||||
/// A callback that will be invoked whenever this widget (and the underlying texture is resized).
|
/// A callback that will be invoked whenever this widget (and the underlying texture is resized).
|
||||||
///
|
///
|
||||||
final Future Function(Size size, t.View view, double pixelRatio)? onResize;
|
final Future Function(Size size, View view, double pixelRatio)? onResize;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// When true, an FPS counter will be displayed at the top right of the widget
|
/// When true, an FPS counter will be displayed at the top right of the widget
|
||||||
@@ -34,7 +29,6 @@ class ThermionTextureWidget extends StatefulWidget {
|
|||||||
const ThermionTextureWidget({
|
const ThermionTextureWidget({
|
||||||
super.key,
|
super.key,
|
||||||
required this.viewer,
|
required this.viewer,
|
||||||
required this.view,
|
|
||||||
this.initial,
|
this.initial,
|
||||||
this.onResize,
|
this.onResize,
|
||||||
this.showFpsCounter = false,
|
this.showFpsCounter = false,
|
||||||
@@ -49,7 +43,7 @@ class ThermionTextureWidget extends StatefulWidget {
|
|||||||
class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
|
class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
|
||||||
PlatformTextureDescriptor? _texture;
|
PlatformTextureDescriptor? _texture;
|
||||||
|
|
||||||
static final _views = <t.View>[];
|
static final _views = <View>[];
|
||||||
|
|
||||||
final _logger = Logger("_ThermionTextureWidgetState");
|
final _logger = Logger("_ThermionTextureWidgetState");
|
||||||
|
|
||||||
@@ -63,7 +57,7 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
|
|||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
_views.remove(widget.view);
|
_views.remove(widget.viewer.view);
|
||||||
if (_texture != null) {
|
if (_texture != null) {
|
||||||
ThermionFlutterPlatform.instance.destroyTextureDescriptor(_texture!);
|
ThermionFlutterPlatform.instance.destroyTextureDescriptor(_texture!);
|
||||||
}
|
}
|
||||||
@@ -73,10 +67,10 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
if (_views.contains(widget.view)) {
|
if (_views.contains(widget.viewer.view)) {
|
||||||
throw Exception("View already embedded in a widget");
|
throw Exception("View already embedded in a widget");
|
||||||
}
|
}
|
||||||
_views.add(widget.view);
|
_views.add(widget.viewer.view);
|
||||||
|
|
||||||
// Start FPS counter update timer if enabled
|
// Start FPS counter update timer if enabled
|
||||||
if (widget.showFpsCounter) {
|
if (widget.showFpsCounter) {
|
||||||
@@ -95,7 +89,6 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||||
await widget.viewer.initialized;
|
|
||||||
|
|
||||||
var dpr = MediaQuery.of(context).devicePixelRatio;
|
var dpr = MediaQuery.of(context).devicePixelRatio;
|
||||||
|
|
||||||
@@ -111,17 +104,17 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
|
|||||||
"Target texture dimensions ${width}x${height} (pixel ratio : $dpr)");
|
"Target texture dimensions ${width}x${height} (pixel ratio : $dpr)");
|
||||||
|
|
||||||
_texture = await ThermionFlutterPlatform.instance
|
_texture = await ThermionFlutterPlatform.instance
|
||||||
.createTextureAndBindToView(widget.view, width, height);
|
.createTextureAndBindToView(widget.viewer.view, width, height);
|
||||||
|
|
||||||
_logger.info(
|
_logger.info(
|
||||||
"Actual texture dimensions ${_texture!.width}x${_texture!.height} (pixel ratio : $dpr)");
|
"Actual texture dimensions ${_texture!.width}x${_texture!.height} (pixel ratio : $dpr)");
|
||||||
|
|
||||||
await widget.view.setViewport(_texture!.width, _texture!.height);
|
await widget.viewer.view.setViewport(_texture!.width, _texture!.height);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await widget.onResize?.call(
|
await widget.onResize?.call(
|
||||||
Size(_texture!.width.toDouble(), _texture!.height.toDouble()),
|
Size(_texture!.width.toDouble(), _texture!.height.toDouble()),
|
||||||
widget.view,
|
widget.viewer.view,
|
||||||
dpr);
|
dpr);
|
||||||
} catch (err, st) {
|
} catch (err, st) {
|
||||||
_logger.severe(err);
|
_logger.severe(err);
|
||||||
@@ -191,7 +184,7 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
|
|||||||
widget.viewer.msPerFrame - _headroomInMs)) {
|
widget.viewer.msPerFrame - _headroomInMs)) {
|
||||||
_rendering = true;
|
_rendering = true;
|
||||||
if (this == _states.first && _texture != null) {
|
if (this == _states.first && _texture != null) {
|
||||||
await widget.viewer.requestFrame();
|
await FilamentApp.instance!.requestFrame();
|
||||||
lastRender = d.inMilliseconds;
|
lastRender = d.inMilliseconds;
|
||||||
|
|
||||||
if (widget.showFpsCounter) {
|
if (widget.showFpsCounter) {
|
||||||
@@ -243,16 +236,16 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
|
|||||||
"Resizing texture to dimensions ${newWidth}x${newHeight} (pixel ratio : $dpr)");
|
"Resizing texture to dimensions ${newWidth}x${newHeight} (pixel ratio : $dpr)");
|
||||||
|
|
||||||
_texture = await ThermionFlutterPlatform.instance
|
_texture = await ThermionFlutterPlatform.instance
|
||||||
.resizeTexture(_texture!, widget.view, newWidth, newHeight);
|
.resizeTexture(_texture!, widget.viewer.view, newWidth, newHeight);
|
||||||
|
|
||||||
_logger.info(
|
_logger.info(
|
||||||
"Resized texture to dimensions ${_texture!.width}x${_texture!.height} (pixel ratio : $dpr)");
|
"Resized texture to dimensions ${_texture!.width}x${_texture!.height} (pixel ratio : $dpr)");
|
||||||
|
|
||||||
await widget.view.setViewport(_texture!.width, _texture!.height);
|
await widget.viewer.view.setViewport(_texture!.width, _texture!.height);
|
||||||
|
|
||||||
await widget.onResize?.call(
|
await widget.onResize?.call(
|
||||||
Size(_texture!.width.toDouble(), _texture!.height.toDouble()),
|
Size(_texture!.width.toDouble(), _texture!.height.toDouble()),
|
||||||
widget.view,
|
widget.viewer.view,
|
||||||
dpr);
|
dpr);
|
||||||
|
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import 'dart:io';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart' hide View;
|
||||||
import 'package:thermion_flutter/src/widgets/src/thermion_texture_widget.dart';
|
import 'package:thermion_flutter/src/widgets/src/thermion_texture_widget.dart';
|
||||||
import 'package:thermion_flutter/src/widgets/src/thermion_widget_web.dart';
|
import 'package:thermion_flutter/src/widgets/src/thermion_widget_web.dart';
|
||||||
import 'package:thermion_flutter/thermion_flutter.dart';
|
import 'package:thermion_flutter/thermion_flutter.dart';
|
||||||
import 'package:thermion_flutter_web/thermion_flutter_web_options.dart';
|
import 'package:thermion_flutter_web/thermion_flutter_web_options.dart';
|
||||||
import 'package:thermion_dart/src/viewer/src/shared_types/view.dart' as t;
|
|
||||||
|
|
||||||
Future kDefaultResizeCallback(Size size, t.View view, double pixelRatio) async {
|
Future kDefaultResizeCallback(Size size, View view, double pixelRatio) async {
|
||||||
var camera = await view.getCamera();
|
var camera = await view.getCamera();
|
||||||
var near = await camera.getNear();
|
var near = await camera.getNear();
|
||||||
var far = await camera.getCullingFar();
|
var far = await camera.getCullingFar();
|
||||||
@@ -26,11 +24,6 @@ class ThermionWidget extends StatefulWidget {
|
|||||||
///
|
///
|
||||||
final ThermionViewer viewer;
|
final ThermionViewer viewer;
|
||||||
|
|
||||||
///
|
|
||||||
/// The [t.View] associated with this widget. If null, the default View will be used.
|
|
||||||
///
|
|
||||||
final t.View? view;
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// A callback to invoke whenever this widget and the underlying surface are
|
/// A callback to invoke whenever this widget and the underlying surface are
|
||||||
/// resized. If a callback is not explicitly provided, the default callback
|
/// resized. If a callback is not explicitly provided, the default callback
|
||||||
@@ -45,7 +38,7 @@ class ThermionWidget extends StatefulWidget {
|
|||||||
/// If you need to work with Flutter dimensions, divide [size] by
|
/// If you need to work with Flutter dimensions, divide [size] by
|
||||||
/// [pixelRatio].
|
/// [pixelRatio].
|
||||||
///
|
///
|
||||||
final Future Function(Size size, t.View view, double pixelRatio)? onResize;
|
final Future Function(Size size, View view, double pixelRatio)? onResize;
|
||||||
|
|
||||||
final bool showFpsCounter;
|
final bool showFpsCounter;
|
||||||
|
|
||||||
@@ -59,7 +52,6 @@ class ThermionWidget extends StatefulWidget {
|
|||||||
{Key? key,
|
{Key? key,
|
||||||
this.initial,
|
this.initial,
|
||||||
required this.viewer,
|
required this.viewer,
|
||||||
this.view,
|
|
||||||
this.showFpsCounter = false,
|
this.showFpsCounter = false,
|
||||||
this.onResize = kDefaultResizeCallback})
|
this.onResize = kDefaultResizeCallback})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
@@ -69,42 +61,22 @@ class ThermionWidget extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ThermionWidgetState extends State<ThermionWidget> {
|
class _ThermionWidgetState extends State<ThermionWidget> {
|
||||||
t.View? view;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future initialize() async {
|
|
||||||
if (widget.view != null) {
|
|
||||||
view = widget.view;
|
|
||||||
} else {
|
|
||||||
view = await widget.viewer.getViewAt(0);
|
|
||||||
}
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (view == null) {
|
|
||||||
return widget.initial ?? Container(color: Colors.red);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Web doesn't support imported textures yet
|
// Web doesn't support imported textures yet
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
return ThermionWidgetWeb(
|
throw Exception();
|
||||||
viewer: widget.viewer,
|
// return ThermionWidgetWeb(
|
||||||
options: ThermionFlutterPlugin.options as ThermionFlutterWebOptions?);
|
// viewer: widget.viewer,
|
||||||
|
// options: ThermionFlutterPlugin.options as ThermionFlutterWebOptions?);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ThermionTextureWidget(
|
return ThermionTextureWidget(
|
||||||
key: ObjectKey(view!),
|
key: ObjectKey(widget.viewer),
|
||||||
initial: widget.initial,
|
initial: widget.initial,
|
||||||
viewer: widget.viewer,
|
viewer: widget.viewer,
|
||||||
view: view!,
|
view: widget.viewer.view,
|
||||||
showFpsCounter:widget.showFpsCounter,
|
showFpsCounter: widget.showFpsCounter,
|
||||||
onResize: widget.onResize);
|
onResize: widget.onResize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:thermion_flutter/thermion_flutter.dart' hide Texture;
|
||||||
|
|
||||||
|
enum ViewerManipulatorType { ORBIT, FIRST_PERSON }
|
||||||
|
|
||||||
|
class ViewerOptions {
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
final Widget initial;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// When true, an FPS counter will be displayed at the top right of the widget
|
||||||
|
///
|
||||||
|
final bool showFpsCounter;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
final String? assetPath;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
final String? skyboxPath;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
final String? iblPath;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
final LightType? directLightType;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
final bool transformToUnitCube;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
final bool postProcessing;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
final Color? background;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
final bool destroyAppOnUnload;
|
||||||
|
|
||||||
|
const ViewerOptions(
|
||||||
|
{this.initial =
|
||||||
|
const DecoratedBox(decoration: BoxDecoration(color: Colors.red)),
|
||||||
|
this.showFpsCounter = false,
|
||||||
|
this.transformToUnitCube = true,
|
||||||
|
this.postProcessing = true,
|
||||||
|
this.destroyAppOnUnload = false,
|
||||||
|
this.assetPath,
|
||||||
|
this.skyboxPath,
|
||||||
|
this.iblPath,
|
||||||
|
this.directLightType,
|
||||||
|
this.background});
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewerWidget extends StatefulWidget {
|
||||||
|
final ViewerOptions options;
|
||||||
|
|
||||||
|
const ViewerWidget({super.key, this.options = const ViewerOptions()});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() {
|
||||||
|
return _ViewerWidgetState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ViewerWidgetState extends State<ViewerWidget> {
|
||||||
|
ThermionViewer? viewer;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
ThermionFlutterPlugin.createViewer().then((viewer) async {
|
||||||
|
viewer = viewer;
|
||||||
|
|
||||||
|
setState(() {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
if (viewer != null) {
|
||||||
|
_tearDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future _tearDown() async {
|
||||||
|
await viewer!.dispose();
|
||||||
|
if (widget.options.destroyAppOnUnload) {
|
||||||
|
await viewer!.app.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (viewer == null) {
|
||||||
|
return widget.options.initial!;
|
||||||
|
}
|
||||||
|
return ThermionWidget(viewer: viewer!);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,24 +1,25 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:ffi';
|
import 'dart:ffi';
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:thermion_dart/thermion_dart.dart';
|
import 'package:thermion_dart/thermion_dart.dart';
|
||||||
import 'package:thermion_dart/thermion_dart.dart' as t;
|
import 'package:thermion_dart/src/filament/filament.dart';
|
||||||
|
import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_viewer_ffi.dart';
|
||||||
|
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
|
||||||
|
|
||||||
import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_interface.dart';
|
import 'package:thermion_flutter_platform_interface/thermion_flutter_platform_interface.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart';
|
import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dart';
|
||||||
import 'package:thermion_flutter_platform_interface/thermion_flutter_window.dart';
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// An abstract implementation of [ThermionFlutterPlatform] that uses
|
/// An implementation of [ThermionFlutterPlatform] that uses
|
||||||
/// Flutter platform channels to create a rendering context,
|
/// a Flutter platform channel to create a native rendering context, resource
|
||||||
/// resource loaders, and surface/render target(s).
|
/// loader and rendering surfaces.
|
||||||
///
|
///
|
||||||
class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform {
|
class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform {
|
||||||
final channel = const MethodChannel("dev.thermion.flutter/event");
|
final channel = const MethodChannel("dev.thermion.flutter/event");
|
||||||
final _logger = Logger("ThermionFlutterMethodChannelPlatform");
|
|
||||||
|
late final _logger = Logger(this.runtimeType.toString());
|
||||||
|
|
||||||
static SwapChain? _swapChain;
|
static SwapChain? _swapChain;
|
||||||
|
|
||||||
@@ -31,14 +32,17 @@ class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform {
|
|||||||
ThermionFlutterPlatform.instance = instance!;
|
ThermionFlutterPlatform.instance = instance!;
|
||||||
}
|
}
|
||||||
|
|
||||||
t.ThermionViewerFFI? viewer;
|
static Future<Uint8List> loadAsset(String path) async {
|
||||||
|
if (path.startsWith("file://")) {
|
||||||
|
return File(path.replaceAll("file://", "")).readAsBytesSync();
|
||||||
|
}
|
||||||
|
if (path.startsWith("asset://")) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
Future<ThermionViewer> createViewer({ThermionFlutterOptions? options}) async {
|
Future<ThermionViewer> createViewer({ThermionFlutterOptions? options}) async {
|
||||||
if (viewer != null) {
|
|
||||||
throw Exception(
|
|
||||||
"Only one ThermionViewer can be created at any given time; ensure you have called [dispose] on the previous instance before constructing a new instance.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var resourceLoader = Pointer<Void>.fromAddress(
|
var resourceLoader = Pointer<Void>.fromAddress(
|
||||||
await channel.invokeMethod("getResourceLoaderWrapper"));
|
await channel.invokeMethod("getResourceLoaderWrapper"));
|
||||||
|
|
||||||
@@ -47,6 +51,7 @@ class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var driverPlatform = await channel.invokeMethod("getDriverPlatform");
|
var driverPlatform = await channel.invokeMethod("getDriverPlatform");
|
||||||
|
|
||||||
var driverPtr = driverPlatform == null
|
var driverPtr = driverPlatform == null
|
||||||
? nullptr
|
? nullptr
|
||||||
: Pointer<Void>.fromAddress(driverPlatform);
|
: Pointer<Void>.fromAddress(driverPlatform);
|
||||||
@@ -57,21 +62,53 @@ class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform {
|
|||||||
? nullptr
|
? nullptr
|
||||||
: Pointer<Void>.fromAddress(sharedContext);
|
: Pointer<Void>.fromAddress(sharedContext);
|
||||||
|
|
||||||
viewer = ThermionViewerFFI(
|
late Backend backend;
|
||||||
|
if (options?.backend != null) {
|
||||||
|
switch (options!.backend) {
|
||||||
|
case Backend.VULKAN:
|
||||||
|
if (!Platform.isWindows) {
|
||||||
|
throw Exception("Vulkan only supported on Windows");
|
||||||
|
}
|
||||||
|
case Backend.METAL:
|
||||||
|
if (!Platform.isIOS || !Platform.isMacOS) {
|
||||||
|
throw Exception("Metal only supported on iOS/macOS");
|
||||||
|
}
|
||||||
|
case Backend.OPENGL:
|
||||||
|
if (!Platform.isAndroid) {
|
||||||
|
throw Exception("OpenGL only supported on Android");
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw Exception("Unsupported backend");
|
||||||
|
}
|
||||||
|
backend = options.backend!;
|
||||||
|
} else {
|
||||||
|
if (Platform.isWindows) {
|
||||||
|
backend = Backend.VULKAN;
|
||||||
|
} else if (Platform.isMacOS || Platform.isIOS) {
|
||||||
|
backend = Backend.METAL;
|
||||||
|
} else if (Platform.isAndroid) {
|
||||||
|
backend = Backend.OPENGL;
|
||||||
|
} else {
|
||||||
|
throw Exception("Unsupported platform");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final config = FFIFilamentConfig(
|
||||||
|
backend: backend,
|
||||||
resourceLoader: resourceLoader,
|
resourceLoader: resourceLoader,
|
||||||
driver: driverPtr,
|
driver: driverPtr,
|
||||||
|
platform: nullptr,
|
||||||
sharedContext: sharedContextPtr,
|
sharedContext: sharedContextPtr,
|
||||||
uberArchivePath: options?.uberarchivePath);
|
uberArchivePath: options?.uberarchivePath);
|
||||||
await viewer!.initialized;
|
|
||||||
|
|
||||||
viewer!.onDispose(() async {
|
await FFIFilamentApp.create(config);
|
||||||
_swapChain = null;
|
|
||||||
this.viewer = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (_swapChain != null) {
|
final viewer = ThermionViewerFFI(
|
||||||
throw Exception("Only a single swapchain can be created");
|
loadAsset: loadAsset,
|
||||||
}
|
);
|
||||||
|
|
||||||
|
await viewer.initialized;
|
||||||
|
|
||||||
|
viewer.onDispose(() async {});
|
||||||
|
|
||||||
// this implementation renders directly into a texture/render target
|
// this implementation renders directly into a texture/render target
|
||||||
// for some reason we still need to create a (headless) swapchain, but the
|
// for some reason we still need to create a (headless) swapchain, but the
|
||||||
@@ -79,7 +116,7 @@ class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform {
|
|||||||
// TODO - see if we can use `renderStandaloneView` in FilamentViewer to
|
// TODO - see if we can use `renderStandaloneView` in FilamentViewer to
|
||||||
// avoid this
|
// avoid this
|
||||||
if (Platform.isMacOS || Platform.isIOS) {
|
if (Platform.isMacOS || Platform.isIOS) {
|
||||||
_swapChain = await viewer!.createHeadlessSwapChain(1, 1);
|
_swapChain = await FilamentApp.instance!.createHeadlessSwapChain(1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return viewer!;
|
return viewer!;
|
||||||
@@ -96,8 +133,8 @@ class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform {
|
|||||||
final hardwareId = result[1] as int;
|
final hardwareId = result[1] as int;
|
||||||
var window = result[2] as int?; // usually 0 for nullptr
|
var window = result[2] as int?; // usually 0 for nullptr
|
||||||
|
|
||||||
return PlatformTextureDescriptor(flutterId, hardwareId, window, width, height);
|
return PlatformTextureDescriptor(
|
||||||
|
flutterId, hardwareId, window, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -109,43 +146,62 @@ class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform {
|
|||||||
///
|
///
|
||||||
///
|
///
|
||||||
Future<PlatformTextureDescriptor?> createTextureAndBindToView(
|
Future<PlatformTextureDescriptor?> createTextureAndBindToView(
|
||||||
t.View view, int width, int height) async {
|
View view, int width, int height) async {
|
||||||
var descriptor = await createTextureDescriptor(width, height);
|
var descriptor = await createTextureDescriptor(width, height);
|
||||||
|
|
||||||
|
|
||||||
if (Platform.isWindows) {
|
if (Platform.isWindows) {
|
||||||
if (_swapChain != null) {
|
if (_swapChain != null) {
|
||||||
await view.setRenderable(false, _swapChain!);
|
await FilamentApp.instance!.setRenderable(view, false);
|
||||||
await viewer!.destroySwapChain(_swapChain!);
|
await FilamentApp.instance!.destroySwapChain(_swapChain!);
|
||||||
}
|
}
|
||||||
|
|
||||||
_swapChain =
|
_swapChain = await FilamentApp.instance!
|
||||||
await viewer!.createHeadlessSwapChain(descriptor.width, descriptor.height);
|
.createHeadlessSwapChain(descriptor.width, descriptor.height);
|
||||||
} else if (Platform.isAndroid) {
|
} else if (Platform.isAndroid) {
|
||||||
if (_swapChain != null) {
|
if (_swapChain != null) {
|
||||||
await view.setRenderable(false, _swapChain!);
|
await FilamentApp.instance!.setRenderable(view, false);
|
||||||
await viewer!.destroySwapChain(_swapChain!);
|
await FilamentApp.instance!.destroySwapChain(_swapChain!);
|
||||||
}
|
}
|
||||||
_swapChain = await viewer!.createSwapChain(descriptor.windowHandle!);
|
_swapChain = await FilamentApp.instance!.createSwapChain(descriptor.windowHandle!);
|
||||||
} else {
|
} else {
|
||||||
var renderTarget = await viewer!.createRenderTarget(
|
final color = await FilamentApp.instance!.createTexture(
|
||||||
descriptor.width, descriptor.height, descriptor.hardwareId);
|
descriptor.width, descriptor.height,
|
||||||
|
importedTextureHandle: descriptor.hardwareId,
|
||||||
|
flags: {
|
||||||
|
TextureUsage.TEXTURE_USAGE_BLIT_DST,
|
||||||
|
TextureUsage.TEXTURE_USAGE_COLOR_ATTACHMENT,
|
||||||
|
TextureUsage.TEXTURE_USAGE_SAMPLEABLE
|
||||||
|
});
|
||||||
|
final depth =
|
||||||
|
await FilamentApp.instance!.createTexture(descriptor.width, descriptor.height, flags: {
|
||||||
|
TextureUsage.TEXTURE_USAGE_BLIT_DST,
|
||||||
|
TextureUsage.TEXTURE_USAGE_DEPTH_ATTACHMENT,
|
||||||
|
TextureUsage.TEXTURE_USAGE_SAMPLEABLE
|
||||||
|
});
|
||||||
|
|
||||||
await view.setRenderTarget(renderTarget!);
|
var renderTarget = await FilamentApp.instance!.createRenderTarget(
|
||||||
|
descriptor.width, descriptor.height,
|
||||||
|
color: color, depth: depth);
|
||||||
|
|
||||||
|
await view.setRenderTarget(renderTarget);
|
||||||
}
|
}
|
||||||
await view.setRenderable(true, _swapChain!);
|
await FilamentApp.instance!.register(_swapChain!, view);
|
||||||
|
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future markTextureFrameAvailable(PlatformTextureDescriptor texture) async {
|
Future markTextureFrameAvailable(PlatformTextureDescriptor texture) async {
|
||||||
await channel.invokeMethod("markTextureFrameAvailable", texture.flutterTextureId);
|
await channel.invokeMethod(
|
||||||
|
"markTextureFrameAvailable", texture.flutterTextureId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<PlatformTextureDescriptor> resizeTexture(PlatformTextureDescriptor texture,
|
Future<PlatformTextureDescriptor> resizeTexture(
|
||||||
t.View view, int width, int height) async {
|
PlatformTextureDescriptor texture,
|
||||||
|
View view,
|
||||||
|
int width,
|
||||||
|
int height) async {
|
||||||
var newTexture = await createTextureAndBindToView(view, width, height);
|
var newTexture = await createTextureAndBindToView(view, width, height);
|
||||||
if (newTexture == null) {
|
if (newTexture == null) {
|
||||||
throw Exception();
|
throw Exception();
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ dependencies:
|
|||||||
thermion_dart: ^0.2.1-dev.20.0
|
thermion_dart: ^0.2.1-dev.20.0
|
||||||
logging: ^1.2.0
|
logging: ^1.2.0
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
|
thermion_dart:
|
||||||
|
path: ../../thermion_dart
|
||||||
thermion_flutter_platform_interface:
|
thermion_flutter_platform_interface:
|
||||||
path: ../thermion_flutter_platform_interface
|
path: ../thermion_flutter_platform_interface
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|||||||
Reference in New Issue
Block a user