diff --git a/thermion_dart/lib/src/filament/src/filament_app.dart b/thermion_dart/lib/src/filament/src/filament_app.dart index 03cc4dd5..5f17b5ed 100644 --- a/thermion_dart/lib/src/filament/src/filament_app.dart +++ b/thermion_dart/lib/src/filament/src/filament_app.dart @@ -219,5 +219,18 @@ abstract class FilamentApp { Future setParent(ThermionEntity child, ThermionEntity? parent, {bool preserveScaling}); + /// + /// + /// Future createImageMaterialInstance(); + + /// + /// + /// + Future capture(covariant View view); + + /// + /// + /// + Future setClearColor(double r, double g, double b, double a); } diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/background_image.dart b/thermion_dart/lib/src/viewer/src/ffi/src/background_image.dart index b283eb16..a4574428 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/background_image.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/background_image.dart @@ -14,41 +14,54 @@ class BackgroundImage extends ThermionAsset { ThermionEntity get entity => asset.entity; - Texture? _backgroundImageTexture; + Texture? texture; - FFITextureSampler? _imageSampler; + FFITextureSampler? sampler; + + final MaterialInstance mi; final FFIScene scene; BackgroundImage._( - this.asset, this.scene, this._backgroundImageTexture, this._imageSampler); + this.asset, this.scene, this.texture, this.sampler, this.mi); Future destroy() async { Scene_removeEntity(scene.scene, entity); - await _backgroundImageTexture!.dispose(); - await _imageSampler!.dispose(); + + await texture!.dispose(); + await sampler!.dispose(); + await mi.destroy(); } static Future create( - ThermionViewer viewer, FFIScene scene, Uint8List imageData) async { - final image = await FilamentApp.instance!.decodeImage(imageData); - var backgroundImageTexture = await FilamentApp.instance! - .createTexture(await image.getWidth(), await image.getHeight()); - - var imageSampler = - await FilamentApp.instance!.createTextureSampler() as FFITextureSampler; - + ThermionViewer viewer, FFIScene scene) async { var imageMaterialInstance = await FilamentApp.instance!.createImageMaterialInstance(); - await imageMaterialInstance.setParameterTexture( - "image", backgroundImageTexture as FFITexture, imageSampler); var backgroundImage = await viewer.createGeometry(GeometryHelper.fullscreenQuad()); backgroundImage.setMaterialInstanceAt(imageMaterialInstance); await scene.add(backgroundImage as FFIAsset); return BackgroundImage._( - backgroundImage, scene, backgroundImageTexture, imageSampler); + backgroundImage, scene, null, null, imageMaterialInstance); + } + + Future setBackgroundColor(double r, double g, double b, double a) async { + await mi.setParameterFloat4("backgroundColor", r, g, b, a); + } + + Future setImage(Uint8List imageData) async { + final image = await FilamentApp.instance!.decodeImage(imageData); + + texture ??= await FilamentApp.instance! + .createTexture(await image.getWidth(), await image.getHeight()); + + sampler ??= + await FilamentApp.instance!.createTextureSampler() as FFITextureSampler; + + await mi.setParameterTexture( + "image", texture as FFITexture, sampler as FFITextureSampler); + } /// @@ -263,7 +276,7 @@ class BackgroundImage extends ThermionAsset { } @override - Future setTransform(Matrix4 transform, { ThermionEntity? entity }) { + Future setTransform(Matrix4 transform, {ThermionEntity? entity}) { // TODO: implement setTransform throw UnimplementedError(); } diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/ffi_filament_app.dart b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_filament_app.dart index 0f8b5670..c73c4195 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/ffi_filament_app.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/ffi_filament_app.dart @@ -541,10 +541,52 @@ class FFIFilamentApp extends FilamentApp { /// @override Future createImageMaterialInstance() async { - _imageMaterial ??= FFIMaterial(Material_createImageMaterial(engine), - FilamentApp.instance! as FFIFilamentApp); + if (_imageMaterial == null) { + var ptr = await withPointerCallback( + (cb) => Material_createImageMaterialRenderThread(engine, cb)); + _imageMaterial = + FFIMaterial(ptr, FilamentApp.instance! as FFIFilamentApp); + } var instance = await _imageMaterial!.createInstance() as FFIMaterialInstance; return instance; } + + /// + /// + /// + Future capture(covariant FFIView view) async { + final viewport = await view.getViewport(); + final swapChain = _viewMappings[view]; + final out = Uint8List(viewport.width * viewport.height * 4); + + await withBoolCallback((cb) { + Renderer_beginFrameRenderThread(renderer, swapChain!.swapChain, 0, cb); + }); + await withVoidCallback((cb) { + Renderer_readPixelsRenderThread( + renderer, + view.view, + view.renderTarget?.renderTarget ?? nullptr, + TPixelDataFormat.PIXELDATAFORMAT_RGBA, + TPixelDataType.PIXELDATATYPE_UBYTE, + out.address, + cb, + ); + }); + await withVoidCallback((cb) { + Renderer_endFrameRenderThread(renderer, cb); + }); + await withVoidCallback((cb) { + Engine_flushAndWaitRenderThead(engine, cb); + }); + return out; + } + + /// + /// + /// + Future setClearColor(double r, double g, double b, double a) async { + Renderer_setClearOptions(renderer, r, g, b, a, 0, true, false); + } } diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart index 8ed8f10a..2be08ff5 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_dart.g.dart @@ -1907,6 +1907,19 @@ external void Material_createInstanceRenderThread( onComplete, ); +@ffi.Native< + ffi.Void Function( + ffi.Pointer, + ffi.Pointer< + ffi + .NativeFunction)>>)>( + isLeaf: true) +external void Material_createImageMaterialRenderThread( + ffi.Pointer tEngine, + ffi.Pointer)>> + onComplete, +); + @ffi.Native< ffi.Void Function( ffi.Pointer, ffi.Pointer, ffi.UnsignedInt)>( @@ -1952,280 +1965,38 @@ external FilamentRenderCallback make_render_callback_fn_pointer( ); @ffi.Native< - ffi.Void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer< - ffi - .NativeFunction)>>)>( - isLeaf: true) -external void SceneManager_createGridRenderThread( - ffi.Pointer tSceneManager, - ffi.Pointer tMaterial, - ffi.Pointer)>> - callback, -); - -@ffi.Native< - ffi.Pointer Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.UnsignedInt, - ffi.Pointer< - ffi.NativeFunction)>>)>( - symbol: "SceneManager_createGizmoRenderThread", isLeaf: true) -external ffi.Pointer _SceneManager_createGizmoRenderThread( - ffi.Pointer tSceneManager, - ffi.Pointer tView, - ffi.Pointer tScene, - int tGizmoType, - ffi.Pointer)>> - onComplete, -); - -ffi.Pointer SceneManager_createGizmoRenderThread( - ffi.Pointer tSceneManager, - ffi.Pointer tView, - ffi.Pointer tScene, - TGizmoType tGizmoType, - ffi.Pointer)>> - onComplete, -) => - _SceneManager_createGizmoRenderThread( - tSceneManager, - tView, - tScene, - tGizmoType.value, - onComplete, - ); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Int, - ffi.Pointer, - ffi.Int, - ffi.Pointer, - ffi.Int, - ffi.Pointer, - ffi.Int, - ffi.Int, - ffi.Pointer>, - ffi.Int, - ffi.Bool, - ffi.Pointer< - ffi - .NativeFunction)>>)>( - isLeaf: true) -external void SceneManager_createGeometryRenderThread( - ffi.Pointer sceneManager, - ffi.Pointer vertices, - int numVertices, - ffi.Pointer normals, - int numNormals, - ffi.Pointer uvs, - int numUvs, - ffi.Pointer indices, - int numIndices, - int primitiveType, - ffi.Pointer> materialInstances, - int materialInstanceCount, - bool keepData, - ffi.Pointer)>> - callback, + ffi.Void Function(ffi.Pointer, + ffi.Pointer>)>(isLeaf: true) +external void SceneAsset_destroyRenderThread( + ffi.Pointer tSceneAsset, + ffi.Pointer> onComplete, ); @ffi.Native< ffi.Void Function( - ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, ffi.Pointer, ffi.Size, - ffi.Int, - ffi.Bool, - ffi.Int, - ffi.Int, - ffi.Bool, + ffi.Size, ffi.Pointer< ffi .NativeFunction)>>)>( isLeaf: true) -external void SceneManager_loadGlbFromBufferRenderThread( - ffi.Pointer sceneManager, +external void SceneAsset_loadGlbRenderThread( + ffi.Pointer tAssetLoader, + ffi.Pointer tResourceLoader, + ffi.Pointer tEngine, + ffi.Pointer tNameComponentManager, ffi.Pointer data, int length, int numInstances, - bool keepData, - int priority, - int layer, - bool loadResourcesAsync, ffi.Pointer)>> callback, ); -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer)>>)>(isLeaf: true) -external void SceneManager_createUnlitMaterialInstanceRenderThread( - ffi.Pointer sceneManager, - ffi.Pointer< - ffi.NativeFunction)>> - callback, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - ffi.Pointer< - ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer)>>)>(isLeaf: true) -external void SceneManager_createUnlitFixedSizeMaterialInstanceRenderThread( - ffi.Pointer sceneManager, - ffi.Pointer< - ffi.NativeFunction)>> - callback, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Int, - ffi.Bool, - ffi.Pointer< - ffi - .NativeFunction)>>)>( - isLeaf: true) -external void SceneManager_loadGlbRenderThread( - ffi.Pointer sceneManager, - ffi.Pointer assetPath, - int numInstances, - bool keepData, - ffi.Pointer)>> - callback, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - ffi.Pointer, - ffi.Pointer, - ffi.Bool, - ffi.Pointer< - ffi - .NativeFunction)>>)>( - isLeaf: true) -external void SceneManager_loadGltfRenderThread( - ffi.Pointer sceneManager, - ffi.Pointer assetPath, - ffi.Pointer relativePath, - bool keepData, - ffi.Pointer)>> - callback, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, - ffi.Pointer>)>(isLeaf: true) -external void SceneManager_destroyAllRenderThread( - ffi.Pointer tSceneManager, - ffi.Pointer> callback, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer>)>(isLeaf: true) -external void SceneManager_destroyAssetRenderThread( - ffi.Pointer tSceneManager, - ffi.Pointer sceneAsset, - ffi.Pointer> callback, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, - ffi.Pointer>)>(isLeaf: true) -external void SceneManager_destroyAssetsRenderThread( - ffi.Pointer tSceneManager, - ffi.Pointer> callback, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, - ffi.Pointer>)>(isLeaf: true) -external void SceneManager_destroyLightsRenderThread( - ffi.Pointer tSceneManager, - ffi.Pointer> callback, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - ffi.Uint8, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Bool, - ffi.Pointer>)>( - isLeaf: true) -external void SceneManager_addLightRenderThread( - ffi.Pointer tSceneManager, - int type, - double colour, - double intensity, - double posX, - double posY, - double posZ, - double dirX, - double dirY, - double dirZ, - double falloffRadius, - double spotLightConeInner, - double spotLightConeOuter, - double sunAngularRadius, - double sunHaloSize, - double sunHaloFallof, - bool shadows, - ffi.Pointer> callback, -); - -@ffi.Native< - ffi.Void Function(ffi.Pointer, EntityId, - ffi.Pointer>)>(isLeaf: true) -external void SceneManager_removeLightRenderThread( - ffi.Pointer tSceneManager, - int entityId, - ffi.Pointer> callback, -); - -@ffi.Native< - ffi.Void Function( - ffi.Pointer, - ffi.Pointer< - ffi.NativeFunction)>>)>( - isLeaf: true) -external void SceneManager_createCameraRenderThread( - ffi.Pointer tSceneManager, - ffi.Pointer)>> - callback, -); - @ffi.Native< ffi.Void Function( ffi.Pointer, diff --git a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart index dd04e707..69da56ae 100644 --- a/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart +++ b/thermion_dart/lib/src/viewer/src/ffi/src/thermion_viewer_ffi.dart @@ -129,7 +129,8 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future render() async { - await withVoidCallback((cb) => RenderTicker_renderRenderThread(app.renderTicker, 0, cb)); + await withVoidCallback( + (cb) => RenderTicker_renderRenderThread(app.renderTicker, 0, cb)); } double _msPerFrame = 1000.0 / 60.0; @@ -191,7 +192,8 @@ class ThermionViewerFFI extends ThermionViewer { @override Future setBackgroundImage(String path, {bool fillHeight = false}) async { final imageData = await loadAsset(path); - _backgroundImage = await BackgroundImage.create(this, scene, imageData); + _backgroundImage ??= await BackgroundImage.create(this, scene); + await _backgroundImage!.setImage(imageData); } /// @@ -199,7 +201,13 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future setBackgroundColor(double r, double g, double b, double a) async { - throw UnimplementedError(); + // we don't want to use the Renderer clearColor, because this only applies + // to clearing the swapchain. Even if this Viewer is rendered into the + // swapchain, we don't necessarily (?) want to set the clear color, + // because that will affect other views. + // We therefore use the background image as the color; + _backgroundImage ??= await BackgroundImage.create(this, scene); + await _backgroundImage!.setBackgroundColor(r, g, b, a); } /// @@ -318,8 +326,8 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future addDirectLight(DirectLight directLight) async { - var entity = LightManager_createLight(app.engine, - app.lightManager, TLightType.values[directLight.type.index]); + var entity = LightManager_createLight(app.engine, app.lightManager, + TLightType.values[directLight.type.index]); if (entity == FILAMENT_ASSET_ERROR) { throw Exception("Failed to add light to scene"); } @@ -391,14 +399,16 @@ class ThermionViewerFFI extends ThermionViewer { int priority = 4, int layer = 0, bool loadResourcesAsync = false}) async { - var asset = SceneAsset_loadGlb( - app.gltfAssetLoader, - app.gltfResourceLoader, - app.engine, - app.nameComponentManager, - data.address, - data.length, - numInstances); + var asset = await withPointerCallback((cb) => + SceneAsset_loadGlbRenderThread( + app.gltfAssetLoader, + app.gltfResourceLoader, + app.engine, + app.nameComponentManager, + data.address, + data.length, + numInstances, + cb)); if (asset == nullptr) { throw Exception("An error occurred loading the asset"); @@ -440,7 +450,9 @@ class ThermionViewerFFI extends ThermionViewer { @override Future destroyAsset(covariant FFIAsset asset) async { await scene.remove(asset); - SceneAsset_destroy(asset.asset); + + await withVoidCallback((cb) => SceneAsset_destroyRenderThread(asset.asset, cb)); + // if (asset.boundingBoxAsset != null) { // await asset.setBoundingBoxVisibility(false); // await withVoidCallback((callback) => @@ -466,7 +478,7 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future setToneMapping(ToneMapper mapper) async { - view.setToneMapper(mapper); + await view.setToneMapper(mapper); } /// @@ -774,4 +786,5 @@ class ThermionViewerFFI extends ThermionViewer { // gizmoEntities.toSet() // ..add(SceneAsset_getEntity(gizmo.cast()))); } + } diff --git a/thermion_dart/native/include/c_api/ThermionDartRenderThreadApi.h b/thermion_dart/native/include/c_api/ThermionDartRenderThreadApi.h index 7aeaf875..d5dfb0d2 100644 --- a/thermion_dart/native/include/c_api/ThermionDartRenderThreadApi.h +++ b/thermion_dart/native/include/c_api/ThermionDartRenderThreadApi.h @@ -75,6 +75,7 @@ namespace thermion void (*onComplete)()); EMSCRIPTEN_KEEPALIVE void Material_createInstanceRenderThread(TMaterial *tMaterial, void (*onComplete)(TMaterialInstance *)); + EMSCRIPTEN_KEEPALIVE void Material_createImageMaterialRenderThread(TEngine *tEngine, void (*onComplete)(TMaterial *)); EMSCRIPTEN_KEEPALIVE void View_setToneMappingRenderThread(TView *tView, TEngine *tEngine, TToneMapping toneMapping); EMSCRIPTEN_KEEPALIVE void View_setBloomRenderThread(TView *tView, bool enabled, double strength); @@ -82,60 +83,17 @@ namespace thermion FilamentRenderCallback make_render_callback_fn_pointer(FilamentRenderCallback); - EMSCRIPTEN_KEEPALIVE void SceneManager_createGridRenderThread(TSceneManager *tSceneManager, TMaterial *tMaterial, void (*callback)(TSceneAsset *)); - - EMSCRIPTEN_KEEPALIVE TGizmo *SceneManager_createGizmoRenderThread( - TSceneManager *tSceneManager, - TView *tView, - TScene *tScene, - TGizmoType tGizmoType, - void (*onComplete)(TGizmo *)); - - EMSCRIPTEN_KEEPALIVE void SceneManager_createGeometryRenderThread( - TSceneManager *sceneManager, - float *vertices, - int numVertices, - float *normals, - int numNormals, - float *uvs, - int numUvs, - uint16_t *indices, - int numIndices, - int primitiveType, - TMaterialInstance **materialInstances, - int materialInstanceCount, - bool keepData, - void (*callback)(TSceneAsset *)); - EMSCRIPTEN_KEEPALIVE void SceneManager_loadGlbFromBufferRenderThread(TSceneManager *sceneManager, const uint8_t *const data, size_t length, int numInstances, bool keepData, int priority, int layer, bool loadResourcesAsync, void (*callback)(TSceneAsset *)); - EMSCRIPTEN_KEEPALIVE void SceneManager_createUnlitMaterialInstanceRenderThread(TSceneManager *sceneManager, void (*callback)(TMaterialInstance *)); - EMSCRIPTEN_KEEPALIVE void SceneManager_createUnlitFixedSizeMaterialInstanceRenderThread(TSceneManager *sceneManager, void (*callback)(TMaterialInstance *)); - EMSCRIPTEN_KEEPALIVE void SceneManager_loadGlbRenderThread(TSceneManager *sceneManager, const char *assetPath, int numInstances, bool keepData, void (*callback)(TSceneAsset *)); - EMSCRIPTEN_KEEPALIVE void SceneManager_loadGltfRenderThread(TSceneManager *sceneManager, const char *assetPath, const char *relativePath, bool keepData, void (*callback)(TSceneAsset *)); - EMSCRIPTEN_KEEPALIVE void SceneManager_destroyAllRenderThread(TSceneManager *tSceneManager, void (*callback)()); - EMSCRIPTEN_KEEPALIVE void SceneManager_destroyAssetRenderThread(TSceneManager *tSceneManager, TSceneAsset *sceneAsset, void (*callback)()); - EMSCRIPTEN_KEEPALIVE void SceneManager_destroyAssetsRenderThread(TSceneManager *tSceneManager, void (*callback)()); - EMSCRIPTEN_KEEPALIVE void SceneManager_destroyLightsRenderThread(TSceneManager *tSceneManager, void (*callback)()); - EMSCRIPTEN_KEEPALIVE void SceneManager_addLightRenderThread( - TSceneManager *tSceneManager, - uint8_t type, - float colour, - float intensity, - float posX, - float posY, - float posZ, - float dirX, - float dirY, - float dirZ, - float falloffRadius, - float spotLightConeInner, - float spotLightConeOuter, - float sunAngularRadius, - float sunHaloSize, - float sunHaloFallof, - bool shadows, - void (*callback)(EntityId)); - EMSCRIPTEN_KEEPALIVE void SceneManager_removeLightRenderThread(TSceneManager *tSceneManager, EntityId entityId, void (*callback)()); - EMSCRIPTEN_KEEPALIVE void SceneManager_createCameraRenderThread(TSceneManager *tSceneManager, void (*callback)(TCamera *)); + EMSCRIPTEN_KEEPALIVE void SceneAsset_destroyRenderThread(TSceneAsset *tSceneAsset, void (*onComplete)()); + EMSCRIPTEN_KEEPALIVE void SceneAsset_loadGlbRenderThread( + TGltfAssetLoader *tAssetLoader, + TGltfResourceLoader *tResourceLoader, + TEngine *tEngine, + TNameComponentManager *tNameComponentManager, + uint8_t *data, + size_t length, + size_t numInstances, + void (*callback)(TSceneAsset *) + ); EMSCRIPTEN_KEEPALIVE void SceneAsset_createInstanceRenderThread(TSceneAsset *asset, TMaterialInstance **tMaterialInstances, int materialInstanceCount, void (*callback)(TSceneAsset *)); EMSCRIPTEN_KEEPALIVE void SceneAsset_createGeometryRenderThread( TEngine *tEngine, diff --git a/thermion_dart/native/src/c_api/ThermionDartRenderThreadApi.cpp b/thermion_dart/native/src/c_api/ThermionDartRenderThreadApi.cpp index ed80f706..3ecaa434 100644 --- a/thermion_dart/native/src/c_api/ThermionDartRenderThreadApi.cpp +++ b/thermion_dart/native/src/c_api/ThermionDartRenderThreadApi.cpp @@ -319,6 +319,16 @@ extern "C" auto fut = _rl->add_task(lambda); } + EMSCRIPTEN_KEEPALIVE void Material_createImageMaterialRenderThread(TEngine *tEngine, void (*onComplete)(TMaterial *)) { + std::packaged_task lambda( + [=]() mutable + { + auto *instance = Material_createImageMaterial(tEngine); + onComplete(instance); + }); + auto fut = _rl->add_task(lambda); + } + EMSCRIPTEN_KEEPALIVE void Material_createInstanceRenderThread(TMaterial *tMaterial, void (*onComplete)(TMaterialInstance *)) { std::packaged_task lambda( @@ -330,6 +340,34 @@ extern "C" auto fut = _rl->add_task(lambda); } + EMSCRIPTEN_KEEPALIVE void SceneAsset_destroyRenderThread(TSceneAsset *tSceneAsset, void (*onComplete)()) { + std::packaged_task lambda( + [=]() mutable + { + SceneAsset_destroy(tSceneAsset); + onComplete(); + }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void SceneAsset_loadGlbRenderThread( + TGltfAssetLoader *tAssetLoader, + TGltfResourceLoader *tResourceLoader, + TEngine *tEngine, + TNameComponentManager *tNameComponentManager, + uint8_t *data, + size_t length, + size_t numInstances, + void (*callback)(TSceneAsset *) + ) { + std::packaged_task lambda( + [=] + { + auto sceneAsset = SceneAsset_loadGlb(tAssetLoader, tResourceLoader, tEngine, tNameComponentManager, data, length, numInstances); + callback(sceneAsset); + }); + auto fut = _rl->add_task(lambda); + } EMSCRIPTEN_KEEPALIVE void SceneAsset_createGeometryRenderThread( TEngine *tEngine,