refactoring

This commit is contained in:
Nick Fisher
2025-03-21 14:56:20 +08:00
parent 1177a71f73
commit 255c0edd49
38 changed files with 1521 additions and 1207 deletions

View File

@@ -32,6 +32,11 @@ abstract class ThermionAsset {
/// ///
Future<List<ThermionEntity>> getChildEntities(); Future<List<ThermionEntity>> getChildEntities();
///
///
///
Future<List<String?>> getChildEntityNames();
/// ///
/// ///
/// ///
@@ -91,6 +96,21 @@ abstract class ThermionAsset {
/// ///
Future setReceiveShadows(bool castShadows); Future setReceiveShadows(bool castShadows);
///
///
///
Future<bool> isCastShadowsEnabled({ThermionEntity? entity});
///
///
///
Future<bool> isReceiveShadowsEnabled({ThermionEntity? entity});
///
///
///
Future transformToUnitCube();
/// ///
/// All renderable entities are assigned a layer mask. /// All renderable entities are assigned a layer mask.
/// ///

View File

@@ -241,7 +241,7 @@ abstract class FilamentApp<T> {
/// ///
/// ///
/// ///
Future<ThermionAsset> loadGlbFromBuffer(Uint8List data, T animationManager, Future<ThermionAsset> loadGltfFromBuffer(Uint8List data, T animationManager,
{int numInstances = 1, {int numInstances = 1,
bool keepData = false, bool keepData = false,
int priority = 4, int priority = 4,

View File

@@ -31,8 +31,8 @@ class BackgroundImage extends ThermionAsset {
Future destroy() async { Future destroy() async {
Scene_removeEntity(scene.scene, entity); Scene_removeEntity(scene.scene, entity);
await texture!.dispose(); await texture?.dispose();
await sampler!.dispose(); await sampler?.dispose();
await mi.destroy(); await mi.destroy();
} }
@@ -63,6 +63,10 @@ class BackgroundImage extends ThermionAsset {
await mi.setParameterFloat4("backgroundColor", r, g, b, a); await mi.setParameterFloat4("backgroundColor", r, g, b, a);
} }
Future hideImage() async {
await mi.setParameterInt("showImage", 0);
}
/// ///
/// ///
/// ///
@@ -317,4 +321,25 @@ class BackgroundImage extends ThermionAsset {
// TODO: implement updateBoneMatrices // TODO: implement updateBoneMatrices
throw UnimplementedError(); throw UnimplementedError();
} }
@override
Future<List<String>> getChildEntityNames() async {
return [];
}
@override
Future<bool> isCastShadowsEnabled({ThermionEntity? entity}) async {
return false;
}
@override
Future<bool> isReceiveShadowsEnabled({ThermionEntity? entity}) async {
return false;
}
@override
Future transformToUnitCube() {
// TODO: implement transformToUnitCube
throw UnimplementedError();
}
} }

View File

@@ -21,7 +21,7 @@ Future<void> withVoidCallback2(Function() func) async {
completer.complete(); completer.complete();
}; };
final nativeCallable = NativeCallable<Void Function()>.listener(callback); final nativeCallable = NativeCallable<Void Function()>.listener(callback);
RenderLoop_addTask(nativeCallable.nativeFunction); RenderThread_addTask(nativeCallable.nativeFunction);
await completer.future; await completer.future;
nativeCallable.close(); nativeCallable.close();
} }

View File

@@ -63,6 +63,24 @@ class FFIAsset extends ThermionAsset {
return children; return children;
} }
///
///
///
@override
Future<List<String?>> getChildEntityNames() async {
final childEntities = await getChildEntities();
var names = <String?>[];
for (final entity in childEntities) {
var name = NameComponentManager_getName(app.nameComponentManager, entity);
if (name == nullptr) {
names.add(null);
} else {
names.add(name.cast<Utf8>().toDartString());
}
}
return names;
}
/// ///
/// ///
/// ///
@@ -392,14 +410,16 @@ class FFIAsset extends ThermionAsset {
/// ///
/// ///
/// ///
Future<bool> isCastShadowsEnabled(ThermionEntity entity) async { Future<bool> isCastShadowsEnabled({ThermionEntity? entity}) async {
entity ??= this.entity;
return RenderableManager_isShadowCaster(app.renderableManager, entity); return RenderableManager_isShadowCaster(app.renderableManager, entity);
} }
/// ///
/// ///
/// ///
Future<bool> isReceiveShadowsEnabled(ThermionEntity entity) async { Future<bool> isReceiveShadowsEnabled({ThermionEntity? entity}) async {
entity ??= this.entity;
return RenderableManager_isShadowReceiver(app.renderableManager, entity); return RenderableManager_isShadowReceiver(app.renderableManager, entity);
} }

View File

@@ -70,8 +70,8 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
await FilamentApp.instance!.destroy(); await FilamentApp.instance!.destroy();
} }
RenderLoop_destroy(); RenderThread_destroy();
RenderLoop_create(); RenderThread_create();
final engine = await withPointerCallback<TEngine>((cb) => final engine = await withPointerCallback<TEngine>((cb) =>
Engine_createRenderThread( Engine_createRenderThread(
@@ -95,6 +95,8 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
final renderTicker = RenderTicker_create(renderer); final renderTicker = RenderTicker_create(renderer);
RenderThread_setRenderTicker(renderTicker);
final nameComponentManager = NameComponentManager_create(); final nameComponentManager = NameComponentManager_create();
FilamentApp.instance = FFIFilamentApp( FilamentApp.instance = FFIFilamentApp(
@@ -110,30 +112,34 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
config.resourceLoader); config.resourceLoader);
} }
final _views = <FFISwapChain, List<FFIView>>{}; final _swapChains = <FFISwapChain, List<FFIView>>{};
final _viewMappings = <FFIView, FFISwapChain>{}; final viewsPtr = calloc<Pointer<TView>>(255);
/// ///
/// ///
/// ///
Future setRenderable(covariant FFIView view, bool renderable) async { Future setRenderable(covariant FFIView view, bool renderable) async {
final swapChain = _viewMappings[view]!; await view.setRenderable(renderable);
if (renderable && !_views[swapChain]!.contains(view)) { await _updateRenderableSwapChains();
_views[swapChain]!.add(view); }
} else if (!renderable && _views[swapChain]!.contains(view)) {
_views[swapChain]!.remove(view);
}
final views = calloc<Pointer<TView>>(255); Future _updateRenderableSwapChains() async {
for (final swapChain in _views.keys) { for (final swapChain in _swapChains.keys) {
var numViews = _views[swapChain]!.length; final views = _swapChains[swapChain];
for (int i = 0; i < numViews; i++) { if (views == null) {
views[i] = _views[swapChain]![i].view; continue;
}
int numRenderable = 0;
for (final view in views) {
if (view.renderable) {
viewsPtr[numRenderable] = view.view;
numRenderable++;
}
} }
RenderTicker_setRenderable( RenderTicker_setRenderable(
renderTicker, swapChain.swapChain, views, numViews); renderTicker, swapChain.swapChain, viewsPtr, numRenderable);
} }
calloc.free(views);
} }
@override @override
@@ -143,6 +149,7 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
if (hasStencilBuffer) { if (hasStencilBuffer) {
flags |= TSWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER; flags |= TSWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER;
} }
print("swapchain flags $flags");
final swapChain = await withPointerCallback<TSwapChain>((cb) => final swapChain = await withPointerCallback<TSwapChain>((cb) =>
Engine_createHeadlessSwapChainRenderThread( Engine_createHeadlessSwapChainRenderThread(
this.engine, width, height, flags, cb)); this.engine, width, height, flags, cb));
@@ -173,6 +180,7 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
Engine_destroySwapChainRenderThread( Engine_destroySwapChainRenderThread(
engine, (swapChain as FFISwapChain).swapChain, callback); engine, (swapChain as FFISwapChain).swapChain, callback);
}); });
_swapChains.remove(swapChain);
} }
/// ///
@@ -180,17 +188,21 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
/// ///
@override @override
Future destroy() async { Future destroy() async {
for (final swapChain in _views.keys) { for (final swapChain in _swapChains.keys) {
for (final view in _views[swapChain]!) { if (_swapChains[swapChain] == null) {
continue;
}
for (final view in _swapChains[swapChain]!) {
await setRenderable(view, false); await setRenderable(view, false);
} }
} }
for (final swapChain in _views.keys) { for (final swapChain in _swapChains.keys.toList()) {
await destroySwapChain(swapChain); await destroySwapChain(swapChain);
} }
RenderLoop_destroy(); RenderThread_destroy();
RenderTicker_destroy(renderTicker); RenderTicker_destroy(renderTicker);
Engine_destroy(engine); Engine_destroy(engine);
calloc.free(viewsPtr);
} }
/// ///
@@ -440,8 +452,24 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
/// ///
@override @override
Future render() async { Future render() async {
await withVoidCallback( // await withVoidCallback(
(cb) => RenderTicker_renderRenderThread(renderTicker, 0, cb)); // (cb) => RenderTicker_renderRenderThread(renderTicker, 0, cb));
final swapchain = _swapChains.keys.first;
final view = _swapChains[swapchain]!.first;
await withBoolCallback((cb) {
Renderer_beginFrameRenderThread(renderer, swapchain.swapChain, 0, cb);
});
await withVoidCallback((cb) {
Renderer_renderRenderThread(
renderer,
view.view,
cb,
);
});
await withVoidCallback((cb) {
Renderer_endFrameRenderThread(renderer, cb);
});
} }
/// ///
@@ -450,11 +478,11 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
@override @override
Future register( Future register(
covariant FFISwapChain swapChain, covariant FFIView view) async { covariant FFISwapChain swapChain, covariant FFIView view) async {
_viewMappings[view] = swapChain; if (!_swapChains.containsKey(swapChain)) {
if (!_views.containsKey(swapChain)) { _swapChains[swapChain] = [];
_views[swapChain] = [];
} }
_views[swapChain]!.add(view); _swapChains[swapChain]!.add(view);
await _updateRenderableSwapChains();
} }
final _hooks = <Future Function()>[]; final _hooks = <Future Function()>[];
@@ -493,7 +521,7 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
completer.complete(true); completer.complete(true);
}); });
RenderLoop_requestAnimationFrame(callback.nativeFunction.cast()); RenderThread_requestAnimationFrame(callback.nativeFunction.cast());
try { try {
await completer.future.timeout(Duration(seconds: 1)); await completer.future.timeout(Duration(seconds: 1));
@@ -572,15 +600,20 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
Future<Uint8List> capture(covariant FFIView view, Future<Uint8List> capture(covariant FFIView view,
{bool captureRenderTarget = false}) async { {bool captureRenderTarget = false}) async {
final viewport = await view.getViewport(); final viewport = await view.getViewport();
final swapChain = _viewMappings[view]; final swapChain = _swapChains.keys
.firstWhere((x) => _swapChains[x]?.contains(view) == true);
final out = Uint8List(viewport.width * viewport.height * 4); final out = Uint8List(viewport.width * viewport.height * 4);
await withVoidCallback((cb) { await withVoidCallback((cb) {
Engine_flushAndWaitRenderThead(engine, cb); Engine_flushAndWaitRenderThead(engine, cb);
}); });
var fence = await withPointerCallback<TFence>((cb) {
Engine_createFenceRenderThread(engine, cb);
});
await withBoolCallback((cb) { await withBoolCallback((cb) {
Renderer_beginFrameRenderThread(renderer, swapChain!.swapChain, 0, cb); Renderer_beginFrameRenderThread(renderer, swapChain.swapChain, 0, cb);
}); });
await withVoidCallback((cb) { await withVoidCallback((cb) {
Renderer_renderRenderThread( Renderer_renderRenderThread(
@@ -607,9 +640,14 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
await withVoidCallback((cb) { await withVoidCallback((cb) {
Renderer_endFrameRenderThread(renderer, cb); Renderer_endFrameRenderThread(renderer, cb);
}); });
await withVoidCallback((cb) { await withVoidCallback((cb) {
Engine_flushAndWaitRenderThead(engine, cb); Engine_destroyFenceRenderThread(engine, fence, cb);
}); });
// await withVoidCallback((cb) {
// Engine_flushAndWaitRenderThead(engine, cb);
// });
return out; return out;
} }
@@ -623,7 +661,7 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
/// ///
/// ///
/// ///
Future<ThermionAsset> loadGlbFromBuffer( Future<ThermionAsset> loadGltfFromBuffer(
Uint8List data, Pointer animationManager, Uint8List data, Pointer animationManager,
{int numInstances = 1, {int numInstances = 1,
bool keepData = false, bool keepData = false,
@@ -631,7 +669,7 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
int layer = 0, int layer = 0,
String? relativeResourcePath, String? relativeResourcePath,
bool loadResourcesAsync = false}) async { bool loadResourcesAsync = false}) async {
if (relativeResourcePath != null && !relativeResourcePath!.endsWith("/")) { if (relativeResourcePath != null && !relativeResourcePath.endsWith("/")) {
relativeResourcePath = "$relativeResourcePath/"; relativeResourcePath = "$relativeResourcePath/";
} }
var gltfResourceLoader = await withPointerCallback<TGltfResourceLoader>( var gltfResourceLoader = await withPointerCallback<TGltfResourceLoader>(
@@ -648,7 +686,6 @@ class FFIFilamentApp extends FilamentApp<Pointer> {
var resourceUris = SceneAsset_getResourceUris(asset); var resourceUris = SceneAsset_getResourceUris(asset);
var resourceUriCount = SceneAsset_getResourceUriCount(asset); var resourceUriCount = SceneAsset_getResourceUriCount(asset);
final resources = <FinalizableUint8List>[]; final resources = <FinalizableUint8List>[];
for (int i = 0; i < resourceUriCount; i++) { for (int i = 0; i < resourceUriCount; i++) {

View File

@@ -202,7 +202,7 @@ class FFIMaterialInstance extends MaterialInstance {
completer.complete(); completer.complete();
}; };
final nativeCallable = NativeCallable<Void Function()>.listener(func); final nativeCallable = NativeCallable<Void Function()>.listener(func);
RenderLoop_addTask(nativeCallable.nativeFunction); RenderThread_addTask(nativeCallable.nativeFunction);
await completer.future; await completer.future;
} }
} }

View File

@@ -11,6 +11,9 @@ class FFIView extends View {
final Pointer<TView> view; final Pointer<TView> view;
final FFIFilamentApp app; final FFIFilamentApp app;
bool _renderable = false;
bool get renderable => _renderable;
FFIRenderTarget? renderTarget; FFIRenderTarget? renderTarget;
FFIView(this.view, this.app) { FFIView(this.view, this.app) {
@@ -20,8 +23,19 @@ class FFIView extends View {
} }
} }
Future setRenderable(bool renderable) async {
this._renderable = renderable;
}
@override @override
Future setViewport(int width, int height) async { Future setViewport(int width, int height) async {
// var width_logbase2 = log(width) / ln2;
// var height_logbase2 = log(height) / ln2;
// var newWidth = pow(2.0, width_logbase2.ceil());
// var newHeight = pow(2.0, height_logbase2.ceil());
// print("old: ${width}x${height} new: ${height}x${newHeight}");
// width = newWidth.toInt();
// height = newHeight.toInt();
View_setViewport(view, width, height); View_setViewport(view, width, height);
} }
@@ -80,8 +94,7 @@ class FFIView extends View {
@override @override
Future setToneMapper(ToneMapper mapper) async { Future setToneMapper(ToneMapper mapper) async {
await withVoidCallback((cb) => await withVoidCallback((cb) => View_setToneMappingRenderThread(
View_setToneMappingRenderThread(
view, app.engine, TToneMapping.values[mapper.index], cb)); view, app.engine, TToneMapping.values[mapper.index], cb));
} }

View File

@@ -473,6 +473,22 @@ external TViewport View_getViewport(
ffi.Pointer<TView> view, ffi.Pointer<TView> view,
); );
@ffi.Native<ffi.Void Function(ffi.Pointer<TView>, ffi.UnsignedInt)>(
symbol: "View_setBlendMode", isLeaf: true)
external void _View_setBlendMode(
ffi.Pointer<TView> view,
int blendMode,
);
void View_setBlendMode(
ffi.Pointer<TView> view,
TBlendMode blendMode,
) =>
_View_setBlendMode(
view,
blendMode.value,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<TView>, ffi.Uint32, ffi.Uint32)>( @ffi.Native<ffi.Void Function(ffi.Pointer<TView>, ffi.Uint32, ffi.Uint32)>(
isLeaf: true) isLeaf: true)
external void View_setViewport( external void View_setViewport(
@@ -1494,18 +1510,30 @@ external void RenderTicker_setRenderable(
); );
@ffi.Native<ffi.Void Function()>(isLeaf: true) @ffi.Native<ffi.Void Function()>(isLeaf: true)
external void RenderLoop_create(); external void RenderThread_create();
@ffi.Native<ffi.Void Function()>(isLeaf: true) @ffi.Native<ffi.Void Function()>(isLeaf: true)
external void RenderLoop_destroy(); external void RenderThread_destroy();
@ffi.Native< @ffi.Native<
ffi.Void Function( ffi.Void Function(
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true) ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
external void RenderLoop_requestAnimationFrame( external void RenderThread_requestAnimationFrame(
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete, ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete,
); );
@ffi.Native<ffi.Void Function(ffi.Pointer<TRenderTicker>)>(isLeaf: true)
external void RenderThread_setRenderTicker(
ffi.Pointer<TRenderTicker> tRenderTicker,
);
@ffi.Native<
ffi.Void Function(
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
external void RenderThread_addTask(
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> task,
);
@ffi.Native< @ffi.Native<
ffi.Void Function(ffi.Pointer<TRenderTicker>, ffi.Uint64, ffi.Void Function(ffi.Pointer<TRenderTicker>, ffi.Uint64,
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true) ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
@@ -1515,13 +1543,6 @@ external void RenderTicker_renderRenderThread(
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete, ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> onComplete,
); );
@ffi.Native<
ffi.Void Function(
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)>(isLeaf: true)
external void RenderLoop_addTask(
ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> task,
);
@ffi.Native< @ffi.Native<
ffi.Void Function( ffi.Void Function(
ffi.Pointer<TEngine>, ffi.Pointer<TEngine>,
@@ -3212,6 +3233,13 @@ external ffi.Pointer<TAnimationManager> AnimationManager_create(
ffi.Pointer<TScene> tScene, ffi.Pointer<TScene> tScene,
); );
@ffi.Native<ffi.Void Function(ffi.Pointer<TAnimationManager>, ffi.Uint64)>(
isLeaf: true)
external void AnimationManager_update(
ffi.Pointer<TAnimationManager> tAnimationManager,
int frameTimeInNanos,
);
@ffi.Native<ffi.Void Function(ffi.Pointer<TAnimationManager>, EntityId)>( @ffi.Native<ffi.Void Function(ffi.Pointer<TAnimationManager>, EntityId)>(
isLeaf: true) isLeaf: true)
external void AnimationManager_addAnimationComponent( external void AnimationManager_addAnimationComponent(
@@ -3951,6 +3979,20 @@ enum TQualityLevel {
}; };
} }
enum TBlendMode {
OPAQUE(0),
TRANSLUCENT(1);
final int value;
const TBlendMode(this.value);
static TBlendMode fromValue(int value) => switch (value) {
0 => OPAQUE,
1 => TRANSLUCENT,
_ => throw ArgumentError("Unknown value for TBlendMode: $value"),
};
}
typedef PickCallback = ffi.Pointer<ffi.NativeFunction<PickCallbackFunction>>; typedef PickCallback = ffi.Pointer<ffi.NativeFunction<PickCallbackFunction>>;
typedef PickCallbackFunction = ffi.Void Function( typedef PickCallbackFunction = ffi.Void Function(
ffi.Uint32 requestId, ffi.Uint32 requestId,

View File

@@ -33,8 +33,6 @@ class ThermionViewerFFI extends ThermionViewer {
late final FFIFilamentApp app; late final FFIFilamentApp app;
final FFIRenderTarget? renderTarget;
late final FFIView view; late final FFIView view;
late final FFIScene scene; late final FFIScene scene;
late final Pointer<TAnimationManager> animationManager; late final Pointer<TAnimationManager> animationManager;
@@ -43,7 +41,7 @@ class ThermionViewerFFI extends ThermionViewer {
/// ///
/// ///
/// ///
ThermionViewerFFI({required this.loadAssetFromUri, this.renderTarget}) { ThermionViewerFFI({required this.loadAssetFromUri}) {
if (FilamentApp.instance == null) { if (FilamentApp.instance == null) {
throw Exception("FilamentApp has not been created"); throw Exception("FilamentApp has not been created");
} }
@@ -56,7 +54,7 @@ class ThermionViewerFFI extends ThermionViewer {
/// ///
/// ///
/// ///
Future setViewport(double width, double height) async { Future setViewport(int width, int height) async {
await view.setViewport(width.toInt(), height.toInt()); await view.setViewport(width.toInt(), height.toInt());
for (final camera in _cameras) { for (final camera in _cameras) {
@@ -69,12 +67,12 @@ class ThermionViewerFFI extends ThermionViewer {
far = kFar; far = kFar;
} }
var aspect = width / height; var aspect = width.toDouble() / height.toDouble();
var focalLength = await camera.getFocalLength(); var focalLength = await camera.getFocalLength();
if (focalLength.abs() < 0.1) { if (focalLength.abs() < 0.1) {
focalLength = kFocalLength; focalLength = kFocalLength;
} }
camera.setLensProjection( await camera.setLensProjection(
near: near, far: far, aspect: aspect, focalLength: focalLength); near: near, far: far, aspect: aspect, focalLength: focalLength);
} }
} }
@@ -85,6 +83,14 @@ class ThermionViewerFFI extends ThermionViewer {
await withPointerCallback<TView>( await withPointerCallback<TView>(
(cb) => Engine_createViewRenderThread(app.engine, cb)), (cb) => Engine_createViewRenderThread(app.engine, cb)),
app); app);
await view.setFrustumCullingEnabled(true);
View_setBlendMode(view.view, TBlendMode.TRANSLUCENT);
View_setShadowsEnabled(view.view, false);
View_setStencilBufferEnabled(view.view, false);
View_setAntiAliasing(view.view, false, false, false);
View_setDitheringEnabled(view.view, false);
View_setRenderQuality(view.view, TQualityLevel.MEDIUM);
await FilamentApp.instance!.setClearColor(1.0, 0.0, 0.0, 1.0);
scene = FFIScene(Engine_createScene(app.engine)); scene = FFIScene(Engine_createScene(app.engine));
await view.setScene(scene); await view.setScene(scene);
final camera = FFICamera( final camera = FFICamera(
@@ -92,12 +98,10 @@ class ThermionViewerFFI extends ThermionViewer {
(cb) => Engine_createCameraRenderThread(app.engine, cb)), (cb) => Engine_createCameraRenderThread(app.engine, cb)),
app); app);
_cameras.add(camera); _cameras.add(camera);
await camera.setLensProjection();
await view.setCamera(camera); await view.setCamera(camera);
if (renderTarget != null) {
await view.setRenderTarget(renderTarget);
}
animationManager = await withPointerCallback<TAnimationManager>((cb) => animationManager = await withPointerCallback<TAnimationManager>((cb) =>
AnimationManager_createRenderThread(app.engine, scene.scene, cb)); AnimationManager_createRenderThread(app.engine, scene.scene, cb));
@@ -180,9 +184,13 @@ class ThermionViewerFFI extends ThermionViewer {
/// ///
/// ///
@override @override
Future clearBackgroundImage() async { Future clearBackgroundImage({bool destroy = false}) async {
await _backgroundImage?.destroy(); if (destroy) {
_backgroundImage = null; await _backgroundImage?.destroy();
_backgroundImage = null;
} else {
_backgroundImage?.hideImage();
}
} }
/// ///
@@ -335,7 +343,7 @@ class ThermionViewerFFI extends ThermionViewer {
/// ///
@override @override
Future destroyLights() async { Future destroyLights() async {
for (final light in _lights) { for (final light in _lights.toList()) {
await removeLight(light); await removeLight(light);
} }
} }
@@ -347,37 +355,45 @@ class ThermionViewerFFI extends ThermionViewer {
/// ///
/// ///
@override @override
Future<ThermionAsset> loadGlb(String path, Future<ThermionAsset> loadGltf(String path,
{int numInstances = 1, bool keepData = false, String? relativeResourcePath}) async { {bool addToScene = true,
int numInstances = 1,
bool keepData = false,
String? relativeResourcePath}) async {
final data = await loadAssetFromUri(path); final data = await loadAssetFromUri(path);
return loadGlbFromBuffer(data, return loadGltfFromBuffer(data,
numInstances: numInstances, keepData: keepData, relativeResourcePath: relativeResourcePath); addToScene: addToScene,
numInstances: numInstances,
keepData: keepData,
relativeResourcePath: relativeResourcePath);
} }
/// ///
/// ///
/// ///
@override @override
Future<ThermionAsset> loadGlbFromBuffer(Uint8List data, Future<ThermionAsset> loadGltfFromBuffer(Uint8List data,
{int numInstances = 1, {bool addToScene = true,
int numInstances = 1,
bool keepData = false, bool keepData = false,
int priority = 4, int priority = 4,
int layer = 0, int layer = 0,
bool loadResourcesAsync = false, bool loadResourcesAsync = false,
String? relativeResourcePath}) async { String? relativeResourcePath}) async {
var asset = await FilamentApp.instance!.loadGlbFromBuffer(data, animationManager, var asset = await FilamentApp.instance!.loadGltfFromBuffer(
data, animationManager,
numInstances: numInstances, numInstances: numInstances,
keepData: keepData, keepData: keepData,
priority: priority, priority: priority,
layer: layer, layer: layer,
loadResourcesAsync: loadResourcesAsync, loadResourcesAsync: loadResourcesAsync,
relativeResourcePath:relativeResourcePath relativeResourcePath: relativeResourcePath) as FFIAsset;
) as FFIAsset;
_assets.add(asset);
await scene.add(asset); _assets.add(asset);
if (addToScene) {
await scene.add(asset);
}
return asset; return asset;
} }
@@ -424,6 +440,8 @@ class ThermionViewerFFI extends ThermionViewer {
@override @override
Future setPostProcessing(bool enabled) async { Future setPostProcessing(bool enabled) async {
View_setPostProcessing(view.view, enabled); View_setPostProcessing(view.view, enabled);
await withVoidCallback(
(cb) => Engine_flushAndWaitRenderThead(app.engine, cb));
} }
/// ///
@@ -655,7 +673,8 @@ class ThermionViewerFFI extends ThermionViewer {
@override @override
Future<ThermionAsset> createGeometry(Geometry geometry, Future<ThermionAsset> createGeometry(Geometry geometry,
{List<MaterialInstance>? materialInstances, {List<MaterialInstance>? materialInstances,
bool keepData = false}) async { bool keepData = false,
bool addToScene = true}) async {
var assetPtr = await withPointerCallback<TSceneAsset>((callback) { var assetPtr = await withPointerCallback<TSceneAsset>((callback) {
var ptrList = Int64List(materialInstances?.length ?? 0); var ptrList = Int64List(materialInstances?.length ?? 0);
if (materialInstances != null && materialInstances.isNotEmpty) { if (materialInstances != null && materialInstances.isNotEmpty) {
@@ -688,7 +707,9 @@ class ThermionViewerFFI extends ThermionViewer {
} }
var asset = FFIAsset(assetPtr, app, animationManager); var asset = FFIAsset(assetPtr, app, animationManager);
if (addToScene) {
await scene.add(asset);
}
return asset; return asset;
} }

View File

@@ -71,7 +71,7 @@ abstract class ThermionViewer {
/// ///
/// Removes the background image. /// Removes the background image.
/// ///
Future clearBackgroundImage(); Future clearBackgroundImage({bool destroy = false});
/// ///
/// Sets the color for the background plane (positioned at the maximum depth, i.e. behind all other objects including the skybox). /// Sets the color for the background plane (positioned at the maximum depth, i.e. behind all other objects including the skybox).
@@ -128,10 +128,11 @@ abstract class ThermionViewer {
/// If you want to be able to call [createInstance] at a later time, you must pass true for [keepData]. /// If you want to be able to call [createInstance] at a later time, you must pass true for [keepData].
/// If [keepData] is false, the source glTF data will be released and [createInstance] will throw an exception. /// If [keepData] is false, the source glTF data will be released and [createInstance] will throw an exception.
/// ///
Future<ThermionAsset> loadGlb(String path, Future<ThermionAsset> loadGltf(String path,
{int numInstances = 1, { bool addToScene=true,
int numInstances = 1,
bool keepData = false, bool keepData = false,
String relativeResourcePath}); String? relativeResourcePath});
/// ///
/// Load the .glb asset from the specified buffer, adding all entities to the scene. /// Load the .glb asset from the specified buffer, adding all entities to the scene.
@@ -142,7 +143,7 @@ abstract class ThermionViewer {
/// be loaded asynchronously (so expect some material/texture pop-in); /// be loaded asynchronously (so expect some material/texture pop-in);
/// ///
/// ///
Future<ThermionAsset> loadGlbFromBuffer(Uint8List data, Future<ThermionAsset> loadGltfFromBuffer(Uint8List data,
{int numInstances = 1, {int numInstances = 1,
bool keepData = false, bool keepData = false,
int priority = 4, int priority = 4,
@@ -177,6 +178,11 @@ abstract class ThermionViewer {
/// ///
Future setViewFrustumCulling(bool enabled); Future setViewFrustumCulling(bool enabled);
///
/// Sets the viewport sizes and updates all cameras to use the new aspect ratio.
///
Future setViewport(int width, int height);
/// ///
/// Set the world space position for [lightEntity] to the given coordinates. /// Set the world space position for [lightEntity] to the given coordinates.
/// ///
@@ -230,7 +236,7 @@ abstract class ThermionViewer {
/// ///
/// ///
Future<ThermionAsset> createGeometry(Geometry geometry, Future<ThermionAsset> createGeometry(Geometry geometry,
{List<MaterialInstance>? materialInstances, bool keepData = false}); {List<MaterialInstance>? materialInstances, bool keepData = false, bool addToScene=true});
/// ///
/// The gizmo for translating/rotating objects. Only one gizmo can be active for a given view. /// The gizmo for translating/rotating objects. Only one gizmo can be active for a given view.

View File

@@ -9,6 +9,12 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError(); throw UnimplementedError();
} }
@override
Future addToScene(covariant ThermionAsset asset) {
// TODO: implement addToScene
throw UnimplementedError();
}
@override @override
Future clearBackgroundImage() { Future clearBackgroundImage() {
// TODO: implement clearBackgroundImage // TODO: implement clearBackgroundImage
@@ -21,12 +27,6 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError(); throw UnimplementedError();
} }
@override
Future<ThermionAsset> createGeometry(Geometry geometry, {List<MaterialInstance>? materialInstances, bool keepData = false}) {
// TODO: implement createGeometry
throw UnimplementedError();
}
@override @override
Future<GizmoAsset> createGizmo(covariant View view, GizmoType type) { Future<GizmoAsset> createGizmo(covariant View view, GizmoType type) {
// TODO: implement createGizmo // TODO: implement createGizmo
@@ -92,23 +92,17 @@ class ThermionViewerStub extends ThermionViewer {
Future<bool> get initialized => throw UnimplementedError(); Future<bool> get initialized => throw UnimplementedError();
@override @override
Future<ThermionAsset> loadGlb(String path, {int numInstances = 1, bool keepData = false}) { Future<ThermionAsset> loadGltf(String path, {int numInstances = 1, bool keepData = false, String? relativeResourcePath}) {
// TODO: implement loadGlb
throw UnimplementedError();
}
@override
Future<ThermionAsset> loadGlbFromBuffer(Uint8List data, {int numInstances = 1, bool keepData = false, int priority = 4, int layer = 0, bool loadResourcesAsync = false}) {
// TODO: implement loadGlbFromBuffer
throw UnimplementedError();
}
@override
Future<ThermionAsset> loadGltf(String path, String relativeResourcePath, {bool keepData = false}) {
// TODO: implement loadGltf // TODO: implement loadGltf
throw UnimplementedError(); throw UnimplementedError();
} }
@override
Future<ThermionAsset> loadGltfFromBuffer(Uint8List data, {int numInstances = 1, bool keepData = false, int priority = 4, int layer = 0, bool loadResourcesAsync = false, String? relativeResourcePath}) {
// TODO: implement loadGltfFromBuffer
throw UnimplementedError();
}
@override @override
Future loadIbl(String lightingPath, {double intensity = 30000}) { Future loadIbl(String lightingPath, {double intensity = 30000}) {
// TODO: implement loadIbl // TODO: implement loadIbl
@@ -136,6 +130,12 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError(); throw UnimplementedError();
} }
@override
Future removeFromScene(covariant ThermionAsset asset) {
// TODO: implement removeFromScene
throw UnimplementedError();
}
@override @override
Future removeGridOverlay() { Future removeGridOverlay() {
// TODO: implement removeGridOverlay // TODO: implement removeGridOverlay
@@ -287,5 +287,5 @@ class ThermionViewerStub extends ThermionViewer {
@override @override
// TODO: implement view // TODO: implement view
View get view => throw UnimplementedError(); View get view => throw UnimplementedError();
} }

View File

@@ -4,3 +4,4 @@ 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';
export 'src/filament/filament.dart';

View File

@@ -9,6 +9,8 @@ extern "C"
#endif #endif
EMSCRIPTEN_KEEPALIVE TAnimationManager *AnimationManager_create(TEngine *tEngine, TScene *tScene); EMSCRIPTEN_KEEPALIVE TAnimationManager *AnimationManager_create(TEngine *tEngine, TScene *tScene);
EMSCRIPTEN_KEEPALIVE void AnimationManager_update(TAnimationManager *tAnimationManager, uint64_t frameTimeInNanos);
EMSCRIPTEN_KEEPALIVE void AnimationManager_addAnimationComponent(TAnimationManager *tAnimationManager, EntityId entityId); EMSCRIPTEN_KEEPALIVE void AnimationManager_addAnimationComponent(TAnimationManager *tAnimationManager, EntityId entityId);

View File

@@ -32,8 +32,14 @@ enum TQualityLevel {
ULTRA ULTRA
}; };
enum TBlendMode {
OPAQUE,
TRANSLUCENT
};
// View // View
EMSCRIPTEN_KEEPALIVE TViewport View_getViewport(TView *view); EMSCRIPTEN_KEEPALIVE TViewport View_getViewport(TView *view);
EMSCRIPTEN_KEEPALIVE void View_setBlendMode(TView *view, TBlendMode blendMode);
EMSCRIPTEN_KEEPALIVE void View_setViewport(TView *view, uint32_t width, uint32_t height); EMSCRIPTEN_KEEPALIVE void View_setViewport(TView *view, uint32_t width, uint32_t height);
EMSCRIPTEN_KEEPALIVE void View_setRenderTarget(TView *view, TRenderTarget *renderTarget); EMSCRIPTEN_KEEPALIVE void View_setRenderTarget(TView *view, TRenderTarget *renderTarget);
EMSCRIPTEN_KEEPALIVE void View_setFrustumCullingEnabled(TView *view, bool enabled); EMSCRIPTEN_KEEPALIVE void View_setFrustumCullingEnabled(TView *view, bool enabled);

View File

@@ -14,12 +14,13 @@ namespace thermion
typedef int32_t EntityId; typedef int32_t EntityId;
typedef void (*FilamentRenderCallback)(void *const owner); typedef void (*FilamentRenderCallback)(void *const owner);
EMSCRIPTEN_KEEPALIVE void RenderLoop_create(); EMSCRIPTEN_KEEPALIVE void RenderThread_create();
EMSCRIPTEN_KEEPALIVE void RenderLoop_destroy(); EMSCRIPTEN_KEEPALIVE void RenderThread_destroy();
EMSCRIPTEN_KEEPALIVE void RenderLoop_requestAnimationFrame(void (*onComplete)()); EMSCRIPTEN_KEEPALIVE void RenderThread_requestAnimationFrame(void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void RenderThread_setRenderTicker(TRenderTicker *tRenderTicker);
EMSCRIPTEN_KEEPALIVE void RenderThread_addTask(void (*task)());
EMSCRIPTEN_KEEPALIVE void RenderTicker_renderRenderThread(TRenderTicker *tRenderTicker, uint64_t frameTimeInNanos, void (*onComplete)()); EMSCRIPTEN_KEEPALIVE void RenderTicker_renderRenderThread(TRenderTicker *tRenderTicker, uint64_t frameTimeInNanos, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void RenderLoop_addTask(void (*task)());
EMSCRIPTEN_KEEPALIVE void AnimationManager_createRenderThread(TEngine *tEngine, TScene *tScene, void (*onComplete)(TAnimationManager *)); EMSCRIPTEN_KEEPALIVE void AnimationManager_createRenderThread(TEngine *tEngine, TScene *tScene, void (*onComplete)(TAnimationManager *));
EMSCRIPTEN_KEEPALIVE void Engine_createRenderThread( EMSCRIPTEN_KEEPALIVE void Engine_createRenderThread(

View File

@@ -17,17 +17,17 @@ namespace thermion {
* This class handles frame rendering requests, viewer creation, and maintains * This class handles frame rendering requests, viewer creation, and maintains
* a task queue for rendering operations. * a task queue for rendering operations.
*/ */
class RenderLoop { class RenderThread {
public: public:
/** /**
* @brief Constructs a new RenderLoop and starts the render thread. * @brief Constructs a new RenderThread and starts the render thread.
*/ */
explicit RenderLoop(); explicit RenderThread();
/** /**
* @brief Destroys the RenderLoop and stops the render thread. * @brief Destroys the RenderThread and stops the render thread.
*/ */
~RenderLoop(); ~RenderThread();
/** /**
* @brief Requests a frame to be rendered. * @brief Requests a frame to be rendered.
@@ -74,7 +74,7 @@ private:
// Template implementation // Template implementation
template <class Rt> template <class Rt>
auto RenderLoop::add_task(std::packaged_task<Rt()>& pt) -> std::future<Rt> { auto RenderThread::add_task(std::packaged_task<Rt()>& pt) -> std::future<Rt> {
std::unique_lock<std::mutex> lock(_taskMutex); std::unique_lock<std::mutex> lock(_taskMutex);
auto ret = pt.get_future(); auto ret = pt.get_future();
_tasks.push_back([pt = std::make_shared<std::packaged_task<Rt()>>( _tasks.push_back([pt = std::make_shared<std::packaged_task<Rt()>>(

View File

@@ -69,12 +69,21 @@ namespace thermion
for (auto animationManager : mAnimationManagers) { for (auto animationManager : mAnimationManagers) {
animationManager->update(frameTimeInNanos); animationManager->update(frameTimeInNanos);
} TRACE("Updated AnimationManager");
}
#ifdef ENABLE_TRACING
int numRendered = 0;
TRACE("%d swapchains", mRenderable.size());
#endif
for (const auto& [swapChain, views] : mRenderable) for (const auto& [swapChain, views] : mRenderable)
{ {
if (!views.empty()) if (!views.empty())
{ {
TRACE("Rendering %d views", views.size());
bool beginFrame = mRenderer->beginFrame(swapChain, frameTimeInNanos); bool beginFrame = mRenderer->beginFrame(swapChain, frameTimeInNanos);
if (beginFrame) if (beginFrame)
{ {
@@ -84,7 +93,15 @@ namespace thermion
} }
} }
mRenderer->endFrame(); mRenderer->endFrame();
#ifdef ENABLE_TRACING
numRendered++;
} else {
TRACE("No views for swapchain");
} }
TRACE("%d swapchains rendered", numRendered);
#else
}
#endif
} }
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
_engine->execute(); _engine->execute();

View File

@@ -17,6 +17,11 @@ extern "C"
return reinterpret_cast<TAnimationManager *>(animationManager); return reinterpret_cast<TAnimationManager *>(animationManager);
} }
EMSCRIPTEN_KEEPALIVE void AnimationManager_update(TAnimationManager *tAnimationManager, uint64_t frameTimeInNanos) {
auto animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);
animationManager->update(frameTimeInNanos);
}
EMSCRIPTEN_KEEPALIVE void AnimationManager_addAnimationComponent(TAnimationManager *tAnimationManager, EntityId entityId) EMSCRIPTEN_KEEPALIVE void AnimationManager_addAnimationComponent(TAnimationManager *tAnimationManager, EntityId entityId)
{ {
auto animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager); auto animationManager = reinterpret_cast<AnimationManager *>(tAnimationManager);

View File

@@ -80,14 +80,38 @@ namespace thermion
EMSCRIPTEN_KEEPALIVE TSwapChain *Engine_createSwapChain(TEngine *tEngine, void *window, uint64_t flags) EMSCRIPTEN_KEEPALIVE TSwapChain *Engine_createSwapChain(TEngine *tEngine, void *window, uint64_t flags)
{ {
auto *engine = reinterpret_cast<Engine *>(tEngine); auto *engine = reinterpret_cast<Engine *>(tEngine);
#ifdef ENABLE_TRACING
if((flags & filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT) == filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT) {
TRACE("SWAP_CHAIN_CONFIG_TRANSPARENT");
}
if((flags & filament::backend::SWAP_CHAIN_CONFIG_READABLE) == filament::backend::SWAP_CHAIN_CONFIG_READABLE) {
TRACE("SWAP_CHAIN_CONFIG_READABLE");
}
if((flags & filament::backend::SWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER) == filament::backend::SWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER) {
TRACE("SWAP_CHAIN_CONFIG_READABLE");
}
#endif
auto *swapChain = engine->createSwapChain(window, flags); auto *swapChain = engine->createSwapChain(window, flags);
return reinterpret_cast<TSwapChain *>(swapChain); return reinterpret_cast<TSwapChain *>(swapChain);
} }
EMSCRIPTEN_KEEPALIVE TSwapChain *Engine_createHeadlessSwapChain(TEngine *tEngine, uint32_t width, uint32_t height, uint64_t flags) EMSCRIPTEN_KEEPALIVE TSwapChain *Engine_createHeadlessSwapChain(TEngine *tEngine, uint32_t width, uint32_t height, uint64_t flags)
{ {
TRACE("Creating headless swapchain %dx%d, flags %flags", width, height, flags);
auto *engine = reinterpret_cast<Engine *>(tEngine); auto *engine = reinterpret_cast<Engine *>(tEngine);
auto *swapChain = engine->createSwapChain(width, height, flags); auto *swapChain = engine->createSwapChain(width, height, flags);
#ifdef ENABLE_TRACING
if((flags & filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT) == filament::backend::SWAP_CHAIN_CONFIG_TRANSPARENT) {
TRACE("SWAP_CHAIN_CONFIG_TRANSPARENT");
}
if((flags & filament::backend::SWAP_CHAIN_CONFIG_READABLE) == filament::backend::SWAP_CHAIN_CONFIG_READABLE) {
TRACE("SWAP_CHAIN_CONFIG_READABLE");
}
if((flags & filament::backend::SWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER) == filament::backend::SWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER) {
TRACE("SWAP_CHAIN_CONFIG_READABLE");
}
#endif
return reinterpret_cast<TSwapChain *>(swapChain); return reinterpret_cast<TSwapChain *>(swapChain);
} }
@@ -101,6 +125,9 @@ namespace thermion
{ {
auto *engine = reinterpret_cast<Engine *>(tEngine); auto *engine = reinterpret_cast<Engine *>(tEngine);
auto *view = engine->createView(); auto *view = engine->createView();
view->setShadowingEnabled(false);
view->setAmbientOcclusionOptions({.enabled = false});
view->setDynamicResolutionOptions({.enabled = false});
return reinterpret_cast<TView *>(view); return reinterpret_cast<TView *>(view);
} }

View File

@@ -14,10 +14,14 @@ extern "C"
return reinterpret_cast<TNameComponentManager *>(ncm); return reinterpret_cast<TNameComponentManager *>(ncm);
} }
EMSCRIPTEN_KEEPALIVE const char *NameComponentManager_getName(TNameComponentManager *tNameComponentManager, EntityId entity) EMSCRIPTEN_KEEPALIVE const char *NameComponentManager_getName(TNameComponentManager *tNameComponentManager, EntityId entityId)
{ {
auto ncm = reinterpret_cast<utils::NameComponentManager *>(tNameComponentManager); auto ncm = reinterpret_cast<utils::NameComponentManager *>(tNameComponentManager);
auto instance = ncm->getInstance(utils::Entity::import(entity)); auto entity = utils::Entity::import(entityId);
if(!ncm->hasComponent(entity)) {
return nullptr;
}
auto instance = ncm->getInstance(entity);
return ncm->getName(instance); return ncm->getName(instance);
} }

View File

@@ -16,6 +16,11 @@ using namespace filament;
#endif #endif
EMSCRIPTEN_KEEPALIVE void View_setBlendMode(TView *tView, TBlendMode tBlendMode) {
auto view = reinterpret_cast<View *>(tView);
view->setBlendMode(static_cast<filament::View::BlendMode>(tBlendMode));
}
EMSCRIPTEN_KEEPALIVE TViewport View_getViewport(TView *tView) EMSCRIPTEN_KEEPALIVE TViewport View_getViewport(TView *tView)
{ {
auto view = reinterpret_cast<View *>(tView); auto view = reinterpret_cast<View *>(tView);
@@ -58,6 +63,8 @@ using namespace filament;
{ {
auto view = reinterpret_cast<View *>(tView); auto view = reinterpret_cast<View *>(tView);
view->setPostProcessingEnabled(enabled); view->setPostProcessingEnabled(enabled);
TRACE("Set postprocessing enabled : %d", enabled);
} }
EMSCRIPTEN_KEEPALIVE void View_setShadowsEnabled(TView *tView, bool enabled) EMSCRIPTEN_KEEPALIVE void View_setShadowsEnabled(TView *tView, bool enabled)
@@ -88,6 +95,7 @@ using namespace filament;
decltype(view->getBloomOptions()) opts; decltype(view->getBloomOptions()) opts;
opts.enabled = enabled; opts.enabled = enabled;
opts.strength = strength; opts.strength = strength;
TRACE("Setting bloom options {.enabled = %d, strength = %f}", enabled, strength);
view->setBloomOptions(opts); view->setBloomOptions(opts);
#endif #endif
} }
@@ -101,19 +109,19 @@ using namespace filament;
switch (tToneMapping) switch (tToneMapping)
{ {
case TToneMapping::ACES: case TToneMapping::ACES:
Log("Setting tone mapping to ACES"); TRACE("Setting tone mapping to ACES");
tm = new ACESToneMapper(); tm = new ACESToneMapper();
break; break;
case TToneMapping::LINEAR: case TToneMapping::LINEAR:
Log("Setting tone mapping to Linear"); TRACE("Setting tone mapping to Linear");
tm = new LinearToneMapper(); tm = new LinearToneMapper();
break; break;
case TToneMapping::FILMIC: case TToneMapping::FILMIC:
Log("Setting tone mapping to Filmic"); TRACE("Setting tone mapping to Filmic");
tm = new FilmicToneMapper(); tm = new FilmicToneMapper();
break; break;
default: default:
Log("ERROR: Unsupported tone mapping"); TRACE("ERROR: Unsupported tone mapping");
return; return;
} }
auto newColorGrading = ColorGrading::Builder().toneMapper(tm).build(*engine); auto newColorGrading = ColorGrading::Builder().toneMapper(tm).build(*engine);
@@ -200,6 +208,21 @@ using namespace filament;
auto view = reinterpret_cast<View *>(tView); auto view = reinterpret_cast<View *>(tView);
RenderQuality rq; RenderQuality rq;
rq.hdrColorBuffer = (filament::QualityLevel)qualityLevel; rq.hdrColorBuffer = (filament::QualityLevel)qualityLevel;
switch(rq.hdrColorBuffer) {
case filament::QualityLevel::LOW:
TRACE("Render Quality: LOW");
break;
case filament::QualityLevel::MEDIUM:
TRACE("Render Quality: MEDIUM");
break;
case filament::QualityLevel::HIGH:
TRACE("Render Quality: HIGH");
break;
case filament::QualityLevel::ULTRA:
TRACE("Render Quality: ULTRA");
break;
}
view->setRenderQuality(rq); view->setRenderQuality(rq);
} }

View File

@@ -19,7 +19,7 @@
#include "c_api/TView.h" #include "c_api/TView.h"
#include "c_api/ThermionDartRenderThreadApi.h" #include "c_api/ThermionDartRenderThreadApi.h"
#include "rendering/RenderLoop.hpp" #include "rendering/RenderThread.hpp"
#include "Log.hpp" #include "Log.hpp"
using namespace thermion; using namespace thermion;
@@ -29,36 +29,41 @@ using namespace std::chrono_literals;
extern "C" extern "C"
{ {
static std::unique_ptr<RenderLoop> _rl; static std::unique_ptr<RenderThread> _renderThread;
EMSCRIPTEN_KEEPALIVE void RenderLoop_create() { EMSCRIPTEN_KEEPALIVE void RenderThread_create() {
TRACE("RenderLoop_create"); TRACE("RenderThread_create");
if (_rl) if (_renderThread)
{ {
Log("WARNING - you are attempting to create a RenderLoop when the previous one has not been disposed."); Log("WARNING - you are attempting to create a RenderThread when the previous one has not been disposed.");
} }
_rl = std::make_unique<RenderLoop>(); _renderThread = std::make_unique<RenderThread>();
} }
EMSCRIPTEN_KEEPALIVE void RenderLoop_destroy() { EMSCRIPTEN_KEEPALIVE void RenderThread_destroy() {
TRACE("RenderLoop_destroy"); TRACE("RenderThread_destroy");
if (_rl) if (_renderThread)
{ {
_rl = nullptr; _renderThread = nullptr;
} }
} }
EMSCRIPTEN_KEEPALIVE void RenderLoop_addTask(void (*task)()) { EMSCRIPTEN_KEEPALIVE void RenderThread_addTask(void (*task)()) {
std::packaged_task<void()> lambda( std::packaged_task<void()> lambda(
[=]() mutable [=]() mutable
{ {
task(); task();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void RenderLoop_requestAnimationFrame(void (*onComplete)()) { EMSCRIPTEN_KEEPALIVE void RenderThread_setRenderTicker(TRenderTicker *tRenderTicker) {
_rl->requestFrame(onComplete); auto *renderTicker = reinterpret_cast<RenderTicker *>(tRenderTicker);
_renderThread->setRenderTicker(renderTicker);
}
EMSCRIPTEN_KEEPALIVE void RenderThread_requestAnimationFrame(void (*onComplete)()) {
_renderThread->requestFrame(onComplete);
} }
@@ -69,7 +74,7 @@ extern "C"
RenderTicker_render(tRenderTicker, frameTimeInNanos); RenderTicker_render(tRenderTicker, frameTimeInNanos);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Engine_createRenderThread( EMSCRIPTEN_KEEPALIVE void Engine_createRenderThread(
@@ -85,7 +90,7 @@ extern "C"
auto engine = Engine_create(backend, platform, sharedContext, stereoscopicEyeCount, disableHandleUseAfterFreeCheck); auto engine = Engine_create(backend, platform, sharedContext, stereoscopicEyeCount, disableHandleUseAfterFreeCheck);
onComplete(engine); onComplete(engine);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Engine_createRendererRenderThread(TEngine *tEngine, void (*onComplete)(TRenderer *)) { EMSCRIPTEN_KEEPALIVE void Engine_createRendererRenderThread(TEngine *tEngine, void (*onComplete)(TRenderer *)) {
@@ -95,7 +100,7 @@ extern "C"
auto renderer = Engine_createRenderer(tEngine); auto renderer = Engine_createRenderer(tEngine);
onComplete(renderer); onComplete(renderer);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Engine_createSwapChainRenderThread(TEngine *tEngine, void *window, uint64_t flags, void (*onComplete)(TSwapChain *)) { EMSCRIPTEN_KEEPALIVE void Engine_createSwapChainRenderThread(TEngine *tEngine, void *window, uint64_t flags, void (*onComplete)(TSwapChain *)) {
@@ -105,7 +110,7 @@ extern "C"
auto swapChain = Engine_createSwapChain(tEngine, window, flags); auto swapChain = Engine_createSwapChain(tEngine, window, flags);
onComplete(swapChain); onComplete(swapChain);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Engine_createHeadlessSwapChainRenderThread(TEngine *tEngine, uint32_t width, uint32_t height, uint64_t flags, void (*onComplete)(TSwapChain *)) { EMSCRIPTEN_KEEPALIVE void Engine_createHeadlessSwapChainRenderThread(TEngine *tEngine, uint32_t width, uint32_t height, uint64_t flags, void (*onComplete)(TSwapChain *)) {
@@ -115,7 +120,7 @@ extern "C"
auto swapChain = Engine_createHeadlessSwapChain(tEngine, width, height, flags); auto swapChain = Engine_createHeadlessSwapChain(tEngine, width, height, flags);
onComplete(swapChain); onComplete(swapChain);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Engine_destroySwapChainRenderThread(TEngine *tEngine, TSwapChain *tSwapChain, void (*onComplete)()) { EMSCRIPTEN_KEEPALIVE void Engine_destroySwapChainRenderThread(TEngine *tEngine, TSwapChain *tSwapChain, void (*onComplete)()) {
@@ -125,7 +130,7 @@ extern "C"
Engine_destroySwapChain(tEngine, tSwapChain); Engine_destroySwapChain(tEngine, tSwapChain);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Engine_createCameraRenderThread(TEngine* tEngine, void (*onComplete)(TCamera *)) { EMSCRIPTEN_KEEPALIVE void Engine_createCameraRenderThread(TEngine* tEngine, void (*onComplete)(TCamera *)) {
@@ -135,7 +140,7 @@ extern "C"
auto camera = Engine_createCamera(tEngine); auto camera = Engine_createCamera(tEngine);
onComplete(camera); onComplete(camera);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Engine_createViewRenderThread(TEngine *tEngine, void (*onComplete)(TView *)) { EMSCRIPTEN_KEEPALIVE void Engine_createViewRenderThread(TEngine *tEngine, void (*onComplete)(TView *)) {
@@ -145,7 +150,7 @@ extern "C"
auto * view = Engine_createView(tEngine); auto * view = Engine_createView(tEngine);
onComplete(view); onComplete(view);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Engine_destroyTextureRenderThread(TEngine *engine, TTexture *tTexture, void (*onComplete)()) EMSCRIPTEN_KEEPALIVE void Engine_destroyTextureRenderThread(TEngine *engine, TTexture *tTexture, void (*onComplete)())
@@ -156,7 +161,7 @@ extern "C"
Engine_destroyTexture(engine, tTexture); Engine_destroyTexture(engine, tTexture);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Engine_destroySkyboxRenderThread(TEngine *tEngine, TSkybox *tSkybox, void (*onComplete)()) { EMSCRIPTEN_KEEPALIVE void Engine_destroySkyboxRenderThread(TEngine *tEngine, TSkybox *tSkybox, void (*onComplete)()) {
@@ -166,7 +171,7 @@ extern "C"
Engine_destroySkybox(tEngine, tSkybox); Engine_destroySkybox(tEngine, tSkybox);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Engine_destroyIndirectLightRenderThread(TEngine *tEngine, TIndirectLight *tIndirectLight, void (*onComplete)()) { EMSCRIPTEN_KEEPALIVE void Engine_destroyIndirectLightRenderThread(TEngine *tEngine, TIndirectLight *tIndirectLight, void (*onComplete)()) {
@@ -176,7 +181,7 @@ extern "C"
Engine_destroyIndirectLight(tEngine, tIndirectLight); Engine_destroyIndirectLight(tEngine, tIndirectLight);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Engine_buildMaterialRenderThread(TEngine *tEngine, const uint8_t *materialData, size_t length, void (*onComplete)(TMaterial *)) EMSCRIPTEN_KEEPALIVE void Engine_buildMaterialRenderThread(TEngine *tEngine, const uint8_t *materialData, size_t length, void (*onComplete)(TMaterial *))
@@ -187,7 +192,7 @@ extern "C"
auto material = Engine_buildMaterial(tEngine, materialData, length); auto material = Engine_buildMaterial(tEngine, materialData, length);
onComplete(material); onComplete(material);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
@@ -199,7 +204,17 @@ extern "C"
Engine_destroyMaterial(tEngine, tMaterial); Engine_destroyMaterial(tEngine, tMaterial);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Engine_destroyMaterialInstanceRenderThread(TEngine *tEngine, TMaterialInstance *tMaterialInstance, void (*onComplete)()) {
std::packaged_task<void()> lambda(
[=]() mutable
{
Engine_destroyMaterialInstance(tEngine, tMaterialInstance);
onComplete();
});
auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Engine_createFenceRenderThread(TEngine *tEngine, void (*onComplete)(TFence*)) { EMSCRIPTEN_KEEPALIVE void Engine_createFenceRenderThread(TEngine *tEngine, void (*onComplete)(TFence*)) {
@@ -209,7 +224,7 @@ extern "C"
auto *fence = Engine_createFence(tEngine); auto *fence = Engine_createFence(tEngine);
onComplete(fence); onComplete(fence);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Engine_destroyFenceRenderThread(TEngine *tEngine, TFence *tFence, void (*onComplete)()) { EMSCRIPTEN_KEEPALIVE void Engine_destroyFenceRenderThread(TEngine *tEngine, TFence *tFence, void (*onComplete)()) {
@@ -219,7 +234,7 @@ extern "C"
Engine_destroyFence(tEngine, tFence); Engine_destroyFence(tEngine, tFence);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Engine_flushAndWaitRenderThead(TEngine *tEngine, void (*onComplete)()) { EMSCRIPTEN_KEEPALIVE void Engine_flushAndWaitRenderThead(TEngine *tEngine, void (*onComplete)()) {
@@ -229,7 +244,7 @@ extern "C"
Engine_flushAndWait(tEngine); Engine_flushAndWait(tEngine);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Engine_buildSkyboxRenderThread(TEngine *tEngine, uint8_t *skyboxData, size_t length, void (*onComplete)(TSkybox *), void (*onTextureUploadComplete)()) { EMSCRIPTEN_KEEPALIVE void Engine_buildSkyboxRenderThread(TEngine *tEngine, uint8_t *skyboxData, size_t length, void (*onComplete)(TSkybox *), void (*onTextureUploadComplete)()) {
@@ -239,7 +254,7 @@ extern "C"
auto *skybox = Engine_buildSkybox(tEngine, skyboxData, length, onTextureUploadComplete); auto *skybox = Engine_buildSkybox(tEngine, skyboxData, length, onTextureUploadComplete);
onComplete(skybox); onComplete(skybox);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Engine_buildIndirectLightRenderThread(TEngine *tEngine, uint8_t *iblData, size_t length, float intensity, void (*onComplete)(TIndirectLight *), void (*onTextureUploadComplete)()) { EMSCRIPTEN_KEEPALIVE void Engine_buildIndirectLightRenderThread(TEngine *tEngine, uint8_t *iblData, size_t length, float intensity, void (*onComplete)(TIndirectLight *), void (*onTextureUploadComplete)()) {
@@ -249,7 +264,7 @@ extern "C"
auto *indirectLight = Engine_buildIndirectLight(tEngine, iblData, length, intensity, onTextureUploadComplete); auto *indirectLight = Engine_buildIndirectLight(tEngine, iblData, length, intensity, onTextureUploadComplete);
onComplete(indirectLight); onComplete(indirectLight);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Renderer_beginFrameRenderThread(TRenderer *tRenderer, TSwapChain *tSwapChain, uint64_t frameTimeInNanos, void (*onComplete)(bool)) { EMSCRIPTEN_KEEPALIVE void Renderer_beginFrameRenderThread(TRenderer *tRenderer, TSwapChain *tSwapChain, uint64_t frameTimeInNanos, void (*onComplete)(bool)) {
@@ -259,7 +274,7 @@ extern "C"
auto result = Renderer_beginFrame(tRenderer, tSwapChain, frameTimeInNanos); auto result = Renderer_beginFrame(tRenderer, tSwapChain, frameTimeInNanos);
onComplete(result); onComplete(result);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Renderer_endFrameRenderThread(TRenderer *tRenderer, void (*onComplete)()) { EMSCRIPTEN_KEEPALIVE void Renderer_endFrameRenderThread(TRenderer *tRenderer, void (*onComplete)()) {
std::packaged_task<void()> lambda( std::packaged_task<void()> lambda(
@@ -268,7 +283,7 @@ extern "C"
Renderer_endFrame(tRenderer); Renderer_endFrame(tRenderer);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Renderer_renderRenderThread(TRenderer *tRenderer, TView *tView, void (*onComplete)()) { EMSCRIPTEN_KEEPALIVE void Renderer_renderRenderThread(TRenderer *tRenderer, TView *tView, void (*onComplete)()) {
@@ -278,7 +293,7 @@ extern "C"
Renderer_render(tRenderer, tView); Renderer_render(tRenderer, tView);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Renderer_renderStandaloneViewRenderThread(TRenderer *tRenderer, TView *tView, void (*onComplete)()) { EMSCRIPTEN_KEEPALIVE void Renderer_renderStandaloneViewRenderThread(TRenderer *tRenderer, TView *tView, void (*onComplete)()) {
@@ -288,7 +303,7 @@ extern "C"
Renderer_renderStandaloneView(tRenderer, tView); Renderer_renderStandaloneView(tRenderer, tView);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Renderer_setClearOptionsRenderThread( EMSCRIPTEN_KEEPALIVE void Renderer_setClearOptionsRenderThread(
@@ -306,7 +321,7 @@ extern "C"
Renderer_setClearOptions(tRenderer, clearR, clearG, clearB, clearA, clearStencil, clear, discard); Renderer_setClearOptions(tRenderer, clearR, clearG, clearB, clearA, clearStencil, clear, discard);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Renderer_readPixelsRenderThread( EMSCRIPTEN_KEEPALIVE void Renderer_readPixelsRenderThread(
@@ -323,7 +338,7 @@ extern "C"
Renderer_readPixels(tRenderer, tView, tRenderTarget, tPixelBufferFormat, tPixelDataType, out); Renderer_readPixels(tRenderer, tView, tRenderTarget, tPixelBufferFormat, tPixelDataType, out);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Material_createImageMaterialRenderThread(TEngine *tEngine, void (*onComplete)(TMaterial *)) { EMSCRIPTEN_KEEPALIVE void Material_createImageMaterialRenderThread(TEngine *tEngine, void (*onComplete)(TMaterial *)) {
@@ -333,7 +348,7 @@ extern "C"
auto *instance = Material_createImageMaterial(tEngine); auto *instance = Material_createImageMaterial(tEngine);
onComplete(instance); onComplete(instance);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Material_createInstanceRenderThread(TMaterial *tMaterial, void (*onComplete)(TMaterialInstance *)) EMSCRIPTEN_KEEPALIVE void Material_createInstanceRenderThread(TMaterial *tMaterial, void (*onComplete)(TMaterialInstance *))
@@ -344,7 +359,7 @@ extern "C"
auto *instance = Material_createInstance(tMaterial); auto *instance = Material_createInstance(tMaterial);
onComplete(instance); onComplete(instance);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void SceneAsset_destroyRenderThread(TSceneAsset *tSceneAsset, void (*onComplete)()) { EMSCRIPTEN_KEEPALIVE void SceneAsset_destroyRenderThread(TSceneAsset *tSceneAsset, void (*onComplete)()) {
@@ -354,7 +369,7 @@ extern "C"
SceneAsset_destroy(tSceneAsset); SceneAsset_destroy(tSceneAsset);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void SceneAsset_loadGlbRenderThread( EMSCRIPTEN_KEEPALIVE void SceneAsset_loadGlbRenderThread(
@@ -372,7 +387,7 @@ extern "C"
auto sceneAsset = SceneAsset_loadGlb(tEngine, tAssetLoader, tNameComponentManager, data, length, numInstances); auto sceneAsset = SceneAsset_loadGlb(tEngine, tAssetLoader, tNameComponentManager, data, length, numInstances);
callback(sceneAsset); callback(sceneAsset);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void SceneAsset_createGeometryRenderThread( EMSCRIPTEN_KEEPALIVE void SceneAsset_createGeometryRenderThread(
@@ -396,7 +411,7 @@ extern "C"
auto sceneAsset = SceneAsset_createGeometry(tEngine, vertices, numVertices, normals, numNormals, uvs, numUvs, indices, numIndices, tPrimitiveType, materialInstances, materialInstanceCount); auto sceneAsset = SceneAsset_createGeometry(tEngine, vertices, numVertices, normals, numNormals, uvs, numUvs, indices, numIndices, tPrimitiveType, materialInstances, materialInstanceCount);
callback(sceneAsset); callback(sceneAsset);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void SceneAsset_createInstanceRenderThread( EMSCRIPTEN_KEEPALIVE void SceneAsset_createInstanceRenderThread(
@@ -410,7 +425,7 @@ extern "C"
auto instanceAsset = SceneAsset_createInstance(asset, tMaterialInstances, materialInstanceCount); auto instanceAsset = SceneAsset_createInstance(asset, tMaterialInstances, materialInstanceCount);
callback(instanceAsset); callback(instanceAsset);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void MaterialProvider_createMaterialInstanceRenderThread(TMaterialProvider *tMaterialProvider, TMaterialKey *tKey, void (*callback)(TMaterialInstance *)) EMSCRIPTEN_KEEPALIVE void MaterialProvider_createMaterialInstanceRenderThread(TMaterialProvider *tMaterialProvider, TMaterialKey *tKey, void (*callback)(TMaterialInstance *))
@@ -421,7 +436,7 @@ extern "C"
auto materialInstance = MaterialProvider_createMaterialInstance(tMaterialProvider, tKey); auto materialInstance = MaterialProvider_createMaterialInstance(tMaterialProvider, tKey);
callback(materialInstance); callback(materialInstance);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void View_setToneMappingRenderThread(TView *tView, TEngine *tEngine, TToneMapping toneMapping, void (*callback)()) EMSCRIPTEN_KEEPALIVE void View_setToneMappingRenderThread(TView *tView, TEngine *tEngine, TToneMapping toneMapping, void (*callback)())
@@ -432,7 +447,7 @@ extern "C"
View_setToneMapping(tView, tEngine, toneMapping); View_setToneMapping(tView, tEngine, toneMapping);
callback(); callback();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void View_setBloomRenderThread(TView *tView, bool enabled, double strength, void (*callback)()) EMSCRIPTEN_KEEPALIVE void View_setBloomRenderThread(TView *tView, bool enabled, double strength, void (*callback)())
@@ -443,7 +458,7 @@ extern "C"
View_setBloom(tView, enabled, strength); View_setBloom(tView, enabled, strength);
callback(); callback();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void View_setCameraRenderThread(TView *tView, TCamera *tCamera, void (*callback)()) EMSCRIPTEN_KEEPALIVE void View_setCameraRenderThread(TView *tView, TCamera *tCamera, void (*callback)())
@@ -454,7 +469,7 @@ extern "C"
View_setCamera(tView, tCamera); View_setCamera(tView, tCamera);
callback(); callback();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void AnimationManager_createRenderThread(TEngine *tEngine, TScene *tScene, void (*onComplete)(TAnimationManager *)) { EMSCRIPTEN_KEEPALIVE void AnimationManager_createRenderThread(TEngine *tEngine, TScene *tScene, void (*onComplete)(TAnimationManager *)) {
@@ -464,7 +479,7 @@ extern "C"
auto *animationManager = AnimationManager_create(tEngine, tScene); auto *animationManager = AnimationManager_create(tEngine, tScene);
onComplete(animationManager); onComplete(animationManager);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void AnimationManager_updateBoneMatricesRenderThread( EMSCRIPTEN_KEEPALIVE void AnimationManager_updateBoneMatricesRenderThread(
@@ -478,7 +493,7 @@ extern "C"
bool result = AnimationManager_updateBoneMatrices(tAnimationManager, sceneAsset); bool result = AnimationManager_updateBoneMatrices(tAnimationManager, sceneAsset);
callback(result); callback(result);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void AnimationManager_setMorphTargetWeightsRenderThread( EMSCRIPTEN_KEEPALIVE void AnimationManager_setMorphTargetWeightsRenderThread(
@@ -494,7 +509,7 @@ extern "C"
bool result = AnimationManager_setMorphTargetWeights(tAnimationManager, entityId, morphData, numWeights); bool result = AnimationManager_setMorphTargetWeights(tAnimationManager, entityId, morphData, numWeights);
callback(result); callback(result);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
// Add these implementations to your ThermionDartRenderThreadApi.cpp file // Add these implementations to your ThermionDartRenderThreadApi.cpp file
@@ -508,7 +523,7 @@ extern "C"
auto image = Image_createEmpty(width, height, channel); auto image = Image_createEmpty(width, height, channel);
onComplete(image); onComplete(image);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Image_decodeRenderThread(uint8_t *data, size_t length, const char *name, void (*onComplete)(TLinearImage *)) EMSCRIPTEN_KEEPALIVE void Image_decodeRenderThread(uint8_t *data, size_t length, const char *name, void (*onComplete)(TLinearImage *))
@@ -519,7 +534,7 @@ extern "C"
auto image = Image_decode(data, length, name); auto image = Image_decode(data, length, name);
onComplete(image); onComplete(image);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Image_getBytesRenderThread(TLinearImage *tLinearImage, void (*onComplete)(float *)) EMSCRIPTEN_KEEPALIVE void Image_getBytesRenderThread(TLinearImage *tLinearImage, void (*onComplete)(float *))
@@ -530,7 +545,7 @@ extern "C"
auto bytes = Image_getBytes(tLinearImage); auto bytes = Image_getBytes(tLinearImage);
onComplete(bytes); onComplete(bytes);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Image_destroyRenderThread(TLinearImage *tLinearImage, void (*onComplete)()) EMSCRIPTEN_KEEPALIVE void Image_destroyRenderThread(TLinearImage *tLinearImage, void (*onComplete)())
@@ -541,7 +556,7 @@ extern "C"
Image_destroy(tLinearImage); Image_destroy(tLinearImage);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Image_getWidthRenderThread(TLinearImage *tLinearImage, void (*onComplete)(uint32_t)) EMSCRIPTEN_KEEPALIVE void Image_getWidthRenderThread(TLinearImage *tLinearImage, void (*onComplete)(uint32_t))
@@ -552,7 +567,7 @@ extern "C"
auto width = Image_getWidth(tLinearImage); auto width = Image_getWidth(tLinearImage);
onComplete(width); onComplete(width);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Image_getHeightRenderThread(TLinearImage *tLinearImage, void (*onComplete)(uint32_t)) EMSCRIPTEN_KEEPALIVE void Image_getHeightRenderThread(TLinearImage *tLinearImage, void (*onComplete)(uint32_t))
@@ -563,7 +578,7 @@ extern "C"
auto height = Image_getHeight(tLinearImage); auto height = Image_getHeight(tLinearImage);
onComplete(height); onComplete(height);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Image_getChannelsRenderThread(TLinearImage *tLinearImage, void (*onComplete)(uint32_t)) EMSCRIPTEN_KEEPALIVE void Image_getChannelsRenderThread(TLinearImage *tLinearImage, void (*onComplete)(uint32_t))
@@ -574,7 +589,7 @@ extern "C"
auto channels = Image_getChannels(tLinearImage); auto channels = Image_getChannels(tLinearImage);
onComplete(channels); onComplete(channels);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Texture_buildRenderThread( EMSCRIPTEN_KEEPALIVE void Texture_buildRenderThread(
@@ -593,7 +608,7 @@ extern "C"
auto *texture = Texture_build(tEngine, width, height, depth, levels, tUsage, import, sampler, format); auto *texture = Texture_build(tEngine, width, height, depth, levels, tUsage, import, sampler, format);
onComplete(texture); onComplete(texture);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
// Texture methods // Texture methods
@@ -607,7 +622,7 @@ extern "C"
bool result = Texture_loadImage(tEngine, tTexture, tImage, bufferFormat, pixelDataType); bool result = Texture_loadImage(tEngine, tTexture, tImage, bufferFormat, pixelDataType);
onComplete(result); onComplete(result);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Texture_setImageRenderThread( EMSCRIPTEN_KEEPALIVE void Texture_setImageRenderThread(
@@ -630,7 +645,7 @@ extern "C"
bufferFormat, pixelDataType); bufferFormat, pixelDataType);
onComplete(result); onComplete(result);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Texture_setImageWithDepthRenderThread( EMSCRIPTEN_KEEPALIVE void Texture_setImageWithDepthRenderThread(
@@ -671,7 +686,7 @@ extern "C"
); );
onComplete(result); onComplete(result);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void RenderTarget_getColorTextureRenderThread(TRenderTarget *tRenderTarget, void (*onComplete)(TTexture *)) EMSCRIPTEN_KEEPALIVE void RenderTarget_getColorTextureRenderThread(TRenderTarget *tRenderTarget, void (*onComplete)(TTexture *))
@@ -682,7 +697,7 @@ extern "C"
auto texture = RenderTarget_getColorTexture(tRenderTarget); auto texture = RenderTarget_getColorTexture(tRenderTarget);
onComplete(texture); onComplete(texture);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void RenderTarget_createRenderThread( EMSCRIPTEN_KEEPALIVE void RenderTarget_createRenderThread(
@@ -702,7 +717,7 @@ extern "C"
auto texture = RenderTarget_create(tEngine, width, height, tColor, tDepth); auto texture = RenderTarget_create(tEngine, width, height, tColor, tDepth);
onComplete(texture); onComplete(texture);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
@@ -716,7 +731,7 @@ extern "C"
auto sampler = TextureSampler_create(); auto sampler = TextureSampler_create();
onComplete(sampler); onComplete(sampler);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void TextureSampler_createWithFilteringRenderThread( EMSCRIPTEN_KEEPALIVE void TextureSampler_createWithFilteringRenderThread(
@@ -733,7 +748,7 @@ extern "C"
auto sampler = TextureSampler_createWithFiltering(minFilter, magFilter, wrapS, wrapT, wrapR); auto sampler = TextureSampler_createWithFiltering(minFilter, magFilter, wrapS, wrapT, wrapR);
onComplete(sampler); onComplete(sampler);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void TextureSampler_createWithComparisonRenderThread( EMSCRIPTEN_KEEPALIVE void TextureSampler_createWithComparisonRenderThread(
@@ -747,7 +762,7 @@ extern "C"
auto sampler = TextureSampler_createWithComparison(compareMode, compareFunc); auto sampler = TextureSampler_createWithComparison(compareMode, compareFunc);
onComplete(sampler); onComplete(sampler);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void TextureSampler_setMinFilterRenderThread( EMSCRIPTEN_KEEPALIVE void TextureSampler_setMinFilterRenderThread(
@@ -761,7 +776,7 @@ extern "C"
TextureSampler_setMinFilter(sampler, filter); TextureSampler_setMinFilter(sampler, filter);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void TextureSampler_setMagFilterRenderThread( EMSCRIPTEN_KEEPALIVE void TextureSampler_setMagFilterRenderThread(
@@ -775,7 +790,7 @@ extern "C"
TextureSampler_setMagFilter(sampler, filter); TextureSampler_setMagFilter(sampler, filter);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void TextureSampler_setWrapModeSRenderThread( EMSCRIPTEN_KEEPALIVE void TextureSampler_setWrapModeSRenderThread(
@@ -789,7 +804,7 @@ extern "C"
TextureSampler_setWrapModeS(sampler, mode); TextureSampler_setWrapModeS(sampler, mode);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void TextureSampler_setWrapModeTRenderThread( EMSCRIPTEN_KEEPALIVE void TextureSampler_setWrapModeTRenderThread(
@@ -803,7 +818,7 @@ extern "C"
TextureSampler_setWrapModeT(sampler, mode); TextureSampler_setWrapModeT(sampler, mode);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void TextureSampler_setWrapModeRRenderThread( EMSCRIPTEN_KEEPALIVE void TextureSampler_setWrapModeRRenderThread(
@@ -817,7 +832,7 @@ extern "C"
TextureSampler_setWrapModeR(sampler, mode); TextureSampler_setWrapModeR(sampler, mode);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void TextureSampler_setAnisotropyRenderThread( EMSCRIPTEN_KEEPALIVE void TextureSampler_setAnisotropyRenderThread(
@@ -831,7 +846,7 @@ extern "C"
TextureSampler_setAnisotropy(sampler, anisotropy); TextureSampler_setAnisotropy(sampler, anisotropy);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void TextureSampler_setCompareModeRenderThread( EMSCRIPTEN_KEEPALIVE void TextureSampler_setCompareModeRenderThread(
@@ -846,7 +861,7 @@ extern "C"
TextureSampler_setCompareMode(sampler, mode, func); TextureSampler_setCompareMode(sampler, mode, func);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void TextureSampler_destroyRenderThread( EMSCRIPTEN_KEEPALIVE void TextureSampler_destroyRenderThread(
@@ -859,7 +874,7 @@ extern "C"
TextureSampler_destroy(sampler); TextureSampler_destroy(sampler);
onComplete(); onComplete();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void GltfAssetLoader_createRenderThread(TEngine *tEngine, TMaterialProvider *tMaterialProvider, void (*callback)(TGltfAssetLoader *)) { EMSCRIPTEN_KEEPALIVE void GltfAssetLoader_createRenderThread(TEngine *tEngine, TMaterialProvider *tMaterialProvider, void (*callback)(TGltfAssetLoader *)) {
@@ -869,7 +884,7 @@ extern "C"
auto loader = GltfAssetLoader_create(tEngine, tMaterialProvider); auto loader = GltfAssetLoader_create(tEngine, tMaterialProvider);
callback(loader); callback(loader);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void GltfResourceLoader_createRenderThread(TEngine *tEngine, const char* relativeResourcePath, void (*callback)(TGltfResourceLoader *)) { EMSCRIPTEN_KEEPALIVE void GltfResourceLoader_createRenderThread(TEngine *tEngine, const char* relativeResourcePath, void (*callback)(TGltfResourceLoader *)) {
@@ -879,7 +894,7 @@ extern "C"
auto loader = GltfResourceLoader_create(tEngine, relativeResourcePath); auto loader = GltfResourceLoader_create(tEngine, relativeResourcePath);
callback(loader); callback(loader);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void GltfResourceLoader_destroyRenderThread(TEngine *tEngine, TGltfResourceLoader *tResourceLoader, void (*callback)()) { EMSCRIPTEN_KEEPALIVE void GltfResourceLoader_destroyRenderThread(TEngine *tEngine, TGltfResourceLoader *tResourceLoader, void (*callback)()) {
@@ -889,7 +904,7 @@ extern "C"
GltfResourceLoader_destroy(tEngine, tResourceLoader); GltfResourceLoader_destroy(tEngine, tResourceLoader);
callback(); callback();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void GltfResourceLoader_loadResourcesRenderThread(TGltfResourceLoader *tGltfResourceLoader, TFilamentAsset *tFilamentAsset, void (*callback)(bool)) { EMSCRIPTEN_KEEPALIVE void GltfResourceLoader_loadResourcesRenderThread(TGltfResourceLoader *tGltfResourceLoader, TFilamentAsset *tFilamentAsset, void (*callback)(bool)) {
@@ -899,7 +914,7 @@ extern "C"
auto result = GltfResourceLoader_loadResources(tGltfResourceLoader, tFilamentAsset); auto result = GltfResourceLoader_loadResources(tGltfResourceLoader, tFilamentAsset);
callback(result); callback(result);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void GltfResourceLoader_addResourceDataRenderThread( EMSCRIPTEN_KEEPALIVE void GltfResourceLoader_addResourceDataRenderThread(
@@ -914,7 +929,7 @@ extern "C"
GltfResourceLoader_addResourceData(tGltfResourceLoader, uri, data, length); GltfResourceLoader_addResourceData(tGltfResourceLoader, uri, data, length);
callback(); callback();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void GltfAssetLoader_loadRenderThread( EMSCRIPTEN_KEEPALIVE void GltfAssetLoader_loadRenderThread(
@@ -931,7 +946,7 @@ extern "C"
auto loader = GltfAssetLoader_load(tEngine, tAssetLoader, data, length, numInstances); auto loader = GltfAssetLoader_load(tEngine, tAssetLoader, data, length, numInstances);
callback(loader); callback(loader);
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
EMSCRIPTEN_KEEPALIVE void Scene_addFilamentAssetRenderThread(TScene* tScene, TFilamentAsset *tAsset, void (*callback)()) { EMSCRIPTEN_KEEPALIVE void Scene_addFilamentAssetRenderThread(TScene* tScene, TFilamentAsset *tAsset, void (*callback)()) {
@@ -941,6 +956,6 @@ extern "C"
Scene_addFilamentAsset(tScene, tAsset); Scene_addFilamentAsset(tScene, tAsset);
callback(); callback();
}); });
auto fut = _rl->add_task(lambda); auto fut = _renderThread->add_task(lambda);
} }
} }

View File

@@ -1,4 +1,4 @@
#include "rendering/RenderLoop.hpp" #include "rendering/RenderThread.hpp"
#include <functional> #include <functional>
#include <stdlib.h> #include <stdlib.h>
@@ -8,7 +8,7 @@
namespace thermion { namespace thermion {
RenderLoop::RenderLoop() RenderThread::RenderThread()
{ {
srand(time(NULL)); srand(time(NULL));
t = new std::thread([this]() { t = new std::thread([this]() {
@@ -18,26 +18,27 @@ RenderLoop::RenderLoop()
}); });
} }
RenderLoop::~RenderLoop() RenderThread::~RenderThread()
{ {
TRACE("Destroying RenderLoop"); TRACE("Destroying RenderThread");
_stop = true; _stop = true;
_cv.notify_one(); _cv.notify_one();
TRACE("Joining RenderLoop thread.."); TRACE("Joining RenderThread thread..");
t->join(); t->join();
delete t; delete t;
TRACE("RenderLoop destructor complete"); TRACE("RenderThread destructor complete");
} }
void RenderLoop::requestFrame(void (*callback)()) void RenderThread::requestFrame(void (*callback)())
{ {
TRACE("Request frame");
std::unique_lock<std::mutex> lock(_mutex); std::unique_lock<std::mutex> lock(_mutex);
this->_requestFrameRenderCallback = callback; this->_requestFrameRenderCallback = callback;
_cv.notify_one(); _cv.notify_one();
} }
void RenderLoop::iter() void RenderThread::iter()
{ {
{ {
std::unique_lock<std::mutex> lock(_mutex); std::unique_lock<std::mutex> lock(_mutex);

View File

@@ -10,283 +10,288 @@ void main() async {
final testHelper = TestHelper("geometry"); final testHelper = TestHelper("geometry");
await testHelper.setup(); await testHelper.setup();
group("custom geometry", () { group("custom geometry", () {
test('create cube (uvs only)', () async { test('add geometry', () async {
var viewer = await testHelper.createViewer();
await viewer
.createGeometry(GeometryHelper.cube(normals: false, uvs: true));
await testHelper.capture(viewer, "geometry_cube_with_uvs");
});
test('create cube with normals & uvs', () async {
var viewer = await testHelper.createViewer();
await viewer
.createGeometry(GeometryHelper.cube(normals: true, uvs: true));
await testHelper.capture(viewer, "geometry_cube_with_normals_uvs");
});
test('create cube (no normals/uvs)', () async {
await testHelper.withViewer((viewer) async { await testHelper.withViewer((viewer) async {
var viewMatrix = final asset = await viewer.createGeometry(GeometryHelper.cube());
makeViewMatrix(Vector3(0, 2, 5), Vector3.zero(), Vector3(0, 1, 0)); await viewer.addToScene(asset);
viewMatrix.invert(); await testHelper.capture(viewer.view, "add_geometry");
await viewer.setCameraModelMatrix4(viewMatrix); await viewer.removeFromScene(asset);
final cube = await viewer await testHelper.capture(viewer.view, "remove_geometry");
.createGeometry(GeometryHelper.cube(normals: false, uvs: false)); await viewer.destroyAsset(asset);
await testHelper.capture(viewer, "geometry_cube_no_normals_uvs");
await viewer.destroyAsset(cube);
await testHelper.capture(viewer, "geometry_remove_cube");
});
});
test('create cube with unlit ubershader material (no normals/uvs)',
() async {
await testHelper.withViewer((viewer) async {
final materialInstance =
await viewer.createUbershaderMaterialInstance(unlit: true);
await materialInstance.setParameterFloat4(
"baseColorFactor", 1.0, 0.0, 0.0, 1.0);
final cube = await viewer.createGeometry(
GeometryHelper.cube(normals: false, uvs: false),
materialInstances: [materialInstance]);
await testHelper.capture(viewer, "geometry_cube_ubershader");
});
});
test('create cube with lit ubershader material (normals/ no uvs)',
() async {
await testHelper.withViewer((viewer) async {
final materialInstance = await viewer.createUbershaderMaterialInstance(
unlit: false, alphaMode: AlphaMode.BLEND, hasVertexColors: false);
await materialInstance.setParameterFloat4(
"baseColorFactor", 1.0, 0.0, 0.0, 1.0);
final cube = await viewer.createGeometry(
GeometryHelper.cube(normals: true, uvs: false),
materialInstances: [materialInstance]);
await viewer.addDirectLight(DirectLight.sun(
intensity: 100000,
castShadows: false,
direction: Vector3(0, -0.5, -1)));
// await viewer.addDirectLight(DirectLight.spot(
// intensity: 1000000,
// position: Vector3(0,3,3),
// direction: Vector3(0,-1.5,-1),
// falloffRadius: 10));
await materialInstance.setParameterFloat4(
"baseColorFactor", 1.0, 0.0, 0.0, 1.0);
await testHelper.capture(viewer, "geometry_cube_lit_ubershader");
});
});
test('create instance', () async {
await testHelper.withViewer((viewer) async {
final cube = await viewer
.createGeometry(GeometryHelper.cube(normals: false, uvs: false));
await viewer.setTransform(
cube.entity, Matrix4.translation(Vector3.all(-1)));
final instance = await cube.createInstance();
await instance.addToScene();
await viewer.setTransform(
instance.entity, Matrix4.translation(Vector3.all(1)));
await testHelper.capture(viewer, "geometry_instanced");
await viewer.destroyAsset(instance);
await testHelper.capture(viewer, "geometry_instance_removed");
});
});
test('create instance (shared material)', () async {
await testHelper.withViewer((viewer) async {
final materialInstance = await viewer.createUnlitMaterialInstance();
await materialInstance.setParameterFloat4(
"baseColorFactor", 1.0, 0.0, 0.0, 1.0);
final cube = await viewer.createGeometry(
GeometryHelper.cube(normals: true, uvs: false),
materialInstances: [materialInstance]);
final instance = await cube.createInstance();
await instance.addToScene();
await viewer.setTransform(
instance.entity, Matrix4.translation(Vector3.all(1)));
await testHelper.capture(
viewer, "geometry_instanced_with_shared_material");
});
});
// test('create instance (no material on second instance)', () async {
// await testHelper.withViewer((viewer) async {
// final materialInstance = await viewer.createUnlitMaterialInstance();
// await materialInstance.setParameterFloat4(
// "baseColorFactor", 1.0, 0.0, 0.0, 1.0);
// final cube = await viewer.createGeometry(
// GeometryHelper.cube(normals: true, uvs: false),
// materialInstances: [materialInstance]);
// final instance = await viewer
// .createInstance(cube);
// await viewer.setTransform(
// instance.entity, Matrix4.translation(Vector3.all(1)));
// await testHelper.capture(
// viewer, "geometry_instanced_with_no_material_instance");
// });
// });
// test('create instance (separate materials)', () async {
// await testHelper.withViewer((viewer) async {
// final materialInstance = await viewer.createUnlitMaterialInstance();
// await materialInstance.setParameterFloat4(
// "baseColorFactor", 1.0, 0.0, 0.0, 1.0);
// final cube = await viewer.createGeometry(
// GeometryHelper.cube(normals: true, uvs: false),
// materialInstances: [materialInstance]);
// final materialInstance2 = await viewer.createUnlitMaterialInstance();
// await materialInstance2.setParameterFloat4(
// "baseColorFactor", 0.0, 1.0, 0.0, 1.0);
// final instance = await viewer
// .createInstance(cube, materialInstances: [materialInstance2]);
// await viewer.setTransform(
// instance.entity, Matrix4.translation(Vector3.all(1)));
// await testHelper.capture(
// viewer, "geometry_instanced_with_separate_material_instances");
// });
// });
test('create cube with custom ubershader material (color)', () async {
await testHelper.withViewer((viewer) async {
await viewer.addLight(LightType.SUN, 6500, 1000000, 0, 0, 0, 0, 0, -1);
await viewer.setCameraPosition(0, 2, 6);
await viewer
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 8));
await viewer.setBackgroundColor(1.0, 0.0, 1.0, 1.0);
var materialInstance =
await viewer.createUbershaderMaterialInstance(unlit: true);
final cube = await viewer.createGeometry(
GeometryHelper.cube(uvs: false, normals: true),
materialInstances: [materialInstance]);
await materialInstance.setParameterFloat4(
"baseColorFactor", 0.0, 1.0, 0.0, 0.0);
await testHelper.capture(
viewer, "geometry_cube_with_custom_material_ubershader");
await viewer.destroyAsset(cube);
});
});
test('create cube with custom ubershader material instance (texture)',
() async {
var viewer = await testHelper.createViewer();
await viewer.addLight(LightType.SUN, 6500, 1000000, 0, 0, 0, 0, 0, -1);
await viewer.setCameraPosition(0, 2, 6);
await viewer
.setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 8));
await viewer.setBackgroundColor(1.0, 0.0, 0.0, 1.0);
var materialInstance = await viewer.createUbershaderMaterialInstance();
final cube = await viewer.createGeometry(
GeometryHelper.cube(uvs: true, normals: true),
materialInstances: [materialInstance]);
final image = await viewer.decodeImage(
File("${testHelper.testDir}/assets/cube_texture_512x512.png")
.readAsBytesSync());
var texture = await viewer.createTexture(
await image.getWidth(), await image.getHeight());
await texture.setLinearImage(
image, PixelDataFormat.RGBA, PixelDataType.FLOAT);
await testHelper.capture(
viewer, "geometry_cube_with_custom_material_ubershader_texture");
await viewer.destroyAsset(cube);
await texture.dispose();
await image.destroy();
});
test('unlit material with color only', () async {
await testHelper.withViewer((viewer) async {
var materialInstance = await viewer.createUnlitMaterialInstance();
var cube = await viewer.createGeometry(GeometryHelper.cube(),
materialInstances: [materialInstance]);
await materialInstance.setParameterFloat4(
"baseColorFactor", 0.0, 1.0, 0.0, 1.0);
await testHelper.capture(viewer, "unlit_material_base_color");
});
});
test('unlit material with texture', () async {
await testHelper.withViewer((viewer) async {
var materialInstance = await viewer.createUnlitMaterialInstance();
var cube = await viewer.createGeometry(GeometryHelper.cube(),
materialInstances: [materialInstance]);
await materialInstance.setParameterInt("baseColorIndex", 0);
final image = await viewer.decodeImage(
File("${testHelper.testDir}/assets/cube_texture_512x512.png")
.readAsBytesSync());
var texture = await viewer.createTexture(
await image.getWidth(), await image.getHeight());
await texture.setLinearImage(
image, PixelDataFormat.RGBA, PixelDataType.FLOAT);
await testHelper.capture(viewer, "unlit_material_texture_only");
await viewer.destroyAsset(cube);
});
});
test('shared material instance with texture and base color', () async {
await testHelper.withViewer((viewer) async {
var materialInstance = await viewer.createUnlitMaterialInstance();
var cube1 = await viewer.createGeometry(GeometryHelper.cube(),
materialInstances: [materialInstance]);
var cube2 = await viewer.createGeometry(GeometryHelper.cube(),
materialInstances: [materialInstance]);
await viewer.setTransform(
cube2.entity, Matrix4.translation(Vector3(1, 1, 1)));
await materialInstance.setParameterInt("baseColorIndex", 0);
final image = await viewer.decodeImage(
File("${testHelper.testDir}/assets/cube_texture_512x512.png")
.readAsBytesSync());
var texture = await viewer.createTexture(
await image.getWidth(), await image.getHeight());
await texture.setLinearImage(
image, PixelDataFormat.RGBA, PixelDataType.FLOAT);
await testHelper.capture(viewer, "unlit_material_shared");
await texture.dispose();
await image.destroy();
});
});
test('create sphere (no normals)', () async {
await testHelper.withViewer((viewer) async {
await viewer
.createGeometry(GeometryHelper.sphere(normals: false, uvs: false));
await testHelper.capture(viewer, "geometry_sphere_no_normals");
}, bg: kBlue, cameraPosition: Vector3(0, 0, 6));
});
test('create multiple (non-instanced) geometry', () async {
await testHelper.withViewer((viewer) async {
final cube1 = await viewer
.createGeometry(GeometryHelper.cube(normals: false, uvs: false));
final cube2 = await viewer
.createGeometry(GeometryHelper.cube(normals: false, uvs: false));
await viewer.setTransform(
cube2.entity, Matrix4.translation(Vector3(0, 1.5, 0)));
await testHelper.capture(viewer, "multiple_geometry");
}, bg: kRed); }, bg: kRed);
}); });
test('create camera geometry', () async {
await testHelper.withViewer((viewer) async {
final camera = await viewer.createGeometry(
GeometryHelper.wireframeCamera(normals: false, uvs: false));
await viewer.setTransform(camera.entity, Matrix4.rotationY(pi / 4));
await testHelper.capture(viewer, "camera_geometry");
});
});
}); });
// test('create cube with normals & uvs', () async {
// var viewer = await testHelper.createViewer();
// await viewer
// .createGeometry(GeometryHelper.cube(normals: true, uvs: true));
// await testHelper.capture(viewer, "geometry_cube_with_normals_uvs");
// });
// test('create cube (no normals/uvs)', () async {
// await testHelper.withViewer((viewer) async {
// var viewMatrix =
// makeViewMatrix(Vector3(0, 2, 5), Vector3.zero(), Vector3(0, 1, 0));
// viewMatrix.invert();
// await viewer.setCameraModelMatrix4(viewMatrix);
// final cube = await viewer
// .createGeometry(GeometryHelper.cube(normals: false, uvs: false));
// await testHelper.capture(viewer, "geometry_cube_no_normals_uvs");
// await viewer.destroyAsset(cube);
// await testHelper.capture(viewer, "geometry_remove_cube");
// });
// });
// test('create cube with unlit ubershader material (no normals/uvs)',
// () async {
// await testHelper.withViewer((viewer) async {
// final materialInstance =
// await viewer.createUbershaderMaterialInstance(unlit: true);
// await materialInstance.setParameterFloat4(
// "baseColorFactor", 1.0, 0.0, 0.0, 1.0);
// final cube = await viewer.createGeometry(
// GeometryHelper.cube(normals: false, uvs: false),
// materialInstances: [materialInstance]);
// await testHelper.capture(viewer, "geometry_cube_ubershader");
// });
// });
// test('create cube with lit ubershader material (normals/ no uvs)',
// () async {
// await testHelper.withViewer((viewer) async {
// final materialInstance = await viewer.createUbershaderMaterialInstance(
// unlit: false, alphaMode: AlphaMode.BLEND, hasVertexColors: false);
// await materialInstance.setParameterFloat4(
// "baseColorFactor", 1.0, 0.0, 0.0, 1.0);
// final cube = await viewer.createGeometry(
// GeometryHelper.cube(normals: true, uvs: false),
// materialInstances: [materialInstance]);
// await viewer.addDirectLight(DirectLight.sun(
// intensity: 100000,
// castShadows: false,
// direction: Vector3(0, -0.5, -1)));
// // await viewer.addDirectLight(DirectLight.spot(
// // intensity: 1000000,
// // position: Vector3(0,3,3),
// // direction: Vector3(0,-1.5,-1),
// // falloffRadius: 10));
// await materialInstance.setParameterFloat4(
// "baseColorFactor", 1.0, 0.0, 0.0, 1.0);
// await testHelper.capture(viewer, "geometry_cube_lit_ubershader");
// });
// });
// test('create instance', () async {
// await testHelper.withViewer((viewer) async {
// final cube = await viewer
// .createGeometry(GeometryHelper.cube(normals: false, uvs: false));
// await viewer.setTransform(
// cube.entity, Matrix4.translation(Vector3.all(-1)));
// final instance = await cube.createInstance();
// await instance.addToScene();
// await viewer.setTransform(
// instance.entity, Matrix4.translation(Vector3.all(1)));
// await testHelper.capture(viewer, "geometry_instanced");
// await viewer.destroyAsset(instance);
// await testHelper.capture(viewer, "geometry_instance_removed");
// });
// });
// test('create instance (shared material)', () async {
// await testHelper.withViewer((viewer) async {
// final materialInstance = await viewer.createUnlitMaterialInstance();
// await materialInstance.setParameterFloat4(
// "baseColorFactor", 1.0, 0.0, 0.0, 1.0);
// final cube = await viewer.createGeometry(
// GeometryHelper.cube(normals: true, uvs: false),
// materialInstances: [materialInstance]);
// final instance = await cube.createInstance();
// await instance.addToScene();
// await viewer.setTransform(
// instance.entity, Matrix4.translation(Vector3.all(1)));
// await testHelper.capture(
// viewer, "geometry_instanced_with_shared_material");
// });
// });
// // test('create instance (no material on second instance)', () async {
// // await testHelper.withViewer((viewer) async {
// // final materialInstance = await viewer.createUnlitMaterialInstance();
// // await materialInstance.setParameterFloat4(
// // "baseColorFactor", 1.0, 0.0, 0.0, 1.0);
// // final cube = await viewer.createGeometry(
// // GeometryHelper.cube(normals: true, uvs: false),
// // materialInstances: [materialInstance]);
// // final instance = await viewer
// // .createInstance(cube);
// // await viewer.setTransform(
// // instance.entity, Matrix4.translation(Vector3.all(1)));
// // await testHelper.capture(
// // viewer, "geometry_instanced_with_no_material_instance");
// // });
// // });
// // test('create instance (separate materials)', () async {
// // await testHelper.withViewer((viewer) async {
// // final materialInstance = await viewer.createUnlitMaterialInstance();
// // await materialInstance.setParameterFloat4(
// // "baseColorFactor", 1.0, 0.0, 0.0, 1.0);
// // final cube = await viewer.createGeometry(
// // GeometryHelper.cube(normals: true, uvs: false),
// // materialInstances: [materialInstance]);
// // final materialInstance2 = await viewer.createUnlitMaterialInstance();
// // await materialInstance2.setParameterFloat4(
// // "baseColorFactor", 0.0, 1.0, 0.0, 1.0);
// // final instance = await viewer
// // .createInstance(cube, materialInstances: [materialInstance2]);
// // await viewer.setTransform(
// // instance.entity, Matrix4.translation(Vector3.all(1)));
// // await testHelper.capture(
// // viewer, "geometry_instanced_with_separate_material_instances");
// // });
// // });
// test('create cube with custom ubershader material (color)', () async {
// await testHelper.withViewer((viewer) async {
// await viewer.addLight(LightType.SUN, 6500, 1000000, 0, 0, 0, 0, 0, -1);
// await viewer.setCameraPosition(0, 2, 6);
// await viewer
// .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 8));
// await viewer.setBackgroundColor(1.0, 0.0, 1.0, 1.0);
// var materialInstance =
// await viewer.createUbershaderMaterialInstance(unlit: true);
// final cube = await viewer.createGeometry(
// GeometryHelper.cube(uvs: false, normals: true),
// materialInstances: [materialInstance]);
// await materialInstance.setParameterFloat4(
// "baseColorFactor", 0.0, 1.0, 0.0, 0.0);
// await testHelper.capture(
// viewer, "geometry_cube_with_custom_material_ubershader");
// await viewer.destroyAsset(cube);
// });
// });
// test('create cube with custom ubershader material instance (texture)',
// () async {
// var viewer = await testHelper.createViewer();
// await viewer.addLight(LightType.SUN, 6500, 1000000, 0, 0, 0, 0, 0, -1);
// await viewer.setCameraPosition(0, 2, 6);
// await viewer
// .setCameraRotation(Quaternion.axisAngle(Vector3(1, 0, 0), -pi / 8));
// await viewer.setBackgroundColor(1.0, 0.0, 0.0, 1.0);
// var materialInstance = await viewer.createUbershaderMaterialInstance();
// final cube = await viewer.createGeometry(
// GeometryHelper.cube(uvs: true, normals: true),
// materialInstances: [materialInstance]);
// final image = await viewer.decodeImage(
// File("${testHelper.testDir}/assets/cube_texture_512x512.png")
// .readAsBytesSync());
// var texture = await viewer.createTexture(
// await image.getWidth(), await image.getHeight());
// await texture.setLinearImage(
// image, PixelDataFormat.RGBA, PixelDataType.FLOAT);
// await testHelper.capture(
// viewer, "geometry_cube_with_custom_material_ubershader_texture");
// await viewer.destroyAsset(cube);
// await texture.dispose();
// await image.destroy();
// });
// test('unlit material with color only', () async {
// await testHelper.withViewer((viewer) async {
// var materialInstance = await viewer.createUnlitMaterialInstance();
// var cube = await viewer.createGeometry(GeometryHelper.cube(),
// materialInstances: [materialInstance]);
// await materialInstance.setParameterFloat4(
// "baseColorFactor", 0.0, 1.0, 0.0, 1.0);
// await testHelper.capture(viewer, "unlit_material_base_color");
// });
// });
// test('unlit material with texture', () async {
// await testHelper.withViewer((viewer) async {
// var materialInstance = await viewer.createUnlitMaterialInstance();
// var cube = await viewer.createGeometry(GeometryHelper.cube(),
// materialInstances: [materialInstance]);
// await materialInstance.setParameterInt("baseColorIndex", 0);
// final image = await viewer.decodeImage(
// File("${testHelper.testDir}/assets/cube_texture_512x512.png")
// .readAsBytesSync());
// var texture = await viewer.createTexture(
// await image.getWidth(), await image.getHeight());
// await texture.setLinearImage(
// image, PixelDataFormat.RGBA, PixelDataType.FLOAT);
// await testHelper.capture(viewer, "unlit_material_texture_only");
// await viewer.destroyAsset(cube);
// });
// });
// test('shared material instance with texture and base color', () async {
// await testHelper.withViewer((viewer) async {
// var materialInstance = await viewer.createUnlitMaterialInstance();
// var cube1 = await viewer.createGeometry(GeometryHelper.cube(),
// materialInstances: [materialInstance]);
// var cube2 = await viewer.createGeometry(GeometryHelper.cube(),
// materialInstances: [materialInstance]);
// await viewer.setTransform(
// cube2.entity, Matrix4.translation(Vector3(1, 1, 1)));
// await materialInstance.setParameterInt("baseColorIndex", 0);
// final image = await viewer.decodeImage(
// File("${testHelper.testDir}/assets/cube_texture_512x512.png")
// .readAsBytesSync());
// var texture = await viewer.createTexture(
// await image.getWidth(), await image.getHeight());
// await texture.setLinearImage(
// image, PixelDataFormat.RGBA, PixelDataType.FLOAT);
// await testHelper.capture(viewer, "unlit_material_shared");
// await texture.dispose();
// await image.destroy();
// });
// });
// test('create sphere (no normals)', () async {
// await testHelper.withViewer((viewer) async {
// await viewer
// .createGeometry(GeometryHelper.sphere(normals: false, uvs: false));
// await testHelper.capture(viewer, "geometry_sphere_no_normals");
// }, bg: kBlue, cameraPosition: Vector3(0, 0, 6));
// });
// test('create multiple (non-instanced) geometry', () async {
// await testHelper.withViewer((viewer) async {
// final cube1 = await viewer
// .createGeometry(GeometryHelper.cube(normals: false, uvs: false));
// final cube2 = await viewer
// .createGeometry(GeometryHelper.cube(normals: false, uvs: false));
// await viewer.setTransform(
// cube2.entity, Matrix4.translation(Vector3(0, 1.5, 0)));
// await testHelper.capture(viewer, "multiple_geometry");
// }, bg: kRed);
// });
// test('create camera geometry', () async {
// await testHelper.withViewer((viewer) async {
// final camera = await viewer.createGeometry(
// GeometryHelper.wireframeCamera(normals: false, uvs: false));
// await viewer.setTransform(camera.entity, Matrix4.rotationY(pi / 4));
// await testHelper.capture(viewer, "camera_geometry");
// });
// });
// });
} }

View File

@@ -165,22 +165,25 @@ class TestHelper {
resourceLoader.ref.freeResource = freeResource.nativeFunction; resourceLoader.ref.freeResource = freeResource.nativeFunction;
await FFIFilamentApp.create(); await FFIFilamentApp.create();
await FilamentApp.instance!.setClearColor(0, 0, 0, 1); await FilamentApp.instance!.setClearColor(0, 1, 0, 1);
} }
/// ///
/// ///
/// ///
Future withViewer(Future Function(ThermionViewer viewer) fn, Future withViewer(
{img.Color? bg, Future Function(ThermionViewer viewer) fn, {
Vector3? cameraPosition, img.Color? bg,
({int width, int height}) viewportDimensions = (width: 500, height: 500), Vector3? cameraPosition,
bool postProcessing = false, ({int width, int height}) viewportDimensions = (width: 512, height: 512),
bool createRenderTarget = false}) async { bool postProcessing = false,
bool addSkybox = false,
bool createRenderTarget = false,
}) async {
cameraPosition ??= Vector3(0, 2, 6); cameraPosition ??= Vector3(0, 2, 6);
var swapChain = await FilamentApp.instance!.createHeadlessSwapChain( var swapChain = await FilamentApp.instance!
viewportDimensions.width, viewportDimensions.height) as FFISwapChain; .createHeadlessSwapChain(viewportDimensions.width, viewportDimensions.height) as FFISwapChain;
FFIRenderTarget? renderTarget; FFIRenderTarget? renderTarget;
if (createRenderTarget) { if (createRenderTarget) {
@@ -198,7 +201,8 @@ class TestHelper {
}, },
textureFormat: TextureFormat.RGB32F, textureFormat: TextureFormat.RGB32F,
importedTextureHandle: metalColorTexture.metalTextureAddress); importedTextureHandle: metalColorTexture.metalTextureAddress);
var width = await color.getWidth();
var height = await color.getHeight();
var depth = await FilamentApp.instance! var depth = await FilamentApp.instance!
.createTexture(viewportDimensions.width, viewportDimensions.height, .createTexture(viewportDimensions.width, viewportDimensions.height,
flags: { flags: {
@@ -216,16 +220,23 @@ class TestHelper {
var viewer = ThermionViewerFFI( var viewer = ThermionViewerFFI(
loadAssetFromUri: (path) async => loadAssetFromUri: (path) async =>
File(path.replaceAll("file://", "")).readAsBytesSync(), File(path.replaceAll("file://", "")).readAsBytesSync());
renderTarget: renderTarget);
await viewer.initialized; await viewer.initialized;
await FilamentApp.instance!.register(swapChain, viewer.view); await FilamentApp.instance!.register(swapChain, viewer.view);
if (renderTarget != null) {
await viewer.view.setRenderTarget(renderTarget);
}
await viewer.view await viewer.view
.setViewport(viewportDimensions.width, viewportDimensions.height); .setViewport(
viewportDimensions.width,
viewportDimensions.height
);
await viewer.view.setBloom(false, 0); if (addSkybox) {
await viewer
.loadSkybox("file://${testDir}/assets/default_env_skybox.ktx");
}
if (bg != null) { if (bg != null) {
await viewer.setBackgroundColor( await viewer.setBackgroundColor(

View File

@@ -20,4 +20,5 @@ class ThermionFlutterPlugin {
return viewer; return viewer;
} }
} }

View File

@@ -1,98 +1,98 @@
import 'dart:math'; // import 'dart:math';
import 'package:flutter/material.dart'; // import 'package:flutter/material.dart';
import 'package:thermion_flutter/thermion_flutter.dart'; // import 'package:thermion_flutter/thermion_flutter.dart';
import 'package:vector_math/vector_math_64.dart' as v64; // import 'package:vector_math/vector_math_64.dart' as v64;
class CameraOrientationWidget extends StatefulWidget { // class CameraOrientationWidget extends StatefulWidget {
final ThermionViewer viewer; // final ThermionViewer viewer;
const CameraOrientationWidget({Key? key, required this.viewer}) // const CameraOrientationWidget({Key? key, required this.viewer})
: super(key: key); // : super(key: key);
@override // @override
_CameraOrientationWidgetState createState() => // _CameraOrientationWidgetState createState() =>
_CameraOrientationWidgetState(); // _CameraOrientationWidgetState();
} // }
class _CameraOrientationWidgetState extends State<CameraOrientationWidget> // class _CameraOrientationWidgetState extends State<CameraOrientationWidget>
with SingleTickerProviderStateMixin { // with SingleTickerProviderStateMixin {
late AnimationController _controller; // late AnimationController _controller;
v64.Vector3? _position; // v64.Vector3? _position;
v64.Matrix3? _rotation; // v64.Matrix3? _rotation;
@override // @override
void initState() { // void initState() {
super.initState(); // super.initState();
_controller = AnimationController( // _controller = AnimationController(
vsync: this, // vsync: this,
duration: const Duration(milliseconds: 16), // ~60 FPS // duration: const Duration(milliseconds: 16), // ~60 FPS
)..repeat(); // )..repeat();
_controller.addListener(_updateCameraInfo); // _controller.addListener(_updateCameraInfo);
} // }
void _updateCameraInfo() async { // void _updateCameraInfo() async {
final camera = await widget.viewer.getActiveCamera(); // final camera = await widget.viewer.getActiveCamera();
final position = await widget.viewer.getCameraPosition(); // final position = await widget.viewer.getCameraPosition();
final rotation = await widget.viewer.getCameraRotation(); // final rotation = await widget.viewer.getCameraRotation();
setState(() { // setState(() {
_position = position; // _position = position;
_rotation = rotation; // _rotation = rotation;
}); // });
} // }
@override // @override
void dispose() { // void dispose() {
_controller.dispose(); // _controller.dispose();
super.dispose(); // super.dispose();
} // }
@override // @override
Widget build(BuildContext context) { // Widget build(BuildContext context) {
return Container( // return Container(
padding: const EdgeInsets.all(8.0), // padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration( // decoration: BoxDecoration(
color: Colors.black.withOpacity(0.7), // color: Colors.black.withOpacity(0.7),
borderRadius: BorderRadius.circular(8.0), // borderRadius: BorderRadius.circular(8.0),
), // ),
child: Column( // child: Column(
crossAxisAlignment: CrossAxisAlignment.start, // crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min, // mainAxisSize: MainAxisSize.min,
children: [ // children: [
Text( // Text(
'Position: ${_formatVector(_position)}', // 'Position: ${_formatVector(_position)}',
style: const TextStyle(color: Colors.white), // style: const TextStyle(color: Colors.white),
), // ),
const SizedBox(height: 4), // const SizedBox(height: 4),
Text( // Text(
'Rotation: ${_formatMatrix(_rotation)}', // 'Rotation: ${_formatMatrix(_rotation)}',
style: const TextStyle(color: Colors.white), // style: const TextStyle(color: Colors.white),
), // ),
], // ],
), // ),
); // );
} // }
String _formatVector(v64.Vector3? vector) { // String _formatVector(v64.Vector3? vector) {
if (vector == null) return 'N/A'; // if (vector == null) return 'N/A';
return '(${vector.x.toStringAsFixed(2)}, ${vector.y.toStringAsFixed(2)}, ${vector.z.toStringAsFixed(2)})'; // return '(${vector.x.toStringAsFixed(2)}, ${vector.y.toStringAsFixed(2)}, ${vector.z.toStringAsFixed(2)})';
} // }
String _formatMatrix(v64.Matrix3? matrix) { // String _formatMatrix(v64.Matrix3? matrix) {
if (matrix == null) return 'N/A'; // if (matrix == null) return 'N/A';
return 'Yaw: ${_getYaw(matrix).toStringAsFixed(2)}°, Pitch: ${_getPitch(matrix).toStringAsFixed(2)}°, Roll: ${_getRoll(matrix).toStringAsFixed(2)}°'; // return 'Yaw: ${_getYaw(matrix).toStringAsFixed(2)}°, Pitch: ${_getPitch(matrix).toStringAsFixed(2)}°, Roll: ${_getRoll(matrix).toStringAsFixed(2)}°';
} // }
double _getYaw(v64.Matrix3 matrix) { // double _getYaw(v64.Matrix3 matrix) {
return -atan2(matrix[2], matrix[0]) * 180 / pi; // return -atan2(matrix[2], matrix[0]) * 180 / pi;
} // }
double _getPitch(v64.Matrix3 matrix) { // double _getPitch(v64.Matrix3 matrix) {
return -asin(matrix[5]) * 180 / pi; // return -asin(matrix[5]) * 180 / pi;
} // }
double _getRoll(v64.Matrix3 matrix) { // double _getRoll(v64.Matrix3 matrix) {
return atan2(matrix[3], matrix[4]) * 180 / pi; // return atan2(matrix[3], matrix[4]) * 180 / pi;
} // }
} // }

View File

@@ -1,79 +1,79 @@
import 'package:flutter/material.dart'; // import 'package:flutter/material.dart';
import 'package:thermion_dart/thermion_dart.dart'; // import 'package:thermion_dart/thermion_dart.dart';
class CameraSelectorWidget extends StatefulWidget { // class CameraSelectorWidget extends StatefulWidget {
final ThermionViewer viewer; // final ThermionViewer viewer;
const CameraSelectorWidget({Key? key, required this.viewer}) : super(key: key); // const CameraSelectorWidget({Key? key, required this.viewer}) : super(key: key);
@override // @override
_CameraSelectorWidgetState createState() => _CameraSelectorWidgetState(); // _CameraSelectorWidgetState createState() => _CameraSelectorWidgetState();
} // }
class _CameraSelectorWidgetState extends State<CameraSelectorWidget> { // class _CameraSelectorWidgetState extends State<CameraSelectorWidget> {
int _activeIndex = 0; // int _activeIndex = 0;
@override // @override
Widget build(BuildContext context) { // Widget build(BuildContext context) {
int cameraCount = widget.viewer.getCameraCount(); // int cameraCount = widget.viewer.getCameraCount();
return Container( // return Container(
height:32, // height:32,
margin: const EdgeInsets.all(8), // margin: const EdgeInsets.all(8),
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), // padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12),
decoration: BoxDecoration( // decoration: BoxDecoration(
color: Colors.white, // color: Colors.white,
borderRadius: BorderRadius.circular(8), // borderRadius: BorderRadius.circular(8),
boxShadow: [ // boxShadow: [
BoxShadow( // BoxShadow(
color: Colors.black.withOpacity(0.1), // color: Colors.black.withOpacity(0.1),
blurRadius: 4, // blurRadius: 4,
offset: const Offset(0, 2), // offset: const Offset(0, 2),
), // ),
], // ],
), // ),
child: Row( // child: Row(
crossAxisAlignment: CrossAxisAlignment.center, // crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min, // mainAxisSize: MainAxisSize.min,
children: [ // children: [
_buildCameraButton("Main", 0), // _buildCameraButton("Main", 0),
if (cameraCount > 1) const VerticalDivider(width: 16, thickness: 1), // if (cameraCount > 1) const VerticalDivider(width: 16, thickness: 1),
...List.generate(cameraCount - 1, (index) { // ...List.generate(cameraCount - 1, (index) {
return _buildCameraButton("${index + 1}", index + 1); // return _buildCameraButton("${index + 1}", index + 1);
}), // }),
], // ],
), // ),
); // );
} // }
Widget _buildCameraButton(String label, int index) { // Widget _buildCameraButton(String label, int index) {
bool isActive = _activeIndex == index; // bool isActive = _activeIndex == index;
return Flexible(child:TextButton( // return Flexible(child:TextButton(
onPressed: () async { // onPressed: () async {
if (index == 0) { // if (index == 0) {
await widget.viewer.setMainCamera(); // await widget.viewer.setMainCamera();
} else { // } else {
Camera camera = widget.viewer.getCameraAt(index); // Camera camera = widget.viewer.getCameraAt(index);
await widget.viewer.setActiveCamera(camera); // await widget.viewer.setActiveCamera(camera);
} // }
setState(() { // setState(() {
_activeIndex = index; // _activeIndex = index;
}); // });
}, // },
style: TextButton.styleFrom( // style: TextButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), // padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
minimumSize: Size.zero, // minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap, // tapTargetSize: MaterialTapTargetSize.shrinkWrap,
backgroundColor: isActive ? Colors.blue.withOpacity(0.1) : null, // backgroundColor: isActive ? Colors.blue.withOpacity(0.1) : null,
), // ),
child: Text( // child: Text(
label, // label,
style: TextStyle( // style: TextStyle(
fontSize: 10, // fontSize: 10,
fontWeight: isActive ? FontWeight.bold : FontWeight.normal, // fontWeight: isActive ? FontWeight.bold : FontWeight.normal,
color: isActive ? Colors.blue : Colors.black87, // color: isActive ? Colors.blue : Colors.black87,
), // ),
), // ),
)); // ));
} // }
} // }

View File

@@ -1,110 +1,110 @@
import 'package:animation_tools_dart/animation_tools_dart.dart'; // import 'package:animation_tools_dart/animation_tools_dart.dart';
import 'package:thermion_dart/thermion_dart.dart'; // import 'package:thermion_dart/thermion_dart.dart';
import 'package:flutter/material.dart'; // import 'package:flutter/material.dart';
import 'dart:math'; // import 'dart:math';
class ChildRenderableWidget extends StatelessWidget { // class ChildRenderableWidget extends StatelessWidget {
final ThermionViewer controller; // final ThermionViewer controller;
final ThermionEntity entity; // final ThermionEntity entity;
const ChildRenderableWidget( // const ChildRenderableWidget(
{super.key, required this.controller, required this.entity}); // {super.key, required this.controller, required this.entity});
Widget _childRenderable(ThermionEntity childEntity) { // Widget _childRenderable(ThermionEntity childEntity) {
var name = controller.getNameForEntity(childEntity) ?? "<none>"; // var name = controller.getNameForEntity(childEntity) ?? "<none>";
var names = controller.getMorphTargetNames(entity, childEntity); // var names = controller.getMorphTargetNames(entity, childEntity);
return FutureBuilder( // return FutureBuilder(
future: names, // future: names,
builder: (_, morphTargetsSnapshot) { // builder: (_, morphTargetsSnapshot) {
if (!morphTargetsSnapshot.hasData) { // if (!morphTargetsSnapshot.hasData) {
return Container(); // return Container();
} // }
var morphTargets = morphTargetsSnapshot.data!; // var morphTargets = morphTargetsSnapshot.data!;
final menuChildren = <Widget>[]; // final menuChildren = <Widget>[];
if (morphTargets.isEmpty) { // if (morphTargets.isEmpty) {
menuChildren.add(Text("None")); // menuChildren.add(Text("None"));
} else { // } else {
for (int i = 0; i < 2; i++) { // for (int i = 0; i < 2; i++) {
var newWeights = List.filled(morphTargets.length, i.toDouble()); // var newWeights = List.filled(morphTargets.length, i.toDouble());
menuChildren.add(MenuItemButton( // menuChildren.add(MenuItemButton(
child: Text("Set to $i"), // child: Text("Set to $i"),
onPressed: () async { // onPressed: () async {
try { // try {
await controller! // await controller!
.setMorphTargetWeights(childEntity, newWeights); // .setMorphTargetWeights(childEntity, newWeights);
} catch (err, st) { // } catch (err, st) {
print("Error setting morph target weights"); // print("Error setting morph target weights");
print(err); // print(err);
print(st); // print(st);
} // }
})); // }));
} // }
menuChildren.add(MenuItemButton( // menuChildren.add(MenuItemButton(
child: Text("Animate all morph target from 0 to 1"), // child: Text("Animate all morph target from 0 to 1"),
onPressed: () async { // onPressed: () async {
var morphData = MorphAnimationData( // var morphData = MorphAnimationData(
List<List<double>>.generate( // List<List<double>>.generate(
120, // 120,
(i) => List<double>.filled( // (i) => List<double>.filled(
morphTargets.length, i / 120)), // morphTargets.length, i / 120)),
morphTargets); // morphTargets);
await controller!.setMorphAnimationData(entity, morphData, // await controller!.setMorphAnimationData(entity, morphData,
targetMeshNames: [name]); // targetMeshNames: [name]);
})); // }));
menuChildren.addAll(morphTargets.map((t) => Text(t))); // menuChildren.addAll(morphTargets.map((t) => Text(t)));
} // }
return SubmenuButton(child: Text(name), menuChildren: menuChildren); // return SubmenuButton(child: Text(name), menuChildren: menuChildren);
}); // });
} // }
@override // @override
Widget build(BuildContext context) { // Widget build(BuildContext context) {
return FutureBuilder( // return FutureBuilder(
future: controller!.getChildEntities(entity, true), // future: controller!.getChildEntities(entity, true),
builder: (ctx, snapshot) { // builder: (ctx, snapshot) {
if (!snapshot.hasData) { // if (!snapshot.hasData) {
return Container(); // return Container();
} // }
var children = snapshot.data!; // var children = snapshot.data!;
return SubmenuButton( // return SubmenuButton(
child: Text("Renderable entities"), // child: Text("Renderable entities"),
menuChildren: <Widget>[ // menuChildren: <Widget>[
MenuItemButton( // MenuItemButton(
child: Text("Set children transforms to identity"), // child: Text("Set children transforms to identity"),
onPressed: () async { // onPressed: () async {
var childEntities = // var childEntities =
await controller.getChildEntities(entity, true); // await controller.getChildEntities(entity, true);
for (final child in childEntities) { // for (final child in childEntities) {
await controller.setTransform( // await controller.setTransform(
child, Matrix4.identity()); // child, Matrix4.identity());
} // }
}), // }),
MenuItemButton( // MenuItemButton(
child: Text("Set children transforms to 90/X"), // child: Text("Set children transforms to 90/X"),
onPressed: () async { // onPressed: () async {
var childEntities = // var childEntities =
await controller.getChildEntities(entity, true); // await controller.getChildEntities(entity, true);
for (final child in childEntities) { // for (final child in childEntities) {
await controller.setTransform( // await controller.setTransform(
child, Matrix4.rotationX(pi / 2)); // child, Matrix4.rotationX(pi / 2));
} // }
}), // }),
MenuItemButton( // MenuItemButton(
child: Text("Set children transforms to 90/Y"), // child: Text("Set children transforms to 90/Y"),
onPressed: () async { // onPressed: () async {
var childEntities = // var childEntities =
await controller.getChildEntities(entity, true); // await controller.getChildEntities(entity, true);
for (final child in childEntities) { // for (final child in childEntities) {
await controller.setTransform( // await controller.setTransform(
child, Matrix4.rotationY(pi / 2)); // child, Matrix4.rotationY(pi / 2));
} // }
}), // }),
] + // ] +
children.map(_childRenderable).toList()); // children.map(_childRenderable).toList());
}); // });
} // }
} // }

View File

@@ -1,421 +1,421 @@
import 'dart:math'; // import 'dart:math';
import 'package:thermion_dart/thermion_dart.dart'; // import 'package:thermion_dart/thermion_dart.dart';
import 'package:animation_tools_dart/animation_tools_dart.dart'; // import 'package:animation_tools_dart/animation_tools_dart.dart';
import 'package:flutter/material.dart'; // import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; // import 'package:flutter/widgets.dart';
import 'package:vector_math/vector_math_64.dart'; // import 'package:vector_math/vector_math_64.dart';
class SkeletonMenuItemWidget extends StatelessWidget { // class SkeletonMenuItemWidget extends StatelessWidget {
final ThermionViewer controller; // final ThermionViewer controller;
final ThermionEntity entity; // final ThermionEntity entity;
const SkeletonMenuItemWidget( // const SkeletonMenuItemWidget(
{super.key, required this.controller, required this.entity}); // {super.key, required this.controller, required this.entity});
void _addBoneAnimation(String bone) async { // void _addBoneAnimation(String bone) async {
await controller.addAnimationComponent(entity); // await controller.addAnimationComponent(entity);
var numFrames = 120; // var numFrames = 120;
var animationData = List<List<BoneAnimationFrame>>.generate( // var animationData = List<List<BoneAnimationFrame>>.generate(
numFrames, // numFrames,
(frameNum) => [ // (frameNum) => [
( // (
rotation: Quaternion.axisAngle( // rotation: Quaternion.axisAngle(
Vector3(1, 0, 0), (frameNum / 90) * 2 * pi), // Vector3(1, 0, 0), (frameNum / 90) * 2 * pi),
translation: Vector3.zero() // translation: Vector3.zero()
) // )
]); // ]);
var animation = // var animation =
BoneAnimationData([bone], animationData, space: Space.ParentWorldRotation); // BoneAnimationData([bone], animationData, space: Space.ParentWorldRotation);
await controller.addAnimationComponent(entity); // await controller.addAnimationComponent(entity);
await controller.addBoneAnimation(entity, animation); // await controller.addBoneAnimation(entity, animation);
} // }
@override // @override
Widget build(BuildContext context) { // Widget build(BuildContext context) {
var boneNames = controller.getBoneNames(entity); // var boneNames = controller.getBoneNames(entity);
return FutureBuilder( // return FutureBuilder(
future: boneNames, // future: boneNames,
builder: (_, boneNamesSnapshot) { // builder: (_, boneNamesSnapshot) {
if (!boneNamesSnapshot.hasData) { // if (!boneNamesSnapshot.hasData) {
return Container(); // return Container();
} // }
var boneNames = boneNamesSnapshot.data!; // var boneNames = boneNamesSnapshot.data!;
if (boneNames.isEmpty) { // if (boneNames.isEmpty) {
return Text("No bones"); // return Text("No bones");
} // }
return SubmenuButton( // return SubmenuButton(
menuChildren: <Widget>[ // menuChildren: <Widget>[
MenuItemButton( // MenuItemButton(
onPressed: () async { // onPressed: () async {
await controller.resetBones(entity); // await controller.resetBones(entity);
}, // },
child: Text("Reset")), // child: Text("Reset")),
MenuItemButton( // MenuItemButton(
onPressed: () async { // onPressed: () async {
await controller.resetBones(entity); // await controller.resetBones(entity);
var bone = await controller.getBone(entity, 1); // var bone = await controller.getBone(entity, 1);
var frames = <List<BoneAnimationFrame>>[]; // var frames = <List<BoneAnimationFrame>>[];
for (int i = 0; i < 60; i++) { // for (int i = 0; i < 60; i++) {
var frame = <BoneAnimationFrame>[]; // var frame = <BoneAnimationFrame>[];
frame.add(( // frame.add((
rotation: // rotation:
Quaternion.axisAngle( // Quaternion.axisAngle(
Vector3(1, 0, 0), i/60 * pi/4) * // Vector3(1, 0, 0), i/60 * pi/4) *
(Quaternion.axisAngle( // (Quaternion.axisAngle(
Vector3(0, 0, 1), i/60*-pi/4) // Vector3(0, 0, 1), i/60*-pi/4)
* // *
Quaternion.axisAngle( // Quaternion.axisAngle(
Vector3(0, 1, 0), i/60 * pi/4) ), // Vector3(0, 1, 0), i/60 * pi/4) ),
translation: Vector3.zero() // translation: Vector3.zero()
)); // ));
frames.add(frame); // frames.add(frame);
} // }
var animation = BoneAnimationData(["Bone.002"], frames, // var animation = BoneAnimationData(["Bone.002"], frames,
space: Space.ParentWorldRotation); // space: Space.ParentWorldRotation);
await controller.addAnimationComponent(entity); // await controller.addAnimationComponent(entity);
await controller.addBoneAnimation(entity, animation); // await controller.addBoneAnimation(entity, animation);
}, // },
child: Text("Test animation (parent space)")), // child: Text("Test animation (parent space)")),
MenuItemButton( // MenuItemButton(
onPressed: () async { // onPressed: () async {
await controller.resetBones(entity); // await controller.resetBones(entity);
var bone = await controller.getBone(entity, 1); // var bone = await controller.getBone(entity, 1);
var frames = <List<BoneAnimationFrame>>[]; // var frames = <List<BoneAnimationFrame>>[];
for (int i = 0; i < 60; i++) { // for (int i = 0; i < 60; i++) {
var frame = <BoneAnimationFrame>[]; // var frame = <BoneAnimationFrame>[];
frame.add(( // frame.add((
rotation: Quaternion.axisAngle( // rotation: Quaternion.axisAngle(
Vector3(0, 0, 1), (i / 60) * pi / 2), // Vector3(0, 0, 1), (i / 60) * pi / 2),
translation: Vector3.zero() // translation: Vector3.zero()
)); // ));
frames.add(frame); // frames.add(frame);
} // }
var animation = BoneAnimationData( // var animation = BoneAnimationData(
["Bone.001"], frames, // ["Bone.001"], frames,
space: Space.Bone); // space: Space.Bone);
await controller.addAnimationComponent(entity); // await controller.addAnimationComponent(entity);
await controller.addBoneAnimation(entity, animation); // await controller.addBoneAnimation(entity, animation);
}, // },
child: Text("Test animation (bone space)")), // child: Text("Test animation (bone space)")),
MenuItemButton( // MenuItemButton(
onPressed: () async { // onPressed: () async {
var frames = <List<BoneAnimationFrame>>[]; // var frames = <List<BoneAnimationFrame>>[];
for (int i = 0; i < 60; i++) { // for (int i = 0; i < 60; i++) {
var frame = <BoneAnimationFrame>[]; // var frame = <BoneAnimationFrame>[];
frame.add(( // frame.add((
rotation: Quaternion.axisAngle( // rotation: Quaternion.axisAngle(
Vector3(0, 0, 1), (i / 60) * pi / 2), // Vector3(0, 0, 1), (i / 60) * pi / 2),
translation: Vector3.zero() // translation: Vector3.zero()
)); // ));
frames.add(frame); // frames.add(frame);
} // }
var animation = BoneAnimationData( // var animation = BoneAnimationData(
["Bone.001"], frames, // ["Bone.001"], frames,
space: Space.ParentWorldRotation); // space: Space.ParentWorldRotation);
await controller.addAnimationComponent(entity); // await controller.addAnimationComponent(entity);
await controller.addBoneAnimation(entity, animation); // await controller.addBoneAnimation(entity, animation);
}, // },
child: Text("Test animation 2")), // child: Text("Test animation 2")),
MenuItemButton( // MenuItemButton(
onPressed: () async { // onPressed: () async {
var frames = <List<BoneAnimationFrame>>[]; // var frames = <List<BoneAnimationFrame>>[];
for (int i = 0; i < 60; i++) { // for (int i = 0; i < 60; i++) {
var frame = <BoneAnimationFrame>[]; // var frame = <BoneAnimationFrame>[];
frame.add(( // frame.add((
rotation: Quaternion.axisAngle( // rotation: Quaternion.axisAngle(
Vector3(0, 0, 1), (i / 60) * pi / 2), // Vector3(0, 0, 1), (i / 60) * pi / 2),
translation: Vector3.zero() // translation: Vector3.zero()
)); // ));
frames.add(frame); // frames.add(frame);
} // }
var animation = BoneAnimationData( // var animation = BoneAnimationData(
["Bone.002"], frames, // ["Bone.002"], frames,
space: Space.ParentWorldRotation); // space: Space.ParentWorldRotation);
await controller.addAnimationComponent(entity); // await controller.addAnimationComponent(entity);
await controller.addBoneAnimation(entity, animation); // await controller.addBoneAnimation(entity, animation);
}, // },
child: Text("Test animation 3")) // child: Text("Test animation 3"))
] + // ] +
boneNames // boneNames
.map((name) { // .map((name) {
var boneIndex = boneNames.indexOf(name); // var boneIndex = boneNames.indexOf(name);
return SubmenuButton(child: Text(name), menuChildren: [ // return SubmenuButton(child: Text(name), menuChildren: [
MenuItemButton( // MenuItemButton(
child: Text("Print bone transforms "), // child: Text("Print bone transforms "),
onPressed: () async { // onPressed: () async {
var boneEntity = // var boneEntity =
await controller.getBone(entity, boneIndex); // await controller.getBone(entity, boneIndex);
var localTransform = await controller // var localTransform = await controller
.getLocalTransform(boneEntity); // .getLocalTransform(boneEntity);
var worldTransform = await controller // var worldTransform = await controller
.getWorldTransform(boneEntity); // .getWorldTransform(boneEntity);
var inverseWorldTransform = Matrix4.identity() // var inverseWorldTransform = Matrix4.identity()
..copyInverse(worldTransform); // ..copyInverse(worldTransform);
print("Local $localTransform"); // print("Local $localTransform");
print("World $worldTransform"); // print("World $worldTransform");
print("World inverse $inverseWorldTransform"); // print("World inverse $inverseWorldTransform");
}), // }),
MenuItemButton( // MenuItemButton(
child: Text("Set bone transform to identity"), // child: Text("Set bone transform to identity"),
onPressed: () async { // onPressed: () async {
var boneEntity = // var boneEntity =
await controller.getBone(entity, boneIndex); // await controller.getBone(entity, boneIndex);
var localTransform = Matrix4.identity(); // var localTransform = Matrix4.identity();
await controller.setTransform( // await controller.setTransform(
boneEntity, localTransform); // boneEntity, localTransform);
await controller.updateBoneMatrices(entity); // await controller.updateBoneMatrices(entity);
}), // }),
MenuItemButton( // MenuItemButton(
child: Text( // child: Text(
"Set bone transform to 90/X (parent space)"), // "Set bone transform to 90/X (parent space)"),
onPressed: () async { // onPressed: () async {
var boneEntity = // var boneEntity =
await controller.getBone(entity, boneIndex); // await controller.getBone(entity, boneIndex);
var localTransform = Matrix4.rotationX(pi / 2); // var localTransform = Matrix4.rotationX(pi / 2);
await controller.setTransform( // await controller.setTransform(
boneEntity, localTransform); // boneEntity, localTransform);
await controller.updateBoneMatrices(entity); // await controller.updateBoneMatrices(entity);
}), // }),
MenuItemButton( // MenuItemButton(
child: Text( // child: Text(
"Set bone transform to 90/X (pose space)"), // "Set bone transform to 90/X (pose space)"),
onPressed: () async { // onPressed: () async {
var boneEntity = // var boneEntity =
await controller.getBone(entity, boneIndex); // await controller.getBone(entity, boneIndex);
var localTransform = await controller // var localTransform = await controller
.getLocalTransform(boneEntity); // .getLocalTransform(boneEntity);
localTransform = // localTransform =
localTransform * Matrix4.rotationX(pi / 2); // localTransform * Matrix4.rotationX(pi / 2);
await controller.setTransform( // await controller.setTransform(
boneEntity, localTransform); // boneEntity, localTransform);
await controller.updateBoneMatrices(entity); // await controller.updateBoneMatrices(entity);
}), // }),
MenuItemButton( // MenuItemButton(
child: Text("Set bone transform to 90/X"), // child: Text("Set bone transform to 90/X"),
onPressed: () async { // onPressed: () async {
var boneEntity = // var boneEntity =
await controller.getBone(entity, boneIndex); // await controller.getBone(entity, boneIndex);
var localTransform = Matrix4.rotationX(pi / 2); // var localTransform = Matrix4.rotationX(pi / 2);
await controller.setTransform( // await controller.setTransform(
boneEntity, localTransform); // boneEntity, localTransform);
await controller.updateBoneMatrices(entity); // await controller.updateBoneMatrices(entity);
}), // }),
MenuItemButton( // MenuItemButton(
child: Text("Set bone transform to 0,-1,0"), // child: Text("Set bone transform to 0,-1,0"),
onPressed: () async { // onPressed: () async {
var boneEntity = // var boneEntity =
await controller.getBone(entity, boneIndex); // await controller.getBone(entity, boneIndex);
var localTransform = // var localTransform =
Matrix4.translation(Vector3(0, -1, 0)); // Matrix4.translation(Vector3(0, -1, 0));
await controller.setTransform( // await controller.setTransform(
boneEntity, localTransform); // boneEntity, localTransform);
await controller.updateBoneMatrices(entity); // await controller.updateBoneMatrices(entity);
}), // }),
MenuItemButton( // MenuItemButton(
child: Text( // child: Text(
"Set bone matrices/transform to identity"), // "Set bone matrices/transform to identity"),
onPressed: () async { // onPressed: () async {
var boneEntity = await controller.getBone( // var boneEntity = await controller.getBone(
entity, boneNames.indexOf(name)); // entity, boneNames.indexOf(name));
await controller.setTransform( // await controller.setTransform(
boneEntity, Matrix4.identity()); // boneEntity, Matrix4.identity());
var childEntities = await controller // var childEntities = await controller
.getChildEntities(entity, true); // .getChildEntities(entity, true);
for (final child in childEntities) { // for (final child in childEntities) {
await controller.setBoneTransform( // await controller.setBoneTransform(
child, // child,
boneNames.indexOf(name), // boneNames.indexOf(name),
Matrix4.identity()); // Matrix4.identity());
} // }
}), // }),
SubmenuButton( // SubmenuButton(
child: Text("Set bone matrices to"), // child: Text("Set bone matrices to"),
menuChildren: [ // menuChildren: [
MenuItemButton( // MenuItemButton(
child: Text("Identity"), // child: Text("Identity"),
onPressed: () async { // onPressed: () async {
await controller // await controller
.removeAnimationComponent(entity); // .removeAnimationComponent(entity);
for (var child in await controller // for (var child in await controller
.getChildEntities(entity, true)) { // .getChildEntities(entity, true)) {
print( // print(
"Setting transform for ${await controller.getNameForEntity(child)}"); // "Setting transform for ${await controller.getNameForEntity(child)}");
await controller.setBoneTransform( // await controller.setBoneTransform(
child, // child,
boneNames.indexOf(name), // boneNames.indexOf(name),
Matrix4.identity()); // Matrix4.identity());
} // }
}), // }),
SubmenuButton( // SubmenuButton(
child: Text("Global"), // child: Text("Global"),
menuChildren: ["90/X", "90/Y"] // menuChildren: ["90/X", "90/Y"]
.map((rot) => MenuItemButton( // .map((rot) => MenuItemButton(
onPressed: () async { // onPressed: () async {
var transform = rot == "90/X" // var transform = rot == "90/X"
? Matrix4.rotationX(pi / 2) // ? Matrix4.rotationX(pi / 2)
: Matrix4.rotationY(pi / 2); // : Matrix4.rotationY(pi / 2);
await controller // await controller
.removeAnimationComponent( // .removeAnimationComponent(
entity); // entity);
var index = // var index =
boneNames.indexOf(name); // boneNames.indexOf(name);
var childEntities = // var childEntities =
await controller // await controller
.getChildEntities( // .getChildEntities(
entity, true); // entity, true);
for (var child in childEntities) { // for (var child in childEntities) {
print( // print(
"Setting transform for ${await controller.getNameForEntity(child)} / bone $name (index $index)"); // "Setting transform for ${await controller.getNameForEntity(child)} / bone $name (index $index)");
await controller // await controller
.setBoneTransform(child, // .setBoneTransform(child,
index, transform); // index, transform);
} // }
}, // },
child: Text(rot))) // child: Text(rot)))
.toList()), // .toList()),
SubmenuButton( // SubmenuButton(
child: Text("Bone"), // child: Text("Bone"),
menuChildren: ["90/X", "90/Y", "90/Z"] // menuChildren: ["90/X", "90/Y", "90/Z"]
.map((rot) => MenuItemButton( // .map((rot) => MenuItemButton(
onPressed: () async { // onPressed: () async {
await controller // await controller
.removeAnimationComponent( // .removeAnimationComponent(
entity); // entity);
var index = // var index =
boneNames.indexOf(name); // boneNames.indexOf(name);
var boneEntity = await controller // var boneEntity = await controller
.getBone(entity, index); // .getBone(entity, index);
var rotation = rot == "90/X" // var rotation = rot == "90/X"
? Matrix4.rotationX(pi / 2) // ? Matrix4.rotationX(pi / 2)
: rot == "90/Y" // : rot == "90/Y"
? Matrix4.rotationY( // ? Matrix4.rotationY(
pi / 2) // pi / 2)
: Matrix4.rotationZ( // : Matrix4.rotationZ(
pi / 2); // pi / 2);
var inverseBindMatrix = // var inverseBindMatrix =
await controller // await controller
.getInverseBindMatrix( // .getInverseBindMatrix(
entity, index); // entity, index);
var bindMatrix = // var bindMatrix =
Matrix4.identity(); // Matrix4.identity();
bindMatrix.copyInverse( // bindMatrix.copyInverse(
inverseBindMatrix); // inverseBindMatrix);
var childEntities = // var childEntities =
await controller // await controller
.getChildEntities( // .getChildEntities(
entity, true); // entity, true);
for (var child in childEntities) { // for (var child in childEntities) {
var childGlobalTransform = // var childGlobalTransform =
await controller // await controller
.getWorldTransform( // .getWorldTransform(
child); // child);
var inverseGlobalTransform = // var inverseGlobalTransform =
Matrix4.identity(); // Matrix4.identity();
inverseGlobalTransform // inverseGlobalTransform
.copyInverse( // .copyInverse(
childGlobalTransform); // childGlobalTransform);
var globalBoneTransform = // var globalBoneTransform =
childGlobalTransform * // childGlobalTransform *
bindMatrix; // bindMatrix;
var transform = // var transform =
(inverseGlobalTransform * // (inverseGlobalTransform *
(globalBoneTransform * // (globalBoneTransform *
rotation)) * // rotation)) *
inverseBindMatrix; // inverseBindMatrix;
await controller // await controller
.setBoneTransform(child, // .setBoneTransform(child,
index, transform); // index, transform);
} // }
}, // },
child: Text(rot))) // child: Text(rot)))
.toList()), // .toList()),
]), // ]),
MenuItemButton( // MenuItemButton(
onPressed: () => _addBoneAnimation(name), // onPressed: () => _addBoneAnimation(name),
child: Text( // child: Text(
"Test animation (90 degreees around pose Y)")), // "Test animation (90 degreees around pose Y)")),
]); // ]);
}) // })
.cast<Widget>() // .cast<Widget>()
.toList(), // .toList(),
child: Text("Skeleton")); // child: Text("Skeleton"));
}); // });
} // }
} // }
// MenuItemButton( // // MenuItemButton(
// onPressed: () async { // // onPressed: () async {
// var frames = <List<BoneAnimationFrame>>[]; // // var frames = <List<BoneAnimationFrame>>[];
// for (int i = 0; i < 60; i++) { // // for (int i = 0; i < 60; i++) {
// var frame = <BoneAnimationFrame>[]; // // var frame = <BoneAnimationFrame>[];
// frame.add(( // // frame.add((
// rotation: Quaternion.axisAngle( // // rotation: Quaternion.axisAngle(
// Vector3(0, 0, 1), (i / 60) * pi / 2), // // Vector3(0, 0, 1), (i / 60) * pi / 2),
// translation: Vector3.zero() // // translation: Vector3.zero()
// )); // // ));
// frame.add(( // // frame.add((
// rotation: Quaternion.identity(), // // rotation: Quaternion.identity(),
// translation: Vector3.zero() // // translation: Vector3.zero()
// )); // // ));
// frame.add(( // // frame.add((
// rotation: Quaternion.identity(), // // rotation: Quaternion.identity(),
// translation: Vector3.zero() // // translation: Vector3.zero()
// )); // // ));
// frames.add(frame); // // frames.add(frame);
// } // // }
// for (int i = 0; i < 60; i++) { // // for (int i = 0; i < 60; i++) {
// var frame = <BoneAnimationFrame>[]; // // var frame = <BoneAnimationFrame>[];
// frame.add(( // // frame.add((
// rotation: Quaternion.axisAngle( // // rotation: Quaternion.axisAngle(
// Vector3(0, 0, 1), pi / 2), // // Vector3(0, 0, 1), pi / 2),
// translation: Vector3.zero() // // translation: Vector3.zero()
// )); // // ));
// frame.add(( // // frame.add((
// rotation: Quaternion.axisAngle( // // rotation: Quaternion.axisAngle(
// Vector3(1, 0, 0), i / 60 * (-pi / 2)), // // Vector3(1, 0, 0), i / 60 * (-pi / 2)),
// translation: Vector3.zero() // // translation: Vector3.zero()
// )); // // ));
// frame.add(( // // frame.add((
// rotation: Quaternion.identity(), // // rotation: Quaternion.identity(),
// translation: Vector3.zero() // // translation: Vector3.zero()
// )); // // ));
// frames.add(frame); // // frames.add(frame);
// } // // }
// for (int i = 0; i < 60; i++) { // // for (int i = 0; i < 60; i++) {
// var frame = <BoneAnimationFrame>[]; // // var frame = <BoneAnimationFrame>[];
// frame.add(( // // frame.add((
// rotation: Quaternion.axisAngle( // // rotation: Quaternion.axisAngle(
// Vector3(0, 0, 1), pi / 2), // // Vector3(0, 0, 1), pi / 2),
// translation: Vector3.zero() // // translation: Vector3.zero()
// )); // // ));
// frame.add(( // // frame.add((
// rotation: Quaternion.axisAngle( // // rotation: Quaternion.axisAngle(
// Vector3(1, 0, 0), (-pi / 2)), // // Vector3(1, 0, 0), (-pi / 2)),
// translation: Vector3.zero() // // translation: Vector3.zero()
// )); // // ));
// frame.add(( // // frame.add((
// rotation: Quaternion.axisAngle( // // rotation: Quaternion.axisAngle(
// Vector3(1, 0, 0), i / 60 * (pi / 2)), // // Vector3(1, 0, 0), i / 60 * (pi / 2)),
// translation: Vector3.zero() // // translation: Vector3.zero()
// )); // // ));
// frames.add(frame); // // frames.add(frame);
// } // // }
// var animation = BoneAnimationData( // // var animation = BoneAnimationData(
// ["Bone", "Bone.001", "Bone.002"], frames); // // ["Bone", "Bone.001", "Bone.002"], frames);
// await controller.addAnimationComponent(entity); // // await controller.addAnimationComponent(entity);
// await controller.addBoneAnimation(entity, animation); // // await controller.addBoneAnimation(entity, animation);
// }, // // },
// child: Text("Test animation (pose space)")), // // child: Text("Test animation (pose space)")),

View File

@@ -174,6 +174,7 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
} }
WidgetsBinding.instance.scheduleFrameCallback((d) async { WidgetsBinding.instance.scheduleFrameCallback((d) async {
if (!mounted) { if (!mounted) {
return; return;
} }
@@ -241,7 +242,7 @@ class _ThermionTextureWidgetState extends State<ThermionTextureWidget> {
_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.viewer.view.setViewport(_texture!.width, _texture!.height); await widget.viewer.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()),

View File

@@ -11,6 +11,10 @@ public class ThermionFlutterTexture : NSObject, FlutterTexture {
init(registry:FlutterTextureRegistry, width:Int64, height:Int64) { init(registry:FlutterTextureRegistry, width:Int64, height:Int64) {
self.registry = registry self.registry = registry
self.texture = ThermionTextureSwift(width:width, height: height, isDepth: false) self.texture = ThermionTextureSwift(width:width, height: height, isDepth: false)
// if you need to debug
// self.texture.fillColor()
// let imageURL = URL(fileURLWithPath: "/Users/nickfisher/Documents/thermion/thermion_dart/test/assets/cube_texture_512x512.png")
// let success = self.texture.fillWithPNGImage(imageURL: imageURL)
super.init() super.init()
self.flutterTextureId = registry.register(self) self.flutterTextureId = registry.register(self)
} }

View File

@@ -0,0 +1 @@
../../../..//thermion_dart/native/macos/ThermionTexture.swift

View File

@@ -18,7 +18,7 @@ import 'package:thermion_flutter_platform_interface/thermion_flutter_texture.dar
/// ///
class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform { class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform {
final channel = const MethodChannel("dev.thermion.flutter/event"); final channel = const MethodChannel("dev.thermion.flutter/event");
late final _logger = Logger(this.runtimeType.toString()); late final _logger = Logger(this.runtimeType.toString());
static SwapChain? _swapChain; static SwapChain? _swapChain;
@@ -37,9 +37,10 @@ class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform {
return File(path.replaceAll("file://", "")).readAsBytesSync(); return File(path.replaceAll("file://", "")).readAsBytesSync();
} }
if (path.startsWith("asset://")) { if (path.startsWith("asset://")) {
throw UnimplementedError(); path = path.replaceAll("asset://", "");
} }
throw UnimplementedError(); var asset = await rootBundle.load(path);
return asset.buffer.asUint8List(asset.offsetInBytes);
} }
Future<ThermionViewer> createViewer({ThermionFlutterOptions? options}) async { Future<ThermionViewer> createViewer({ThermionFlutterOptions? options}) async {
@@ -92,18 +93,19 @@ class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform {
throw Exception("Unsupported platform"); throw Exception("Unsupported platform");
} }
} }
final config = FFIFilamentConfig( final config = FFIFilamentConfig(
backend: backend, backend: backend,
resourceLoader: resourceLoader, resourceLoader: loadAsset,
driver: driverPtr, driver: driverPtr,
platform: nullptr, platform: nullptr,
sharedContext: sharedContextPtr, sharedContext: sharedContextPtr,
uberArchivePath: options?.uberarchivePath); uberArchivePath: options?.uberarchivePath);
await FFIFilamentApp.create(config); await FFIFilamentApp.create(config: config);
final viewer = ThermionViewerFFI( final viewer = ThermionViewerFFI(
loadAsset: loadAsset, loadAssetFromUri: loadAsset,
); );
await viewer.initialized; await viewer.initialized;
@@ -117,9 +119,10 @@ class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform {
// avoid this // avoid this
if (Platform.isMacOS || Platform.isIOS) { if (Platform.isMacOS || Platform.isIOS) {
_swapChain = await FilamentApp.instance!.createHeadlessSwapChain(1, 1); _swapChain = await FilamentApp.instance!.createHeadlessSwapChain(1, 1);
await FilamentApp.instance!.register(_swapChain!, viewer.view);
} }
return viewer!; return viewer;
} }
Future<PlatformTextureDescriptor> createTextureDescriptor( Future<PlatformTextureDescriptor> createTextureDescriptor(
@@ -157,27 +160,36 @@ class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform {
_swapChain = await FilamentApp.instance! _swapChain = await FilamentApp.instance!
.createHeadlessSwapChain(descriptor.width, descriptor.height); .createHeadlessSwapChain(descriptor.width, descriptor.height);
await FilamentApp.instance!.register(_swapChain!, view);
} else if (Platform.isAndroid) { } else if (Platform.isAndroid) {
if (_swapChain != null) { if (_swapChain != null) {
await FilamentApp.instance!.setRenderable(view, false); await FilamentApp.instance!.setRenderable(view, false);
await FilamentApp.instance!.destroySwapChain(_swapChain!); await FilamentApp.instance!.destroySwapChain(_swapChain!);
} }
_swapChain = await FilamentApp.instance!.createSwapChain(descriptor.windowHandle!); _swapChain =
await FilamentApp.instance!.createSwapChain(descriptor.windowHandle!);
await FilamentApp.instance!.register(_swapChain!, view);
} else { } else {
final color = await FilamentApp.instance!.createTexture( final color = await FilamentApp.instance!
descriptor.width, descriptor.height, .createTexture(descriptor.width, descriptor.height,
importedTextureHandle: descriptor.hardwareId, importedTextureHandle: descriptor.hardwareId,
flags: { flags: {
TextureUsage.TEXTURE_USAGE_BLIT_DST, // TextureUsage.TEXTURE_USAGE_BLIT_DST,
TextureUsage.TEXTURE_USAGE_COLOR_ATTACHMENT, TextureUsage.TEXTURE_USAGE_COLOR_ATTACHMENT,
TextureUsage.TEXTURE_USAGE_SAMPLEABLE TextureUsage.TEXTURE_USAGE_SAMPLEABLE
}); },
final depth = textureFormat: TextureFormat.RGBA8,
await FilamentApp.instance!.createTexture(descriptor.width, descriptor.height, flags: { textureSamplerType: TextureSamplerType.SAMPLER_2D);
TextureUsage.TEXTURE_USAGE_BLIT_DST, final depth = await FilamentApp.instance!
TextureUsage.TEXTURE_USAGE_DEPTH_ATTACHMENT, .createTexture(descriptor.width, descriptor.height,
TextureUsage.TEXTURE_USAGE_SAMPLEABLE flags: {
}); // TextureUsage.TEXTURE_USAGE_BLIT_DST,
TextureUsage.TEXTURE_USAGE_DEPTH_ATTACHMENT,
TextureUsage.TEXTURE_USAGE_SAMPLEABLE,
},
textureFormat: TextureFormat.DEPTH32F,
textureSamplerType: TextureSamplerType.SAMPLER_2D);
var renderTarget = await FilamentApp.instance!.createRenderTarget( var renderTarget = await FilamentApp.instance!.createRenderTarget(
descriptor.width, descriptor.height, descriptor.width, descriptor.height,
@@ -185,7 +197,6 @@ class ThermionFlutterMethodChannelPlatform extends ThermionFlutterPlatform {
await view.setRenderTarget(renderTarget); await view.setRenderTarget(renderTarget);
} }
await FilamentApp.instance!.register(_swapChain!, view);
return descriptor; return descriptor;
} }

View File

@@ -1,19 +1,17 @@
import 'dart:async'; import 'dart:async';
import 'package:thermion_dart/thermion_dart.dart' as t;
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'package:thermion_dart/thermion_dart.dart'; import 'package:thermion_dart/thermion_dart.dart';
import 'package:thermion_dart/src/filament/filament.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'thermion_flutter_texture.dart'; import 'thermion_flutter_texture.dart';
class ThermionFlutterOptions { class ThermionFlutterOptions {
final String? uberarchivePath; final String? uberarchivePath;
final Backend? backend;
ThermionFlutterOptions({this.uberarchivePath}); const ThermionFlutterOptions({this.uberarchivePath = null, this.backend = null});
const ThermionFlutterOptions.empty() : uberarchivePath = null;
} }
abstract class ThermionFlutterPlatform extends PlatformInterface { abstract class ThermionFlutterPlatform extends PlatformInterface {
ThermionFlutterPlatform() : super(token: _token); ThermionFlutterPlatform() : super(token: _token);
@@ -34,12 +32,13 @@ abstract class ThermionFlutterPlatform extends PlatformInterface {
{covariant ThermionFlutterOptions? options}); {covariant ThermionFlutterOptions? options});
/// ///
/// Creates a raw rendering surface. /// Creates a raw rendering surface.
/// ///
/// This is internal; unless you are [thermion_*] package developer, don't /// This is internal; unless you are [thermion_*] package developer, don't
/// call this yourself. May not be supported on all platforms. /// call this yourself. May not be supported on all platforms.
/// ///
Future<PlatformTextureDescriptor> createTextureDescriptor(int width, int height); Future<PlatformTextureDescriptor> createTextureDescriptor(
int width, int height);
/// ///
/// Destroys a raw rendering surface. /// Destroys a raw rendering surface.
@@ -53,15 +52,14 @@ abstract class ThermionFlutterPlatform extends PlatformInterface {
/// call this yourself. May not be supported on all platforms. /// call this yourself. May not be supported on all platforms.
/// ///
Future<PlatformTextureDescriptor?> createTextureAndBindToView( Future<PlatformTextureDescriptor?> createTextureAndBindToView(
t.View view, int width, int height); View view, int width, int height);
/// ///
/// ///
/// ///
/// ///
Future<PlatformTextureDescriptor?> resizeTexture( Future<PlatformTextureDescriptor?> resizeTexture(
PlatformTextureDescriptor texture, t.View view, int width, int height); PlatformTextureDescriptor texture, View view, int width, int height);
/// ///
/// ///

View File

@@ -31,7 +31,7 @@ class ThermionFlutterWebPlugin extends ThermionFlutterPlatform {
(offsetTop * pixelRatio).ceil().toString(); (offsetTop * pixelRatio).ceil().toString();
_viewer! _viewer!
.updateViewportAndCameraProjection(width.ceil(), height.ceil(), 1.0); .setViewportAndCameraProjection(width.ceil(), height.ceil(), 1.0);
return PlatformTextureDescriptor(null, null, 0, 0, null); return PlatformTextureDescriptor(null, null, 0, 0, null);
} }
@@ -53,7 +53,7 @@ class ThermionFlutterWebPlugin extends ThermionFlutterPlatform {
(offsetLeft * pixelRatio).ceil().toString(); (offsetLeft * pixelRatio).ceil().toString();
(canvas as HTMLElement).style.top = (canvas as HTMLElement).style.top =
(offsetTop * pixelRatio).ceil().toString(); (offsetTop * pixelRatio).ceil().toString();
_viewer!.updateViewportAndCameraProjection(width, height, 1.0); _viewer!.setViewportAndCameraProjection(width, height, 1.0);
return PlatformTextureDescriptor(null, null, 0, 0, null); return PlatformTextureDescriptor(null, null, 0, 0, null);
} }

View File

@@ -11,9 +11,5 @@ class ThermionFlutterWebOptions extends ThermionFlutterOptions {
String? uberarchivePath}) String? uberarchivePath})
: super(uberarchivePath: uberarchivePath); : super(uberarchivePath: uberarchivePath);
const ThermionFlutterWebOptions.empty(
{this.importCanvasAsWidget = false,
this.createCanvas = true,
String? uberarchivePath})
: super.empty();
} }