From ab1de78b74bfd5406619d5cc379cb58116a1019b Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Sat, 21 Sep 2024 10:21:46 +0800 Subject: [PATCH] rename from *FFI to *RenderThread, return bool from render() to check frame timings, update render loop to wait on condition variable, add requestFrame() method --- thermion_dart/ffigen/native.yaml | 4 +- .../viewer/ffi/thermion_dart.g.dart | 114 ++- .../viewer/ffi/thermion_viewer_ffi.dart | 150 +-- .../viewer/thermion_viewer_base.dart | 9 +- .../viewer/thermion_viewer_stub.dart | 16 +- .../native/include/FilamentViewer.hpp | 2 +- .../native/include/ThermionDartApi.h | 4 +- .../native/include/ThermionDartFFIApi.h | 125 --- .../include/ThermionDartRenderThreadApi.h | 126 +++ thermion_dart/native/src/FilamentViewer.cpp | 5 +- thermion_dart/native/src/ThermionDartApi.cpp | 8 +- .../native/src/ThermionDartFFIApi.cpp | 875 ------------------ .../src/ThermionDartRenderThreadApi.cpp | 847 +++++++++++++++++ thermion_dart/test/integration_test.dart | 20 +- 14 files changed, 1170 insertions(+), 1135 deletions(-) delete mode 100644 thermion_dart/native/include/ThermionDartFFIApi.h create mode 100644 thermion_dart/native/include/ThermionDartRenderThreadApi.h delete mode 100644 thermion_dart/native/src/ThermionDartFFIApi.cpp create mode 100644 thermion_dart/native/src/ThermionDartRenderThreadApi.cpp diff --git a/thermion_dart/ffigen/native.yaml b/thermion_dart/ffigen/native.yaml index 2062f1ab..67ed918f 100644 --- a/thermion_dart/ffigen/native.yaml +++ b/thermion_dart/ffigen/native.yaml @@ -1,11 +1,11 @@ output: '../lib/thermion_dart/viewer/ffi/thermion_dart.g.dart' headers: entry-points: - - '../native/include/ThermionDartFFIApi.h' + - '../native/include/ThermionDartRenderThreadApi.h' - '../native/include/ThermionDartApi.h' - '../native/include/ResourceBuffer.h' include-directives: - - '../native/include/ThermionDartFFIApi.h' + - '../native/include/ThermionDartRenderThreadApi.h' - '../native/include/ThermionDartApi.h' - '../native/include/ResourceBuffer.h' - '../native/include/APIBoundaryTypes.h' diff --git a/thermion_dart/lib/thermion_dart/viewer/ffi/thermion_dart.g.dart b/thermion_dart/lib/thermion_dart/viewer/ffi/thermion_dart.g.dart index 55dddb20..11d59ae3 100644 --- a/thermion_dart/lib/thermion_dart/viewer/ffi/thermion_dart.g.dart +++ b/thermion_dart/lib/thermion_dart/viewer/ffi/thermion_dart.g.dart @@ -819,8 +819,8 @@ external ffi.Pointer get_camera_frustum( ); @ffi.Native< - ffi.Void Function(ffi.Pointer, double4x4, ffi.Double, - ffi.Double)>(isLeaf: true) + ffi.Void Function( + ffi.Pointer, double4x4, ffi.Double, ffi.Double)>(isLeaf: true) external void set_camera_projection_matrix( ffi.Pointer camera, double4x4 matrix, @@ -829,8 +829,8 @@ external void set_camera_projection_matrix( ); @ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Double, ffi.Double, - ffi.Double, ffi.Double, ffi.Bool)>(isLeaf: true) + ffi.Void Function(ffi.Pointer, ffi.Double, ffi.Double, ffi.Double, + ffi.Double, ffi.Bool)>(isLeaf: true) external void set_camera_projection_from_fov( ffi.Pointer camera, double fovInDegrees, @@ -857,8 +857,8 @@ external double get_camera_fov( ); @ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Double, ffi.Double, - ffi.Double, ffi.Double)>(isLeaf: true) + ffi.Void Function(ffi.Pointer, ffi.Double, ffi.Double, ffi.Double, + ffi.Double)>(isLeaf: true) external void set_camera_lens_projection( ffi.Pointer camera, double near, @@ -884,6 +884,17 @@ external void set_camera_manipulator_options( double zoomSpeed, ); +@ffi.Native< + ffi.Void Function(ffi.Pointer, double4x4, double4x4, ffi.Double, + ffi.Double)>(isLeaf: true) +external void Camera_setCustomProjectionWithCulling( + ffi.Pointer camera, + double4x4 projectionMatrix, + double4x4 projectionMatrixForCulling, + double near, + double far, +); + @ffi.Native< ffi.Int Function( ffi.Pointer, EntityId, ffi.Pointer)>(isLeaf: true) @@ -1339,7 +1350,7 @@ external void MaterialInstance_setDepthCulling( ffi.NativeFunction< ffi.Void Function( ffi.Pointer viewer)>>)>(isLeaf: true) -external void create_filament_viewer_ffi( +external void create_filament_viewer_render_thread( ffi.Pointer context, ffi.Pointer platform, ffi.Pointer uberArchivePath, @@ -1361,7 +1372,7 @@ external void create_filament_viewer_ffi( ffi.Uint32, ffi.Uint32, ffi.Pointer>)>(isLeaf: true) -external void create_swap_chain_ffi( +external void create_swap_chain_render_thread( ffi.Pointer viewer, ffi.Pointer surface, int width, @@ -1372,7 +1383,7 @@ external void create_swap_chain_ffi( @ffi.Native< ffi.Void Function(ffi.Pointer, ffi.Pointer>)>(isLeaf: true) -external void destroy_swap_chain_ffi( +external void destroy_swap_chain_render_thread( ffi.Pointer viewer, ffi.Pointer> onComplete, ); @@ -1380,7 +1391,7 @@ external void destroy_swap_chain_ffi( @ffi.Native< ffi.Void Function(ffi.Pointer, ffi.IntPtr, ffi.Uint32, ffi.Uint32, ffi.Pointer>)>(isLeaf: true) -external void create_render_target_ffi( +external void create_render_target_render_thread( ffi.Pointer viewer, int nativeTextureId, int width, @@ -1389,19 +1400,19 @@ external void create_render_target_ffi( ); @ffi.Native)>(isLeaf: true) -external void destroy_filament_viewer_ffi( +external void destroy_filament_viewer_render_thread( ffi.Pointer viewer, ); @ffi.Native)>(isLeaf: true) -external void render_ffi( +external void render_render_thread( ffi.Pointer viewer, ); @ffi.Native< ffi.Void Function(ffi.Pointer, ffi.Pointer, ffi.Pointer>)>(isLeaf: true) -external void capture_ffi( +external void capture_render_thread( ffi.Pointer viewer, ffi.Pointer out, ffi.Pointer> onComplete, @@ -1416,14 +1427,19 @@ external FilamentRenderCallback make_render_callback_fn_pointer( @ffi.Native< ffi.Void Function(ffi.Pointer, ffi.Bool, ffi.Pointer>)>(isLeaf: true) -external void set_rendering_ffi( +external void set_rendering_render_thread( ffi.Pointer viewer, bool rendering, ffi.Pointer> onComplete, ); +@ffi.Native)>(isLeaf: true) +external void request_frame_render_thread( + ffi.Pointer viewer, +); + @ffi.Native, ffi.Float)>(isLeaf: true) -external void set_frame_interval_ffi( +external void set_frame_interval_render_thread( ffi.Pointer viewer, double frameInterval, ); @@ -1431,7 +1447,7 @@ external void set_frame_interval_ffi( @ffi.Native< ffi.Void Function(ffi.Pointer, ffi.Float, ffi.Float, ffi.Float, ffi.Float)>(isLeaf: true) -external void set_background_color_ffi( +external void set_background_color_render_thread( ffi.Pointer viewer, double r, double g, @@ -1440,14 +1456,14 @@ external void set_background_color_ffi( ); @ffi.Native)>(isLeaf: true) -external void clear_background_image_ffi( +external void clear_background_image_render_thread( ffi.Pointer viewer, ); @ffi.Native< ffi.Void Function(ffi.Pointer, ffi.Pointer, ffi.Bool, ffi.Pointer>)>(isLeaf: true) -external void set_background_image_ffi( +external void set_background_image_render_thread( ffi.Pointer viewer, ffi.Pointer path, bool fillHeight, @@ -1457,7 +1473,7 @@ external void set_background_image_ffi( @ffi.Native< ffi.Void Function( ffi.Pointer, ffi.Float, ffi.Float, ffi.Bool)>(isLeaf: true) -external void set_background_image_position_ffi( +external void set_background_image_position_render_thread( ffi.Pointer viewer, double x, double y, @@ -1465,13 +1481,13 @@ external void set_background_image_position_ffi( ); @ffi.Native, ffi.Int)>(isLeaf: true) -external void set_tone_mapping_ffi( +external void set_tone_mapping_render_thread( ffi.Pointer viewer, int toneMapping, ); @ffi.Native, ffi.Float)>(isLeaf: true) -external void set_bloom_ffi( +external void set_bloom_render_thread( ffi.Pointer viewer, double strength, ); @@ -1479,7 +1495,7 @@ external void set_bloom_ffi( @ffi.Native< ffi.Void Function(ffi.Pointer, ffi.Pointer, ffi.Pointer>)>(isLeaf: true) -external void load_skybox_ffi( +external void load_skybox_render_thread( ffi.Pointer viewer, ffi.Pointer skyboxPath, ffi.Pointer> onComplete, @@ -1488,19 +1504,19 @@ external void load_skybox_ffi( @ffi.Native< ffi.Void Function( ffi.Pointer, ffi.Pointer, ffi.Float)>(isLeaf: true) -external void load_ibl_ffi( +external void load_ibl_render_thread( ffi.Pointer viewer, ffi.Pointer iblPath, double intensity, ); @ffi.Native)>(isLeaf: true) -external void remove_skybox_ffi( +external void remove_skybox_render_thread( ffi.Pointer viewer, ); @ffi.Native)>(isLeaf: true) -external void remove_ibl_ffi( +external void remove_ibl_render_thread( ffi.Pointer viewer, ); @@ -1525,7 +1541,7 @@ external void remove_ibl_ffi( ffi.Bool, ffi.Pointer>)>( isLeaf: true) -external void add_light_ffi( +external void add_light_render_thread( ffi.Pointer viewer, int type, double colour, @@ -1547,13 +1563,13 @@ external void add_light_ffi( ); @ffi.Native, EntityId)>(isLeaf: true) -external void remove_light_ffi( +external void remove_light_render_thread( ffi.Pointer viewer, int entityId, ); @ffi.Native)>(isLeaf: true) -external void clear_lights_ffi( +external void clear_lights_render_thread( ffi.Pointer viewer, ); @@ -1565,7 +1581,7 @@ external void clear_lights_ffi( ffi.Bool, ffi.Pointer>)>( isLeaf: true) -external void load_glb_ffi( +external void load_glb_render_thread( ffi.Pointer sceneManager, ffi.Pointer assetPath, int numInstances, @@ -1584,7 +1600,7 @@ external void load_glb_ffi( ffi.Int, ffi.Pointer>)>( isLeaf: true) -external void load_glb_from_buffer_ffi( +external void load_glb_from_buffer_render_thread( ffi.Pointer sceneManager, ffi.Pointer data, int length, @@ -1603,7 +1619,7 @@ external void load_glb_from_buffer_ffi( ffi.Bool, ffi.Pointer>)>( isLeaf: true) -external void load_gltf_ffi( +external void load_gltf_render_thread( ffi.Pointer sceneManager, ffi.Pointer assetPath, ffi.Pointer relativePath, @@ -1615,7 +1631,7 @@ external void load_gltf_ffi( ffi.Void Function(ffi.Pointer, EntityId, ffi.Pointer>)>( isLeaf: true) -external void create_instance_ffi( +external void create_instance_render_thread( ffi.Pointer sceneManager, int entityId, ffi.Pointer> callback, @@ -1624,7 +1640,7 @@ external void create_instance_ffi( @ffi.Native< ffi.Void Function(ffi.Pointer, EntityId, ffi.Pointer>)>(isLeaf: true) -external void remove_entity_ffi( +external void remove_entity_render_thread( ffi.Pointer viewer, int asset, ffi.Pointer> callback, @@ -1633,7 +1649,7 @@ external void remove_entity_ffi( @ffi.Native< ffi.Void Function(ffi.Pointer, ffi.Pointer>)>(isLeaf: true) -external void clear_entities_ffi( +external void clear_entities_render_thread( ffi.Pointer viewer, ffi.Pointer> callback, ); @@ -1645,7 +1661,7 @@ external void clear_entities_ffi( ffi.Pointer, ffi.Pointer>)>( isLeaf: true) -external void set_camera_ffi( +external void set_camera_render_thread( ffi.Pointer viewer, int asset, ffi.Pointer nodeName, @@ -1655,7 +1671,7 @@ external void set_camera_ffi( @ffi.Native< ffi.Void Function(ffi.Pointer, EntityId, ffi.Pointer, ffi.Pointer, ffi.Int)>(isLeaf: true) -external void apply_weights_ffi( +external void apply_weights_render_thread( ffi.Pointer sceneManager, int asset, ffi.Pointer entityName, @@ -1666,7 +1682,7 @@ external void apply_weights_ffi( @ffi.Native< ffi.Void Function( ffi.Pointer, EntityId, ffi.Int, ffi.Int)>(isLeaf: true) -external void set_animation_frame_ffi( +external void set_animation_frame_render_thread( ffi.Pointer sceneManager, int asset, int animationIndex, @@ -1675,7 +1691,7 @@ external void set_animation_frame_ffi( @ffi.Native, EntityId, ffi.Int)>( isLeaf: true) -external void stop_animation_ffi( +external void stop_animation_render_thread( ffi.Pointer sceneManager, int asset, int index, @@ -1685,7 +1701,7 @@ external void stop_animation_ffi( ffi.Void Function(ffi.Pointer, EntityId, ffi.Pointer>)>( isLeaf: true) -external void get_animation_count_ffi( +external void get_animation_count_render_thread( ffi.Pointer sceneManager, int asset, ffi.Pointer> callback, @@ -1698,7 +1714,7 @@ external void get_animation_count_ffi( ffi.Pointer, ffi.Int, ffi.Pointer>)>(isLeaf: true) -external void get_animation_name_ffi( +external void get_animation_name_render_thread( ffi.Pointer sceneManager, int asset, ffi.Pointer outPtr, @@ -1714,7 +1730,7 @@ external void get_animation_name_ffi( ffi.Pointer, ffi.Int, ffi.Pointer>)>(isLeaf: true) -external void get_morph_target_name_ffi( +external void get_morph_target_name_render_thread( ffi.Pointer sceneManager, int assetEntity, int childEntity, @@ -1727,7 +1743,7 @@ external void get_morph_target_name_ffi( ffi.Void Function(ffi.Pointer, EntityId, EntityId, ffi.Pointer>)>( isLeaf: true) -external void get_morph_target_name_count_ffi( +external void get_morph_target_name_count_render_thread( ffi.Pointer sceneManager, int asset, int childEntity, @@ -1742,7 +1758,7 @@ external void get_morph_target_name_count_ffi( ffi.Int, ffi.Pointer>)>( isLeaf: true) -external void set_morph_target_weights_ffi( +external void set_morph_target_weights_render_thread( ffi.Pointer sceneManager, int asset, ffi.Pointer morphData, @@ -1754,7 +1770,7 @@ external void set_morph_target_weights_ffi( ffi.Void Function(ffi.Pointer, EntityId, ffi.Pointer>)>( isLeaf: true) -external void update_bone_matrices_ffi( +external void update_bone_matrices_render_thread( ffi.Pointer sceneManager, int asset, ffi.Pointer> callback, @@ -1769,7 +1785,7 @@ external void update_bone_matrices_ffi( ffi.Pointer, ffi.Pointer>)>( isLeaf: true) -external void set_bone_transform_ffi( +external void set_bone_transform_render_thread( ffi.Pointer sceneManager, int asset, int skinIndex, @@ -1779,7 +1795,7 @@ external void set_bone_transform_ffi( ); @ffi.Native, ffi.Bool)>(isLeaf: true) -external void set_post_processing_ffi( +external void set_post_processing_render_thread( ffi.Pointer viewer, bool enabled, ); @@ -1787,7 +1803,7 @@ external void set_post_processing_ffi( @ffi.Native< ffi.Void Function(ffi.Pointer, EntityId, ffi.Pointer>)>(isLeaf: true) -external void reset_to_rest_pose_ffi( +external void reset_to_rest_pose_render_thread( ffi.Pointer sceneManager, int entityId, ffi.Pointer> callback, @@ -1809,7 +1825,7 @@ external void reset_to_rest_pose_ffi( ffi.Bool, ffi.Pointer>)>( isLeaf: true) -external void create_geometry_ffi( +external void create_geometry_render_thread( ffi.Pointer sceneManager, ffi.Pointer vertices, int numVertices, @@ -1836,7 +1852,7 @@ external void create_geometry_ffi( ffi.Uint32, ffi.Uint32, ffi.Pointer>)>(isLeaf: true) -external void unproject_texture_ffi( +external void unproject_texture_render_thread( ffi.Pointer sceneManager, int entity, ffi.Pointer input, diff --git a/thermion_dart/lib/thermion_dart/viewer/ffi/thermion_viewer_ffi.dart b/thermion_dart/lib/thermion_dart/viewer/ffi/thermion_viewer_ffi.dart index 5b100b5c..0afbf003 100644 --- a/thermion_dart/lib/thermion_dart/viewer/ffi/thermion_viewer_ffi.dart +++ b/thermion_dart/lib/thermion_dart/viewer/ffi/thermion_viewer_ffi.dart @@ -95,7 +95,7 @@ class ThermionViewerFFI extends ThermionViewer { Future createRenderTarget( double width, double height, int textureHandle) async { - await withVoidCallback((callback) => create_render_target_ffi( + await withVoidCallback((callback) => create_render_target_render_thread( _viewer!, textureHandle, width.toInt(), height.toInt(), callback)); } @@ -123,14 +123,14 @@ class ThermionViewerFFI extends ThermionViewer { Future createSwapChain(double width, double height, {Pointer? surface}) async { await withVoidCallback((callback) { - create_swap_chain_ffi(_viewer!, surface ?? nullptr, width.toInt(), - height.toInt(), callback); + create_swap_chain_render_thread(_viewer!, surface ?? nullptr, + width.toInt(), height.toInt(), callback); }); } Future destroySwapChain() async { await withVoidCallback((callback) { - destroy_swap_chain_ffi(_viewer!, callback); + destroy_swap_chain_render_thread(_viewer!, callback); }); } @@ -143,8 +143,14 @@ class ThermionViewerFFI extends ThermionViewer { nullptr; var viewer = await withVoidPointerCallback( (Pointer)>> callback) { - create_filament_viewer_ffi(_sharedContext, _driver, uberarchivePtr, - resourceLoader, _renderCallback, _renderCallbackOwner, callback); + create_filament_viewer_render_thread( + _sharedContext, + _driver, + uberarchivePtr, + resourceLoader, + _renderCallback, + _renderCallbackOwner, + callback); }); _viewer = Pointer.fromAddress(viewer); allocator.free(uberarchivePtr); @@ -178,9 +184,9 @@ class ThermionViewerFFI extends ThermionViewer { @override Future setRendering(bool render) async { _rendering = render; - await withVoidCallback((cb) { - set_rendering_ffi(_viewer!, render, cb); - }); + // await withVoidCallback((cb) { + // set_rendering_render_thread(_viewer!, render, cb); + // }); } /// @@ -188,7 +194,7 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future render() async { - render_ffi(_viewer!); + render_render_thread(_viewer!); } /// @@ -201,7 +207,7 @@ class ThermionViewerFFI extends ThermionViewer { 4; final out = allocator(length); await withVoidCallback((cb) { - capture_ffi(_viewer!, out, cb); + capture_render_thread(_viewer!, out, cb); }); final data = Uint8List.fromList(out.asTypedList(length)); allocator.free(out); @@ -214,7 +220,7 @@ class ThermionViewerFFI extends ThermionViewer { @override Future setFrameRate(int framerate) async { final interval = 1000.0 / framerate; - set_frame_interval_ffi(_viewer!, interval); + set_frame_interval_render_thread(_viewer!, interval); } final _onDispose = []; @@ -231,9 +237,9 @@ class ThermionViewerFFI extends ThermionViewer { await setRendering(false); await clearEntities(); await clearLights(); - - destroy_filament_viewer_ffi(_viewer!); - + print("DESTROYING"); + destroy_filament_viewer_render_thread(_viewer!); + print("DESTROYED"); _sceneManager = null; _viewer = null; await _pickResultController.close(); @@ -244,6 +250,7 @@ class ThermionViewerFFI extends ThermionViewer { await callback.call(); } _onDispose.clear(); + print("DONE"); } /// @@ -258,7 +265,7 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future clearBackgroundImage() async { - clear_background_image_ffi(_viewer!); + clear_background_image_render_thread(_viewer!); } /// @@ -268,7 +275,7 @@ class ThermionViewerFFI extends ThermionViewer { Future setBackgroundImage(String path, {bool fillHeight = false}) async { final pathPtr = path.toNativeUtf8(allocator: allocator).cast(); await withVoidCallback((cb) { - set_background_image_ffi(_viewer!, pathPtr, fillHeight, cb); + set_background_image_render_thread(_viewer!, pathPtr, fillHeight, cb); }); allocator.free(pathPtr); @@ -279,7 +286,7 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future setBackgroundColor(double r, double g, double b, double a) async { - set_background_color_ffi(_viewer!, r, g, b, a); + set_background_color_render_thread(_viewer!, r, g, b, a); } /// @@ -288,7 +295,7 @@ class ThermionViewerFFI extends ThermionViewer { @override Future setBackgroundImagePosition(double x, double y, {bool clamp = false}) async { - set_background_image_position_ffi(_viewer!, x, y, clamp); + set_background_image_position_render_thread(_viewer!, x, y, clamp); } /// @@ -299,7 +306,7 @@ class ThermionViewerFFI extends ThermionViewer { final pathPtr = skyboxPath.toNativeUtf8(allocator: allocator).cast(); await withVoidCallback((cb) { - load_skybox_ffi(_viewer!, pathPtr, cb); + load_skybox_render_thread(_viewer!, pathPtr, cb); }); allocator.free(pathPtr); @@ -320,7 +327,7 @@ class ThermionViewerFFI extends ThermionViewer { Future loadIbl(String lightingPath, {double intensity = 30000}) async { final pathPtr = lightingPath.toNativeUtf8(allocator: allocator).cast(); - load_ibl_ffi(_viewer!, pathPtr, intensity); + load_ibl_render_thread(_viewer!, pathPtr, intensity); } /// @@ -341,7 +348,7 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future removeSkybox() async { - remove_skybox_ffi(_viewer!); + remove_skybox_render_thread(_viewer!); } /// @@ -349,7 +356,7 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future removeIbl() async { - remove_ibl_ffi(_viewer!); + remove_ibl_render_thread(_viewer!); } @override @@ -392,7 +399,7 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future addDirectLight(DirectLight directLight) async { - var entity = await withIntCallback((callback) => add_light_ffi( + var entity = await withIntCallback((callback) => add_light_render_thread( _viewer!, directLight.type.index, directLight.color, @@ -424,7 +431,7 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future removeLight(ThermionEntity entity) async { - remove_light_ffi(_viewer!, entity); + remove_light_render_thread(_viewer!, entity); _sceneUpdateEventController.add(SceneUpdateEvent.remove(entity)); } @@ -433,7 +440,7 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future clearLights() async { - clear_lights_ffi(_viewer!); + clear_lights_render_thread(_viewer!); _sceneUpdateEventController.add(SceneUpdateEvent.clearLights()); } @@ -484,7 +491,7 @@ class ThermionViewerFFI extends ThermionViewer { throw Exception("Not yet implemented"); } final pathPtr = path.toNativeUtf8(allocator: allocator).cast(); - var entity = await withIntCallback((callback) => load_glb_ffi( + var entity = await withIntCallback((callback) => load_glb_render_thread( _sceneManager!, pathPtr, numInstances, keepData, callback)); allocator.free(pathPtr); if (entity == _FILAMENT_ASSET_ERROR) { @@ -515,15 +522,9 @@ class ThermionViewerFFI extends ThermionViewer { throw Exception("Layer must be between 0 and 6"); } - var entity = await withIntCallback((callback) => load_glb_from_buffer_ffi( - _sceneManager!, - data.address, - data.length, - numInstances, - keepData, - priority, - layer, - callback)); + var entity = await withIntCallback((callback) => + load_glb_from_buffer_render_thread(_sceneManager!, data.address, + data.length, numInstances, keepData, priority, layer, callback)); if (entity == _FILAMENT_ASSET_ERROR) { throw Exception("An error occurred loading GLB from buffer"); @@ -540,7 +541,7 @@ class ThermionViewerFFI extends ThermionViewer { final pathPtr = path.toNativeUtf8(allocator: allocator).cast(); final relativeResourcePathPtr = relativeResourcePath.toNativeUtf8(allocator: allocator).cast(); - var entity = await withIntCallback((callback) => load_gltf_ffi( + var entity = await withIntCallback((callback) => load_gltf_render_thread( _sceneManager!, pathPtr, relativeResourcePathPtr, keepData, callback)); allocator.free(pathPtr); allocator.free(relativeResourcePathPtr); @@ -614,7 +615,7 @@ class ThermionViewerFFI extends ThermionViewer { weightsPtr[i] = weights[i]; } var success = await withBoolCallback((cb) { - set_morph_target_weights_ffi( + set_morph_target_weights_render_thread( _sceneManager!, entity, weightsPtr, weights.length, cb); }); allocator.free(weightsPtr); @@ -634,7 +635,7 @@ class ThermionViewerFFI extends ThermionViewer { var names = []; var count = await withIntCallback((callback) => - get_morph_target_name_count_ffi( + get_morph_target_name_count_render_thread( _sceneManager!, entity, childEntity, callback)); var outPtr = allocator(255); for (int i = 0; i < count; i++) { @@ -944,7 +945,7 @@ class ThermionViewerFFI extends ThermionViewer { /// Future updateBoneMatrices(ThermionEntity entity) async { var result = await withBoolCallback((cb) { - update_bone_matrices_ffi(_sceneManager!, entity, cb); + update_bone_matrices_render_thread(_sceneManager!, entity, cb); }); if (!result) { throw Exception("Failed to update bone matrices"); @@ -993,7 +994,7 @@ class ThermionViewerFFI extends ThermionViewer { ptr[i] = transform.storage[i]; } var result = await withBoolCallback((cb) { - set_bone_transform_ffi( + set_bone_transform_render_thread( _sceneManager!, entity, skinIndex, boneIndex, ptr, cb); }); @@ -1015,7 +1016,7 @@ class ThermionViewerFFI extends ThermionViewer { throw Exception("No viewer available, ignoring"); } await withVoidCallback((cb) { - reset_to_rest_pose_ffi(_sceneManager!, entity, cb); + reset_to_rest_pose_render_thread(_sceneManager!, entity, cb); }); } @@ -1028,7 +1029,7 @@ class ThermionViewerFFI extends ThermionViewer { @override Future removeEntity(ThermionEntity entity) async { await withVoidCallback( - (callback) => remove_entity_ffi(_viewer!, entity, callback)); + (callback) => remove_entity_render_thread(_viewer!, entity, callback)); _sceneUpdateEventController.add(SceneUpdateEvent.remove(entity)); } @@ -1041,7 +1042,7 @@ class ThermionViewerFFI extends ThermionViewer { @override Future clearEntities() async { await withVoidCallback((callback) { - clear_entities_ffi(_viewer!, callback); + clear_entities_render_thread(_viewer!, callback); }); } @@ -1166,7 +1167,7 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future setToneMapping(ToneMapper mapper) async { - set_tone_mapping_ffi(_viewer!, mapper.index); + set_tone_mapping_render_thread(_viewer!, mapper.index); } /// @@ -1174,7 +1175,7 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future setPostProcessing(bool enabled) async { - set_post_processing_ffi(_viewer!, enabled); + set_post_processing_render_thread(_viewer!, enabled); } /// @@ -1213,7 +1214,7 @@ class ThermionViewerFFI extends ThermionViewer { /// @override Future setBloom(double bloom) async { - set_bloom_ffi(_viewer!, bloom); + set_bloom_render_thread(_viewer!, bloom); } /// @@ -1350,7 +1351,10 @@ class ThermionViewerFFI extends ThermionViewer { /// /// @override - Future setCameraLensProjection({double near = kNear, double far = kFar, double? aspect, + Future setCameraLensProjection( + {double near = kNear, + double far = kFar, + double? aspect, double focalLength = kFocalLength}) async { aspect ??= viewportDimensions.$1 / viewportDimensions.$2; var mainCamera = get_camera(_viewer!, get_main_camera(_viewer!)); @@ -1820,22 +1824,23 @@ class ThermionViewerFFI extends ThermionViewer { throw Exception("Viewer must not be null"); } - var entity = await withIntCallback((callback) => create_geometry_ffi( - _sceneManager!, - geometry.vertices.address, - geometry.vertices.length, - geometry.normals.address, - geometry.normals.length, - geometry.uvs.address, - geometry.uvs.length, - geometry.indices.address, - geometry.indices.length, - geometry.primitiveType.index, - materialInstance == null - ? nullptr - : (materialInstance as ThermionFFIMaterialInstance)._pointer, - keepData, - callback)); + var entity = await withIntCallback((callback) => + create_geometry_render_thread( + _sceneManager!, + geometry.vertices.address, + geometry.vertices.length, + geometry.normals.address, + geometry.normals.length, + geometry.uvs.address, + geometry.uvs.length, + geometry.indices.address, + geometry.indices.length, + geometry.primitiveType.index, + materialInstance == null + ? nullptr + : (materialInstance as ThermionFFIMaterialInstance)._pointer, + keepData, + callback)); if (entity == _FILAMENT_ASSET_ERROR) { throw Exception("Failed to create geometry"); } @@ -1983,8 +1988,16 @@ class ThermionViewerFFI extends ThermionViewer { int inputWidth, int inputHeight, int outWidth, int outHeight) async { final outPtr = Uint8List(outWidth * outHeight * 4); await withVoidCallback((callback) { - unproject_texture_ffi(_viewer!, entity, input.address, inputWidth, - inputHeight, outPtr.address, outWidth, outHeight, callback); + unproject_texture_render_thread( + _viewer!, + entity, + input.address, + inputWidth, + inputHeight, + outPtr.address, + outWidth, + outHeight, + callback); }); return outPtr.buffer.asUint8List(); @@ -2132,6 +2145,11 @@ class ThermionViewerFFI extends ThermionViewer { } return ThermionFFIMaterialInstance(instance); } + + @override + void requestFrame() { + request_frame_render_thread(_viewer!); + } } class ThermionFFITexture extends ThermionTexture { diff --git a/thermion_dart/lib/thermion_dart/viewer/thermion_viewer_base.dart b/thermion_dart/lib/thermion_dart/viewer/thermion_viewer_base.dart index 105b9897..5faecd12 100644 --- a/thermion_dart/lib/thermion_dart/viewer/thermion_viewer_base.dart +++ b/thermion_dart/lib/thermion_dart/viewer/thermion_viewer_base.dart @@ -58,12 +58,17 @@ abstract class ThermionViewer { Future setRendering(bool render); /// - /// Render a single frame. + /// Render a single frame immediately. /// Future render(); /// - /// Render a single frame to the viewport and copy the pixel buffer to [out]. + /// Requests a single frame to be rendered. This is only intended to be used internally. + /// + void requestFrame(); + + /// + /// Render a single frame and copy the pixel buffer to [out]. /// Future capture(); diff --git a/thermion_dart/lib/thermion_dart/viewer/thermion_viewer_stub.dart b/thermion_dart/lib/thermion_dart/viewer/thermion_viewer_stub.dart index 7d32a6af..273809e0 100644 --- a/thermion_dart/lib/thermion_dart/viewer/thermion_viewer_stub.dart +++ b/thermion_dart/lib/thermion_dart/viewer/thermion_viewer_stub.dart @@ -828,11 +828,6 @@ class ThermionViewerStub extends ThermionViewer { throw UnimplementedError(); } - @override - Future setCameraLensProjection(double near, double far, double aspect, double focalLength) { - // TODO: implement setCameraLensProjection - throw UnimplementedError(); - } @override Future setCameraModelMatrix4(Matrix4 matrix) { @@ -952,6 +947,17 @@ class ThermionViewerStub extends ThermionViewer { // TODO: implement setVisibilityLayer throw UnimplementedError(); } + + @override + void requestFrame() { + // TODO: implement requestFrame + } + + @override + Future setCameraLensProjection({double near = kNear, double far = kFar, double? aspect, double focalLength = kFocalLength}) { + // TODO: implement setCameraLensProjection + throw UnimplementedError(); + } } diff --git a/thermion_dart/native/include/FilamentViewer.hpp b/thermion_dart/native/include/FilamentViewer.hpp index 4fb91590..a98e99f9 100644 --- a/thermion_dart/native/include/FilamentViewer.hpp +++ b/thermion_dart/native/include/FilamentViewer.hpp @@ -75,7 +75,7 @@ namespace thermion_filament void clearEntities(); void updateViewport(uint32_t width, uint32_t height); - void render( + bool render( uint64_t frameTimeInNanos, void *pixelBuffer, void (*callback)(void *buf, size_t size, void *data), diff --git a/thermion_dart/native/include/ThermionDartApi.h b/thermion_dart/native/include/ThermionDartApi.h index a9108025..19e33e33 100644 --- a/thermion_dart/native/include/ThermionDartApi.h +++ b/thermion_dart/native/include/ThermionDartApi.h @@ -102,7 +102,7 @@ extern "C" EMSCRIPTEN_KEEPALIVE EntityId get_main_camera(const void *const viewer); EMSCRIPTEN_KEEPALIVE bool set_camera(const void *const viewer, EntityId entity, const char *nodeName); EMSCRIPTEN_KEEPALIVE void set_view_frustum_culling(const void *const viewer, bool enabled); - EMSCRIPTEN_KEEPALIVE void render( + EMSCRIPTEN_KEEPALIVE bool render( const void *const viewer, uint64_t frameTimeInNanos, void *pixelBuffer, @@ -225,6 +225,8 @@ extern "C" EMSCRIPTEN_KEEPALIVE void set_camera_lens_projection(TCamera *camera, double near, double far, double aspect, double focalLength); EMSCRIPTEN_KEEPALIVE void set_camera_focus_distance(TCamera *camera, float focusDistance); EMSCRIPTEN_KEEPALIVE void set_camera_manipulator_options(const void *const viewer, _ManipulatorMode mode, double orbitSpeedX, double orbitSpeedY, double zoomSpeed); + EMSCRIPTEN_KEEPALIVE void Camera_setCustomProjectionWithCulling(TCamera* camera, double4x4 projectionMatrix, double4x4 projectionMatrixForCulling, double near, double far); + EMSCRIPTEN_KEEPALIVE int hide_mesh(void *sceneManager, EntityId entity, const char *meshName); EMSCRIPTEN_KEEPALIVE int reveal_mesh(void *sceneManager, EntityId entity, const char *meshName); diff --git a/thermion_dart/native/include/ThermionDartFFIApi.h b/thermion_dart/native/include/ThermionDartFFIApi.h deleted file mode 100644 index 2a6dc3fd..00000000 --- a/thermion_dart/native/include/ThermionDartFFIApi.h +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef _DART_FILAMENT_FFI_API_H -#define _DART_FILAMENT_FFI_API_H - -#include "ThermionDartApi.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - - /// - /// This header replicates most of the methods in ThermionDartApi.h. - /// It represents the interface for: - /// - invoking those methods that must be called on the main Filament engine thread - /// - setting up a render loop - /// - typedef int32_t EntityId; - typedef void (*FilamentRenderCallback)(void *const owner); - - EMSCRIPTEN_KEEPALIVE void create_filament_viewer_ffi( - void *const context, - void *const platform, - const char *uberArchivePath, - const void *const loader, - void (*renderCallback)(void *const renderCallbackOwner), - void *const renderCallbackOwner, - void (*callback)(void *const viewer)); - EMSCRIPTEN_KEEPALIVE void create_swap_chain_ffi(void *const viewer, void *const surface, uint32_t width, uint32_t height, void (*onComplete)()); - EMSCRIPTEN_KEEPALIVE void destroy_swap_chain_ffi(void *const viewer, void (*onComplete)()); - EMSCRIPTEN_KEEPALIVE void create_render_target_ffi(void *const viewer, intptr_t nativeTextureId, uint32_t width, uint32_t height, void (*onComplete)()); - EMSCRIPTEN_KEEPALIVE void destroy_filament_viewer_ffi(void *const viewer); - EMSCRIPTEN_KEEPALIVE void render_ffi(void *const viewer); - EMSCRIPTEN_KEEPALIVE void capture_ffi(void *const viewer, uint8_t* out, void (*onComplete)()); - EMSCRIPTEN_KEEPALIVE FilamentRenderCallback make_render_callback_fn_pointer(FilamentRenderCallback); - EMSCRIPTEN_KEEPALIVE void set_rendering_ffi(void *const viewer, bool rendering, void(*onComplete)()); - EMSCRIPTEN_KEEPALIVE void set_frame_interval_ffi(void *const viewer, float frameInterval); - EMSCRIPTEN_KEEPALIVE void set_background_color_ffi(void *const viewer, const float r, const float g, const float b, const float a); - EMSCRIPTEN_KEEPALIVE void clear_background_image_ffi(void *const viewer); - EMSCRIPTEN_KEEPALIVE void set_background_image_ffi(void *const viewer, const char *path, bool fillHeight, void (*onComplete)()); - EMSCRIPTEN_KEEPALIVE void set_background_image_position_ffi(void *const viewer, float x, float y, bool clamp); - EMSCRIPTEN_KEEPALIVE void set_tone_mapping_ffi(void *const viewer, int toneMapping); - EMSCRIPTEN_KEEPALIVE void set_bloom_ffi(void *const viewer, float strength); - EMSCRIPTEN_KEEPALIVE void load_skybox_ffi(void *const viewer, const char *skyboxPath, void (*onComplete)()); - EMSCRIPTEN_KEEPALIVE void load_ibl_ffi(void *const viewer, const char *iblPath, float intensity); - EMSCRIPTEN_KEEPALIVE void remove_skybox_ffi(void *const viewer); - EMSCRIPTEN_KEEPALIVE void remove_ibl_ffi(void *const viewer); - EMSCRIPTEN_KEEPALIVE void add_light_ffi( - void *const viewer, - 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 remove_light_ffi(void *const viewer, EntityId entityId); - EMSCRIPTEN_KEEPALIVE void clear_lights_ffi(void *const viewer); - EMSCRIPTEN_KEEPALIVE void load_glb_ffi(void *const sceneManager, const char *assetPath, int numInstances, bool keepData, void (*callback)(EntityId)); - EMSCRIPTEN_KEEPALIVE void load_glb_from_buffer_ffi(void *const sceneManager, const uint8_t *const data, size_t length, int numInstances, bool keepData, int priority, int layer, void (*callback)(EntityId)); - EMSCRIPTEN_KEEPALIVE void load_gltf_ffi(void *const sceneManager, const char *assetPath, const char *relativePath, bool keepData, void (*callback)(EntityId)); - EMSCRIPTEN_KEEPALIVE void create_instance_ffi(void *const sceneManager, EntityId entityId, void (*callback)(EntityId)); - EMSCRIPTEN_KEEPALIVE void remove_entity_ffi(void *const viewer, EntityId asset, void (*callback)()); - EMSCRIPTEN_KEEPALIVE void clear_entities_ffi(void *const viewer, void (*callback)()); - EMSCRIPTEN_KEEPALIVE void set_camera_ffi(void *const viewer, EntityId asset, const char *nodeName, void (*callback)(bool)); - EMSCRIPTEN_KEEPALIVE void apply_weights_ffi( - void *const sceneManager, - EntityId asset, - const char *const entityName, - float *const weights, - int count); - EMSCRIPTEN_KEEPALIVE void set_animation_frame_ffi(void *const sceneManager, EntityId asset, int animationIndex, int animationFrame); - EMSCRIPTEN_KEEPALIVE void stop_animation_ffi(void *const sceneManager, EntityId asset, int index); - EMSCRIPTEN_KEEPALIVE void get_animation_count_ffi(void *const sceneManager, EntityId asset, void (*callback)(int)); - EMSCRIPTEN_KEEPALIVE void get_animation_name_ffi(void *const sceneManager, EntityId asset, char *const outPtr, int index, void (*callback)()); - EMSCRIPTEN_KEEPALIVE void get_morph_target_name_ffi(void *const sceneManager, EntityId assetEntity, EntityId childEntity, char *const outPtr, int index, void (*callback)()); - EMSCRIPTEN_KEEPALIVE void get_morph_target_name_count_ffi(void *const sceneManager, EntityId asset, EntityId childEntity, void (*callback)(int32_t)); - EMSCRIPTEN_KEEPALIVE void set_morph_target_weights_ffi(void *const sceneManager, - EntityId asset, - const float *const morphData, - int numWeights, - void (*callback)(bool)); - - EMSCRIPTEN_KEEPALIVE void update_bone_matrices_ffi(void *sceneManager, - EntityId asset, void(*callback)(bool)); - EMSCRIPTEN_KEEPALIVE void set_bone_transform_ffi( - void *sceneManager, - EntityId asset, - int skinIndex, - int boneIndex, - const float *const transform, - void (*callback)(bool)); - EMSCRIPTEN_KEEPALIVE void set_post_processing_ffi(void *const viewer, bool enabled); - EMSCRIPTEN_KEEPALIVE void reset_to_rest_pose_ffi(void *const sceneManager, EntityId entityId, void(*callback)()); - EMSCRIPTEN_KEEPALIVE void create_geometry_ffi( - void *const sceneManager, - float *vertices, - int numVertices, - float *normals, - int numNormals, - float *uvs, - int numUvs, - uint16_t *indices, - int numIndices, - int primitiveType, - TMaterialInstance *materialInstance, - bool keepData, - void (*callback)(EntityId)); - EMSCRIPTEN_KEEPALIVE void unproject_texture_ffi(void *const sceneManager, EntityId entity, uint8_t* input, uint32_t inputWidth, uint32_t inputHeight, uint8_t* out, uint32_t outWidth, uint32_t outHeight, void(*callback)()); - - -#ifdef __cplusplus -} -#endif - -#endif // _DART_FILAMENT_FFI_API_H diff --git a/thermion_dart/native/include/ThermionDartRenderThreadApi.h b/thermion_dart/native/include/ThermionDartRenderThreadApi.h new file mode 100644 index 00000000..33892b52 --- /dev/null +++ b/thermion_dart/native/include/ThermionDartRenderThreadApi.h @@ -0,0 +1,126 @@ +#ifndef _DART_FILAMENT_FFI_API_H +#define _DART_FILAMENT_FFI_API_H + +#include "ThermionDartApi.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + /// + /// This header replicates most of the methods in ThermionDartApi.h. + /// It represents the interface for: + /// - invoking those methods that must be called on the main Filament engine thread + /// - setting up a render loop + /// + typedef int32_t EntityId; + typedef void (*FilamentRenderCallback)(void *const owner); + + EMSCRIPTEN_KEEPALIVE void create_filament_viewer_render_thread( + void *const context, + void *const platform, + const char *uberArchivePath, + const void *const loader, + void (*renderCallback)(void *const renderCallbackOwner), + void *const renderCallbackOwner, + void (*callback)(void *const viewer)); + EMSCRIPTEN_KEEPALIVE void create_swap_chain_render_thread(void *const viewer, void *const surface, uint32_t width, uint32_t height, void (*onComplete)()); + EMSCRIPTEN_KEEPALIVE void destroy_swap_chain_render_thread(void *const viewer, void (*onComplete)()); + EMSCRIPTEN_KEEPALIVE void create_render_target_render_thread(void *const viewer, intptr_t nativeTextureId, uint32_t width, uint32_t height, void (*onComplete)()); + EMSCRIPTEN_KEEPALIVE void destroy_filament_viewer_render_thread(void *const viewer); + EMSCRIPTEN_KEEPALIVE void render_render_thread(void *const viewer); + EMSCRIPTEN_KEEPALIVE void capture_render_thread(void *const viewer, uint8_t* out, void (*onComplete)()); + EMSCRIPTEN_KEEPALIVE FilamentRenderCallback make_render_callback_fn_pointer(FilamentRenderCallback); + EMSCRIPTEN_KEEPALIVE void set_rendering_render_thread(void *const viewer, bool rendering, void(*onComplete)()); + EMSCRIPTEN_KEEPALIVE void request_frame_render_thread(void *const viewer); + EMSCRIPTEN_KEEPALIVE void set_frame_interval_render_thread(void *const viewer, float frameInterval); + EMSCRIPTEN_KEEPALIVE void set_background_color_render_thread(void *const viewer, const float r, const float g, const float b, const float a); + EMSCRIPTEN_KEEPALIVE void clear_background_image_render_thread(void *const viewer); + EMSCRIPTEN_KEEPALIVE void set_background_image_render_thread(void *const viewer, const char *path, bool fillHeight, void (*onComplete)()); + EMSCRIPTEN_KEEPALIVE void set_background_image_position_render_thread(void *const viewer, float x, float y, bool clamp); + EMSCRIPTEN_KEEPALIVE void set_tone_mapping_render_thread(void *const viewer, int toneMapping); + EMSCRIPTEN_KEEPALIVE void set_bloom_render_thread(void *const viewer, float strength); + EMSCRIPTEN_KEEPALIVE void load_skybox_render_thread(void *const viewer, const char *skyboxPath, void (*onComplete)()); + EMSCRIPTEN_KEEPALIVE void load_ibl_render_thread(void *const viewer, const char *iblPath, float intensity); + EMSCRIPTEN_KEEPALIVE void remove_skybox_render_thread(void *const viewer); + EMSCRIPTEN_KEEPALIVE void remove_ibl_render_thread(void *const viewer); + EMSCRIPTEN_KEEPALIVE void add_light_render_thread( + void *const viewer, + 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 remove_light_render_thread(void *const viewer, EntityId entityId); + EMSCRIPTEN_KEEPALIVE void clear_lights_render_thread(void *const viewer); + EMSCRIPTEN_KEEPALIVE void load_glb_render_thread(void *const sceneManager, const char *assetPath, int numInstances, bool keepData, void (*callback)(EntityId)); + EMSCRIPTEN_KEEPALIVE void load_glb_from_buffer_render_thread(void *const sceneManager, const uint8_t *const data, size_t length, int numInstances, bool keepData, int priority, int layer, void (*callback)(EntityId)); + EMSCRIPTEN_KEEPALIVE void load_gltf_render_thread(void *const sceneManager, const char *assetPath, const char *relativePath, bool keepData, void (*callback)(EntityId)); + EMSCRIPTEN_KEEPALIVE void create_instance_render_thread(void *const sceneManager, EntityId entityId, void (*callback)(EntityId)); + EMSCRIPTEN_KEEPALIVE void remove_entity_render_thread(void *const viewer, EntityId asset, void (*callback)()); + EMSCRIPTEN_KEEPALIVE void clear_entities_render_thread(void *const viewer, void (*callback)()); + EMSCRIPTEN_KEEPALIVE void set_camera_render_thread(void *const viewer, EntityId asset, const char *nodeName, void (*callback)(bool)); + EMSCRIPTEN_KEEPALIVE void apply_weights_render_thread( + void *const sceneManager, + EntityId asset, + const char *const entityName, + float *const weights, + int count); + EMSCRIPTEN_KEEPALIVE void set_animation_frame_render_thread(void *const sceneManager, EntityId asset, int animationIndex, int animationFrame); + EMSCRIPTEN_KEEPALIVE void stop_animation_render_thread(void *const sceneManager, EntityId asset, int index); + EMSCRIPTEN_KEEPALIVE void get_animation_count_render_thread(void *const sceneManager, EntityId asset, void (*callback)(int)); + EMSCRIPTEN_KEEPALIVE void get_animation_name_render_thread(void *const sceneManager, EntityId asset, char *const outPtr, int index, void (*callback)()); + EMSCRIPTEN_KEEPALIVE void get_morph_target_name_render_thread(void *const sceneManager, EntityId assetEntity, EntityId childEntity, char *const outPtr, int index, void (*callback)()); + EMSCRIPTEN_KEEPALIVE void get_morph_target_name_count_render_thread(void *const sceneManager, EntityId asset, EntityId childEntity, void (*callback)(int32_t)); + EMSCRIPTEN_KEEPALIVE void set_morph_target_weights_render_thread(void *const sceneManager, + EntityId asset, + const float *const morphData, + int numWeights, + void (*callback)(bool)); + + EMSCRIPTEN_KEEPALIVE void update_bone_matrices_render_thread(void *sceneManager, + EntityId asset, void(*callback)(bool)); + EMSCRIPTEN_KEEPALIVE void set_bone_transform_render_thread( + void *sceneManager, + EntityId asset, + int skinIndex, + int boneIndex, + const float *const transform, + void (*callback)(bool)); + EMSCRIPTEN_KEEPALIVE void set_post_processing_render_thread(void *const viewer, bool enabled); + EMSCRIPTEN_KEEPALIVE void reset_to_rest_pose_render_thread(void *const sceneManager, EntityId entityId, void(*callback)()); + EMSCRIPTEN_KEEPALIVE void create_geometry_render_thread( + void *const sceneManager, + float *vertices, + int numVertices, + float *normals, + int numNormals, + float *uvs, + int numUvs, + uint16_t *indices, + int numIndices, + int primitiveType, + TMaterialInstance *materialInstance, + bool keepData, + void (*callback)(EntityId)); + EMSCRIPTEN_KEEPALIVE void unproject_texture_render_thread(void *const sceneManager, EntityId entity, uint8_t* input, uint32_t inputWidth, uint32_t inputHeight, uint8_t* out, uint32_t outWidth, uint32_t outHeight, void(*callback)()); + + +#ifdef __cplusplus +} +#endif + +#endif // _DART_FILAMENT_FFI_API_H diff --git a/thermion_dart/native/src/FilamentViewer.cpp b/thermion_dart/native/src/FilamentViewer.cpp index 73333c0f..83b3909e 100644 --- a/thermion_dart/native/src/FilamentViewer.cpp +++ b/thermion_dart/native/src/FilamentViewer.cpp @@ -1106,7 +1106,7 @@ namespace thermion_filament } } - void FilamentViewer::render( + bool FilamentViewer::render( uint64_t frameTimeInNanos, void *pixelBuffer, void (*callback)(void *buf, size_t size, void *data), @@ -1115,7 +1115,7 @@ namespace thermion_filament if (!_view || !_swapChain) { - return; + return false; } auto now = std::chrono::high_resolution_clock::now(); @@ -1191,6 +1191,7 @@ namespace thermion_filament #ifdef __EMSCRIPTEN__ _engine->execute(); #endif + return beginFrame; } class CaptureCallbackHandler : public filament::backend::CallbackHandler { diff --git a/thermion_dart/native/src/ThermionDartApi.cpp b/thermion_dart/native/src/ThermionDartApi.cpp index 770ec434..0654a054 100644 --- a/thermion_dart/native/src/ThermionDartApi.cpp +++ b/thermion_dart/native/src/ThermionDartApi.cpp @@ -338,14 +338,14 @@ extern "C" cam->setModelMatrix(mat); } - EMSCRIPTEN_KEEPALIVE void render( + EMSCRIPTEN_KEEPALIVE bool render( const void *const viewer, uint64_t frameTimeInNanos, void *pixelBuffer, void (*callback)(void *buf, size_t size, void *data), void *data) { - ((FilamentViewer *)viewer)->render(frameTimeInNanos, pixelBuffer, callback, data); + return ((FilamentViewer *)viewer)->render(frameTimeInNanos, pixelBuffer, callback, data); } EMSCRIPTEN_KEEPALIVE void capture( @@ -1057,4 +1057,8 @@ EMSCRIPTEN_KEEPALIVE void MaterialInstance_setDepthWrite(TMaterialInstance* mate EMSCRIPTEN_KEEPALIVE void MaterialInstance_setDepthCulling(TMaterialInstance* materialInstance, bool enabled) { reinterpret_cast(materialInstance)->setDepthCulling(enabled); } + +EMSCRIPTEN_KEEPALIVE void Camera_setCustomProjectionWithCulling(TCamera* camera, double4x4 projectionMatrix, double4x4 projectionMatrixForCulling, double near, double far) { + reinterpret_cast(camera)->setCustomProjection(convert_double4x4_to_mat4(projectionMatrix), convert_double4x4_to_mat4(projectionMatrixForCulling), near, far); +} } diff --git a/thermion_dart/native/src/ThermionDartFFIApi.cpp b/thermion_dart/native/src/ThermionDartFFIApi.cpp deleted file mode 100644 index 366efca1..00000000 --- a/thermion_dart/native/src/ThermionDartFFIApi.cpp +++ /dev/null @@ -1,875 +0,0 @@ -#ifdef __EMSCRIPTEN__ -#define GL_GLEXT_PROTOTYPES -#include -#include - -#include -#include -#include -#include -#include - -extern "C" -{ - extern EMSCRIPTEN_KEEPALIVE EMSCRIPTEN_WEBGL_CONTEXT_HANDLE thermion_dart_web_create_gl_context(); -} -#endif - -#include "ThermionDartFFIApi.h" -#include "FilamentViewer.hpp" -#include "Log.hpp" -#include "ThreadPool.hpp" -#include "filament/LightManager.h" - -#include -#include -#include -#include - -using namespace thermion_filament; -using namespace std::chrono_literals; -#include - -class RenderLoop -{ -public: - explicit RenderLoop() - { - srand(time(NULL)); - #ifdef __EMSCRIPTEN__ - pthread_attr_t attr; - pthread_attr_init(&attr); - emscripten_pthread_attr_settransferredcanvases(&attr, "canvas"); - pthread_create(&t, &attr, &RenderLoop::startHelper, this); - #else - t = new std::thread([this]() { - start(); - }); - #endif - } - - ~RenderLoop() - { - _stop = true; - #ifdef __EMSCRIPTEN__ - pthread_join(t, NULL); - #else - t->join(); - #endif - Log("Render loop killed"); - } - - static void mainLoop(void* arg) { - ((RenderLoop*)arg)->iter(); - } - - static void *startHelper(void * parm) { - #ifdef __EMSCRIPTEN__ - emscripten_set_main_loop_arg(&RenderLoop::mainLoop, parm, 0, true); - #else - ((RenderLoop*)parm)->start(); - #endif - return nullptr; - } - - void start() { - while (!_stop) { - iter(); - } - } - - void iter() { - const auto frameStart = std::chrono::steady_clock::now(); - if (_rendering) { - doRender(); - } - - std::unique_lock lock(_access); - const auto frameEnd = frameStart + std::chrono::microseconds(_frameIntervalInMicroseconds); - - while (std::chrono::steady_clock::now() < frameEnd) { - if (!_tasks.empty()) { - auto task = std::move(_tasks.front()); - _tasks.pop_front(); - lock.unlock(); - task(); - lock.lock(); - } else { - _cond.wait_until(lock, frameEnd); - } - } - } - - void createViewer(void *const context, - void *const platform, - const char *uberArchivePath, - const ResourceLoaderWrapper *const loader, - void (*renderCallback)(void *), - void *const owner, - void (*callback)(void *const)) - { - _renderCallback = renderCallback; - _renderCallbackOwner = owner; - std::packaged_task lambda([=]() mutable - { -#ifdef __EMSCRIPTEN__ - _context = thermion_dart_web_create_gl_context(); - - auto success = emscripten_webgl_make_context_current((EMSCRIPTEN_WEBGL_CONTEXT_HANDLE)_context); - if(success != EMSCRIPTEN_RESULT_SUCCESS) { - std::cout << "Failed to make context current." << std::endl; - return; - } - glClearColor(0.0, 0.5, 0.5, 1.0); - glClear(GL_COLOR_BUFFER_BIT); - // emscripten_webgl_commit_frame(); - - _viewer = (FilamentViewer*) create_filament_viewer((void* const) _context, loader, platform, uberArchivePath); - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0, $1); - }, callback, _viewer); -#else - _viewer = (FilamentViewer*)create_filament_viewer(context, loader, platform, uberArchivePath); - callback(_viewer); -#endif - }); - auto fut = add_task(lambda); - } - - void destroyViewer(FilamentViewer* viewer) - { - std::packaged_task lambda([=]() mutable { - _rendering = false; - _viewer = nullptr; - destroy_filament_viewer(viewer); - }); - auto fut = add_task(lambda); - } - - void setRendering(bool rendering, void(*callback)()) - { - std::packaged_task lambda( - [=]() mutable - { - this->_rendering = rendering; - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0); - }, callback); - #else - callback(); - #endif - }); - auto fut = add_task(lambda); - } - - void doRender() - { - #ifdef __EMSCRIPTEN__ - if(emscripten_is_webgl_context_lost(_context) == EM_TRUE) { - Log("Context lost"); - auto sleepFor = std::chrono::seconds(1); - std::this_thread::sleep_for(sleepFor); - return; - } - #endif - render(_viewer, 0, nullptr, nullptr, nullptr); - if (_renderCallback) - { - _renderCallback(_renderCallbackOwner); - } -#ifdef __EMSCRIPTEN__ - // emscripten_webgl_commit_frame(); -#endif - } - - void setFrameIntervalInMilliseconds(float frameIntervalInMilliseconds) - { - _frameIntervalInMicroseconds = static_cast(1000.0f * frameIntervalInMilliseconds); - } - - template - auto add_task(std::packaged_task &pt) -> std::future - { - std::unique_lock lock(_access); - auto ret = pt.get_future(); - _tasks.push_back([pt = std::make_shared>( - std::move(pt))] - { (*pt)(); }); - _cond.notify_one(); - return ret; - } - - bool _stop = false; - bool _rendering = false; - int _frameIntervalInMicroseconds = 1000000.0 / 60.0; - std::mutex _access; - void (*_renderCallback)(void *const) = nullptr; - void *_renderCallbackOwner = nullptr; - - - std::condition_variable _cond; - std::deque> _tasks; - FilamentViewer* _viewer = nullptr; - #ifdef __EMSCRIPTEN__ - pthread_t t; - EMSCRIPTEN_WEBGL_CONTEXT_HANDLE _context; - int _frameNum = 0; - #else - std::thread *t = nullptr; - #endif -}; - -extern "C" -{ - - static RenderLoop *_rl; - - EMSCRIPTEN_KEEPALIVE void create_filament_viewer_ffi( - void *const context, void *const platform, const char *uberArchivePath, - const void *const loader, - void (*renderCallback)(void *const renderCallbackOwner), - void *const renderCallbackOwner, - void (*callback)(void *const)) - { - - if (!_rl) - { - _rl = new RenderLoop(); - } - _rl->createViewer(context, platform, uberArchivePath, (const ResourceLoaderWrapper *const)loader, - renderCallback, renderCallbackOwner, callback); - } - - EMSCRIPTEN_KEEPALIVE void destroy_filament_viewer_ffi(void *const viewer) - { - _rl->destroyViewer((FilamentViewer*)viewer); - delete _rl; - _rl = nullptr; - } - - EMSCRIPTEN_KEEPALIVE void create_swap_chain_ffi(void *const viewer, - void *const surface, - uint32_t width, - uint32_t height, - void (*onComplete)()) - { - std::packaged_task lambda( - [=]() mutable - { - create_swap_chain(viewer, surface, width, height); - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0); - }, onComplete); - #else - onComplete(); - #endif - }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void destroy_swap_chain_ffi(void *const viewer, void (*onComplete)()) - { - Log("Destroying swapchain"); - std::packaged_task lambda( - [=]() mutable - { - destroy_swap_chain(viewer); - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0); - }, onComplete); - #else - onComplete(); - #endif - }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void create_render_target_ffi(void *const viewer, - intptr_t nativeTextureId, - uint32_t width, - uint32_t height, - void (*onComplete)()) - { - std::packaged_task lambda([=]() mutable - { - create_render_target(viewer, nativeTextureId, width, height); - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0); - }, onComplete); - #else - onComplete(); - #endif - }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void set_rendering_ffi(void *const viewer, - bool rendering, void (*callback)()) - { - if (!_rl) - { - Log("No render loop!"); // PANIC? - } - else - { - _rl->setRendering(rendering, callback); - if (rendering) - { - Log("Set rendering to true"); - } - else - { - Log("Set rendering to false"); - } - } - } - - EMSCRIPTEN_KEEPALIVE void - set_frame_interval_ffi(void* const viewer, float frameIntervalInMilliseconds) - { - _rl->setFrameIntervalInMilliseconds(frameIntervalInMilliseconds); - std::packaged_task lambda([=]() mutable - { ((FilamentViewer*)viewer)->setFrameInterval(frameIntervalInMilliseconds); }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void render_ffi(void *const viewer) - { - std::packaged_task lambda([=]() mutable - { _rl->doRender(); }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void capture_ffi(void *const viewer, uint8_t* pixelBuffer, void (*onComplete)()) { - std::packaged_task lambda([=]() mutable - { capture(viewer, pixelBuffer, onComplete); }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void - set_background_color_ffi(void *const viewer, const float r, const float g, - const float b, const float a) - { - std::packaged_task lambda( - [=]() mutable - { set_background_color(viewer, r, g, b, a); }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void load_gltf_ffi(void *const sceneManager, - const char *path, - const char *relativeResourcePath, - bool keepData, - void (*callback)(EntityId)) - { - std::packaged_task lambda([=]() mutable - { - auto entity = load_gltf(sceneManager, path, relativeResourcePath, keepData); - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0, $1); - }, callback, entity); - #else - callback(entity); - #endif - return entity; }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void load_glb_ffi(void *const sceneManager, - const char *path, - int numInstances, - bool keepData, - void (*callback)(EntityId)) - { - std::packaged_task lambda( - [=]() mutable - { - auto entity = load_glb(sceneManager, path, numInstances, keepData); - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0, $1); - }, callback, entity); - #else - callback(entity); - #endif - return entity; - }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void load_glb_from_buffer_ffi(void *const sceneManager, - const uint8_t *const data, - size_t length, - int numInstances, - bool keepData, - int priority, - int layer, - void (*callback)(EntityId)) - { - std::packaged_task lambda( - [=]() mutable - { - auto entity = load_glb_from_buffer(sceneManager, data, length, keepData, priority, layer); - callback(entity); - return entity; - }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void clear_background_image_ffi(void *const viewer) - { - std::packaged_task lambda([=] - { clear_background_image(viewer); }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void set_background_image_ffi(void *const viewer, - const char *path, - bool fillHeight, void (*callback)()) - { - std::packaged_task lambda( - [=] - { - set_background_image(viewer, path, fillHeight); - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0); - }, callback); - #else - callback(); - #endif - }); - auto fut = _rl->add_task(lambda); - } - EMSCRIPTEN_KEEPALIVE void set_background_image_position_ffi(void *const viewer, - float x, float y, - bool clamp) - { - std::packaged_task lambda( - [=] - { set_background_image_position(viewer, x, y, clamp); }); - auto fut = _rl->add_task(lambda); - } - EMSCRIPTEN_KEEPALIVE void set_tone_mapping_ffi(void *const viewer, - int toneMapping) - { - std::packaged_task lambda( - [=] - { set_tone_mapping(viewer, toneMapping); }); - auto fut = _rl->add_task(lambda); - } - EMSCRIPTEN_KEEPALIVE void set_bloom_ffi(void *const viewer, float strength) - { - std::packaged_task lambda([=] - { set_bloom(viewer, strength); }); - auto fut = _rl->add_task(lambda); - } - EMSCRIPTEN_KEEPALIVE void load_skybox_ffi(void *const viewer, - const char *skyboxPath, - void (*onComplete)()) - { - std::packaged_task lambda([=] - { - load_skybox(viewer, skyboxPath); - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0); - }, onComplete); - #else - onComplete(); - #endif - }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void load_ibl_ffi(void *const viewer, const char *iblPath, - float intensity) - { - std::packaged_task lambda( - [=] - { load_ibl(viewer, iblPath, intensity); }); - auto fut = _rl->add_task(lambda); - } - EMSCRIPTEN_KEEPALIVE void remove_skybox_ffi(void *const viewer) - { - std::packaged_task lambda([=] - { remove_skybox(viewer); }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void remove_ibl_ffi(void *const viewer) - { - std::packaged_task lambda([=] - { remove_ibl(viewer); }); - auto fut = _rl->add_task(lambda); - } - - void add_light_ffi( - void *const viewer, - 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)) - { - std::packaged_task lambda([=] - { - auto entity = add_light( - viewer, - type, - colour, - intensity, - posX, - posY, - posZ, - dirX, - dirY, - dirZ, - falloffRadius, - spotLightConeInner, - spotLightConeOuter, - sunAngularRadius, - sunHaloSize, - sunHaloFallof, - shadows); - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0, $1); - }, callback, entity); - #else - callback(entity); - #endif - - return entity; }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void remove_light_ffi(void *const viewer, - EntityId entityId) - { - std::packaged_task lambda([=] - { remove_light(viewer, entityId); }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void clear_lights_ffi(void *const viewer) - { - std::packaged_task lambda([=] - { clear_lights(viewer); }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void remove_entity_ffi(void *const viewer, - EntityId asset, void (*callback)()) - { - std::packaged_task lambda([=] - { - remove_entity(viewer, asset); - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0); - }, callback); - #else - callback(); - #endif - }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void clear_entities_ffi(void *const viewer, void (*callback)()) - { - std::packaged_task lambda([=] - { - clear_entities(viewer); - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0); - }, callback); - #else - callback(); - #endif - }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void set_camera_ffi(void *const viewer, EntityId asset, - const char *nodeName, void (*callback)(bool)) - { - std::packaged_task lambda( - [=] - { - auto success = set_camera(viewer, asset, nodeName); - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0,$1); - }, callback, success); - #else - callback(success); - #endif - return success; - }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void - get_morph_target_name_ffi(void *sceneManager, EntityId assetEntity, - EntityId childEntity, char *const outPtr, int index, void (*callback)()) - { - std::packaged_task lambda([=] - { - get_morph_target_name(sceneManager, assetEntity, childEntity, outPtr, index); - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0); - }, callback); - #else - callback(); - #endif - }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void - get_morph_target_name_count_ffi(void *sceneManager, EntityId assetEntity, - EntityId childEntity, void (*callback)(int)) - { - std::packaged_task lambda([=] - { - auto count = get_morph_target_name_count(sceneManager, assetEntity, childEntity); - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0,$1); - }, callback, count); - #else - callback(count); - #endif - return count; }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void set_animation_frame_ffi(void *const sceneManager, - EntityId asset, - int animationIndex, - int animationFrame) - { - std::packaged_task lambda([=] - { set_animation_frame(sceneManager, asset, animationIndex, animationFrame); }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void stop_animation_ffi(void *const sceneManager, - EntityId asset, int index) - { - std::packaged_task lambda( - [=] - { stop_animation(sceneManager, asset, index); }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void get_animation_count_ffi(void *const sceneManager, - EntityId asset, - void (*callback)(int)) - { - std::packaged_task lambda( - [=] - { - auto count = get_animation_count(sceneManager, asset); - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0,$1); - }, callback, count); - #else - callback(count); - #endif - return count; - }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void get_animation_name_ffi(void *const sceneManager, - EntityId asset, - char *const outPtr, - int index, - void (*callback)()) - { - std::packaged_task lambda( - [=] - { - get_animation_name(sceneManager, asset, outPtr, index); - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0); - }, callback); - #else - callback(); - #endif - }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void set_post_processing_ffi(void *const viewer, - bool enabled) - { - std::packaged_task lambda( - [=] - { set_post_processing(viewer, enabled); }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void - get_name_for_entity_ffi(void *const sceneManager, const EntityId entityId, void (*callback)(const char *)) - { - std::packaged_task lambda( - [=] - { - auto name = get_name_for_entity(sceneManager, entityId); - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0,$1); - }, callback, name); - #else - callback(name); - #endif - return name; - }); - auto fut = _rl->add_task(lambda); - } - - void set_morph_target_weights_ffi(void *const sceneManager, - EntityId asset, - const float *const morphData, - int numWeights, - void (*callback)(bool)) - { - std::packaged_task lambda( - [=] - { - auto result = set_morph_target_weights(sceneManager, asset, morphData, numWeights); - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0,$1); - }, callback, result); - #else - callback(result); - #endif - }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void set_bone_transform_ffi( - void *sceneManager, - EntityId asset, - int skinIndex, - int boneIndex, - const float *const transform, - void (*callback)(bool)) - { - std::packaged_task lambda( - [=] - { - auto success = set_bone_transform(sceneManager, asset, skinIndex, boneIndex, transform); - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0,$1); - }, callback, success); - #else - callback(success); - #endif - return success; - }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void update_bone_matrices_ffi(void *sceneManager, - EntityId entity, void(*callback)(bool)) { - std::packaged_task lambda( - [=] - { - auto success = update_bone_matrices(sceneManager, entity); - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0); - }, callback, success); - #else - callback(success); - #endif - }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void reset_to_rest_pose_ffi(void *const sceneManager, EntityId entityId, void(*callback)()) - { - std::packaged_task lambda( - [=] - { - reset_to_rest_pose(sceneManager, entityId); - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0); - }, callback); - #else - callback(); - #endif - }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void create_geometry_ffi( - void *const sceneManager, - float *vertices, - int numVertices, - float *normals, - int numNormals, - float *uvs, - int numUvs, - uint16_t *indices, - int numIndices, - int primitiveType, - TMaterialInstance * materialInstance, - bool keepData, - void (*callback)(EntityId)) - { - std::packaged_task lambda( - [=] - { - auto entity = create_geometry(sceneManager, vertices, numVertices, normals, numNormals, uvs, numUvs, indices, numIndices, primitiveType, materialInstance, keepData); - #ifdef __EMSCRIPTEN__ - MAIN_THREAD_EM_ASM({ - moduleArg.dartFilamentResolveCallback($0,$1); - }, callback, entity); - #else - callback(entity); - #endif - return entity; - }); - auto fut = _rl->add_task(lambda); - } - - EMSCRIPTEN_KEEPALIVE void unproject_texture_ffi(void *const viewer, EntityId entity, uint8_t* input, uint32_t inputWidth, uint32_t inputHeight, uint8_t* out, uint32_t outWidth, uint32_t outHeight, void(*callback)()) { - std::packaged_task lambda( - [=] - { - unproject_texture(viewer, entity, input, inputWidth, inputHeight, out, outWidth, outHeight); - callback(); - }); - auto fut = _rl->add_task(lambda); - } - -} diff --git a/thermion_dart/native/src/ThermionDartRenderThreadApi.cpp b/thermion_dart/native/src/ThermionDartRenderThreadApi.cpp new file mode 100644 index 00000000..ab509990 --- /dev/null +++ b/thermion_dart/native/src/ThermionDartRenderThreadApi.cpp @@ -0,0 +1,847 @@ +#ifdef __EMSCRIPTEN__ +#define GL_GLEXT_PROTOTYPES +#include +#include + +#include +#include +#include +#include +#include + +extern "C" +{ + extern EMSCRIPTEN_KEEPALIVE EMSCRIPTEN_WEBGL_CONTEXT_HANDLE thermion_dart_web_create_gl_context(); +} +#endif + +#include "ThermionDartRenderThreadApi.h" +#include "FilamentViewer.hpp" +#include "Log.hpp" +#include "ThreadPool.hpp" +#include "filament/LightManager.h" + +#include +#include +#include +#include + +using namespace thermion_filament; +using namespace std::chrono_literals; +#include + +class RenderLoop +{ +public: + explicit RenderLoop() + { + srand(time(NULL)); +#ifdef __EMSCRIPTEN__ + pthread_attr_t attr; + pthread_attr_init(&attr); + emscripten_pthread_attr_settransferredcanvases(&attr, "canvas"); + pthread_create(&t, &attr, &RenderLoop::startHelper, this); +#else + t = new std::thread([this]() + { start(); }); +#endif + } + + ~RenderLoop() + { + _render = false; + _stop = true; + _cv.notify_one(); +#ifdef __EMSCRIPTEN__ + pthread_join(t, NULL); +#else + t->join(); +#endif + } + + static void mainLoop(void *arg) + { + ((RenderLoop *)arg)->iter(); + } + + static void *startHelper(void *parm) + { +#ifdef __EMSCRIPTEN__ + emscripten_set_main_loop_arg(&RenderLoop::mainLoop, parm, 0, true); +#else + ((RenderLoop *)parm)->start(); +#endif + return nullptr; + } + + void start() + { + while (!_stop) + { + iter(); + } + } + + void requestFrame() + { + this->_render = true; + } + + void iter() + { + std::unique_lock lock(_mutex); + if (_render) + { + doRender(); + _render = false; + + // Calculate and print FPS + auto currentTime = std::chrono::high_resolution_clock::now(); + float deltaTime = std::chrono::duration(currentTime - _lastFrameTime).count(); + _lastFrameTime = currentTime; + + _frameCount++; + _accumulatedTime += deltaTime; + + if (_accumulatedTime >= 1.0f) // Update FPS every second + { + _fps = _frameCount / _accumulatedTime; + std::cout << "FPS: " << _fps << std::endl; + _frameCount = 0; + _accumulatedTime = 0.0f; + } + } + if (!_tasks.empty()) + { + auto task = std::move(_tasks.front()); + _tasks.pop_front(); + lock.unlock(); + task(); + lock.lock(); + } + + _cv.wait_for(lock, std::chrono::microseconds(1000), [this] + { return !_tasks.empty() || _stop || _render; }); + + if (_stop) + return; + } + + void createViewer(void *const context, + void *const platform, + const char *uberArchivePath, + const ResourceLoaderWrapper *const loader, + void (*renderCallback)(void *), + void *const owner, + void (*callback)(void *const)) + { + _renderCallback = renderCallback; + _renderCallbackOwner = owner; + std::packaged_task lambda([=]() mutable + { +#ifdef __EMSCRIPTEN__ + _context = thermion_dart_web_create_gl_context(); + + auto success = emscripten_webgl_make_context_current((EMSCRIPTEN_WEBGL_CONTEXT_HANDLE)_context); + if (success != EMSCRIPTEN_RESULT_SUCCESS) + { + std::cout << "Failed to make context current." << std::endl; + return; + } + glClearColor(0.0, 0.5, 0.5, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + // emscripten_webgl_commit_frame(); + + _viewer = (FilamentViewer *)create_filament_viewer((void *const)_context, loader, platform, uberArchivePath); + MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0, $1); }, callback, _viewer); +#else + _viewer = (FilamentViewer *)create_filament_viewer(context, loader, platform, uberArchivePath); + callback(_viewer); +#endif + }); + auto fut = add_task(lambda); + } + + void destroyViewer(FilamentViewer *viewer) + { + std::packaged_task lambda([=]() mutable + { + _render = false; + _viewer = nullptr; + destroy_filament_viewer(viewer); }); + auto fut = add_task(lambda); + fut.wait(); + } + + bool doRender() + { +#ifdef __EMSCRIPTEN__ + if (emscripten_is_webgl_context_lost(_context) == EM_TRUE) + { + Log("Context lost"); + auto sleepFor = std::chrono::seconds(1); + std::this_thread::sleep_for(sleepFor); + return; + } +#endif + auto rendered = render(_viewer, 0, nullptr, nullptr, nullptr); + if (_renderCallback) + { + _renderCallback(_renderCallbackOwner); + } + return rendered; +#ifdef __EMSCRIPTEN__ + // emscripten_webgl_commit_frame(); +#endif + } + + void setFrameIntervalInMilliseconds(float frameIntervalInMilliseconds) + { + std::unique_lock lock(_mutex); + _frameIntervalInMicroseconds = static_cast(1000.0f * frameIntervalInMilliseconds); + } + + template + auto add_task(std::packaged_task &pt) -> std::future + { + std::unique_lock lock(_mutex); + auto ret = pt.get_future(); + _tasks.push_back([pt = std::make_shared>( + std::move(pt))] + { (*pt)(); }); + _cv.notify_one(); + return ret; + } + +private: + bool _stop = false; + bool _render = false; + int _frameIntervalInMicroseconds = 1000000 / 60; + std::mutex _mutex; + std::condition_variable _cv; + void (*_renderCallback)(void *const) = nullptr; + void *_renderCallbackOwner = nullptr; + std::deque> _tasks; + FilamentViewer *_viewer = nullptr; + std::chrono::high_resolution_clock::time_point _lastFrameTime; + int _frameCount = 0; + float _accumulatedTime = 0.0f; + float _fps = 0.0f; + +#ifdef __EMSCRIPTEN__ + pthread_t t; + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE _context; +#else + std::thread *t = nullptr; +#endif +}; + +extern "C" +{ + + static RenderLoop *_rl; + + EMSCRIPTEN_KEEPALIVE void create_filament_viewer_render_thread( + void *const context, void *const platform, const char *uberArchivePath, + const void *const loader, + void (*renderCallback)(void *const renderCallbackOwner), + void *const renderCallbackOwner, + void (*callback)(void *const)) + { + + if (!_rl) + { + _rl = new RenderLoop(); + } + _rl->createViewer(context, platform, uberArchivePath, (const ResourceLoaderWrapper *const)loader, + renderCallback, renderCallbackOwner, callback); + } + + EMSCRIPTEN_KEEPALIVE void destroy_filament_viewer_render_thread(void *const viewer) + { + _rl->destroyViewer((FilamentViewer *)viewer); + delete _rl; + _rl = nullptr; + } + + EMSCRIPTEN_KEEPALIVE void create_swap_chain_render_thread(void *const viewer, + void *const surface, + uint32_t width, + uint32_t height, + void (*onComplete)()) + { + std::packaged_task lambda( + [=]() mutable + { + create_swap_chain(viewer, surface, width, height); +#ifdef __EMSCRIPTEN__ + MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0); }, onComplete); +#else + onComplete(); +#endif + }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void destroy_swap_chain_render_thread(void *const viewer, void (*onComplete)()) + { + std::packaged_task lambda( + [=]() mutable + { + destroy_swap_chain(viewer); +#ifdef __EMSCRIPTEN__ + MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0); }, onComplete); +#else + onComplete(); +#endif + }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void create_render_target_render_thread(void *const viewer, + intptr_t nativeTextureId, + uint32_t width, + uint32_t height, + void (*onComplete)()) + { + std::packaged_task lambda([=]() mutable + { + create_render_target(viewer, nativeTextureId, width, height); +#ifdef __EMSCRIPTEN__ + MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0); }, onComplete); +#else + onComplete(); +#endif + }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void request_frame_render_thread(void *const viewer) + { + if (!_rl) + { + Log("No render loop!"); // PANIC? + } + else + { + _rl->requestFrame(); + } + } + + EMSCRIPTEN_KEEPALIVE void + set_frame_interval_render_thread(void *const viewer, float frameIntervalInMilliseconds) + { + _rl->setFrameIntervalInMilliseconds(frameIntervalInMilliseconds); + std::packaged_task lambda([=]() mutable + { ((FilamentViewer *)viewer)->setFrameInterval(frameIntervalInMilliseconds); }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void render_render_thread(void *const viewer) + { + std::packaged_task lambda([=]() mutable + { _rl->doRender(); }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void capture_render_thread(void *const viewer, uint8_t *pixelBuffer, void (*onComplete)()) + { + std::packaged_task lambda([=]() mutable + { capture(viewer, pixelBuffer, onComplete); }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void + set_background_color_render_thread(void *const viewer, const float r, const float g, + const float b, const float a) + { + std::packaged_task lambda( + [=]() mutable + { set_background_color(viewer, r, g, b, a); }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void load_gltf_render_thread(void *const sceneManager, + const char *path, + const char *relativeResourcePath, + bool keepData, + void (*callback)(EntityId)) + { + std::packaged_task lambda([=]() mutable + { + auto entity = load_gltf(sceneManager, path, relativeResourcePath, keepData); +#ifdef __EMSCRIPTEN__ + MAIN_THREAD_EM_ASM({ + moduleArg.dartFilamentResolveCallback($0, $1); + }, callback, entity); +#else + callback(entity); +#endif + return entity; }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void load_glb_render_thread(void *const sceneManager, + const char *path, + int numInstances, + bool keepData, + void (*callback)(EntityId)) + { + std::packaged_task lambda( + [=]() mutable + { + auto entity = load_glb(sceneManager, path, numInstances, keepData); +#ifdef __EMSCRIPTEN__ + MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0, $1); }, callback, entity); +#else + callback(entity); +#endif + return entity; + }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void load_glb_from_buffer_render_thread(void *const sceneManager, + const uint8_t *const data, + size_t length, + int numInstances, + bool keepData, + int priority, + int layer, + void (*callback)(EntityId)) + { + std::packaged_task lambda( + [=]() mutable + { + auto entity = load_glb_from_buffer(sceneManager, data, length, keepData, priority, layer); + callback(entity); + return entity; + }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void clear_background_image_render_thread(void *const viewer) + { + std::packaged_task lambda([=] + { clear_background_image(viewer); }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void set_background_image_render_thread(void *const viewer, + const char *path, + bool fillHeight, void (*callback)()) + { + std::packaged_task lambda( + [=] + { + set_background_image(viewer, path, fillHeight); +#ifdef __EMSCRIPTEN__ + MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0); }, callback); +#else + callback(); +#endif + }); + auto fut = _rl->add_task(lambda); + } + EMSCRIPTEN_KEEPALIVE void set_background_image_position_render_thread(void *const viewer, + float x, float y, + bool clamp) + { + std::packaged_task lambda( + [=] + { set_background_image_position(viewer, x, y, clamp); }); + auto fut = _rl->add_task(lambda); + } + EMSCRIPTEN_KEEPALIVE void set_tone_mapping_render_thread(void *const viewer, + int toneMapping) + { + std::packaged_task lambda( + [=] + { set_tone_mapping(viewer, toneMapping); }); + auto fut = _rl->add_task(lambda); + } + EMSCRIPTEN_KEEPALIVE void set_bloom_render_thread(void *const viewer, float strength) + { + std::packaged_task lambda([=] + { set_bloom(viewer, strength); }); + auto fut = _rl->add_task(lambda); + } + EMSCRIPTEN_KEEPALIVE void load_skybox_render_thread(void *const viewer, + const char *skyboxPath, + void (*onComplete)()) + { + std::packaged_task lambda([=] + { + load_skybox(viewer, skyboxPath); +#ifdef __EMSCRIPTEN__ + MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0); }, onComplete); +#else + onComplete(); +#endif + }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void load_ibl_render_thread(void *const viewer, const char *iblPath, + float intensity) + { + std::packaged_task lambda( + [=] + { load_ibl(viewer, iblPath, intensity); }); + auto fut = _rl->add_task(lambda); + } + EMSCRIPTEN_KEEPALIVE void remove_skybox_render_thread(void *const viewer) + { + std::packaged_task lambda([=] + { remove_skybox(viewer); }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void remove_ibl_render_thread(void *const viewer) + { + std::packaged_task lambda([=] + { remove_ibl(viewer); }); + auto fut = _rl->add_task(lambda); + } + + void add_light_render_thread( + void *const viewer, + 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)) + { + std::packaged_task lambda([=] + { + auto entity = add_light( + viewer, + type, + colour, + intensity, + posX, + posY, + posZ, + dirX, + dirY, + dirZ, + falloffRadius, + spotLightConeInner, + spotLightConeOuter, + sunAngularRadius, + sunHaloSize, + sunHaloFallof, + shadows); +#ifdef __EMSCRIPTEN__ + MAIN_THREAD_EM_ASM({ + moduleArg.dartFilamentResolveCallback($0, $1); + }, callback, entity); +#else + callback(entity); +#endif + + return entity; }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void remove_light_render_thread(void *const viewer, + EntityId entityId) + { + std::packaged_task lambda([=] + { remove_light(viewer, entityId); }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void clear_lights_render_thread(void *const viewer) + { + std::packaged_task lambda([=] + { clear_lights(viewer); }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void remove_entity_render_thread(void *const viewer, + EntityId asset, void (*callback)()) + { + std::packaged_task lambda([=] + { + remove_entity(viewer, asset); +#ifdef __EMSCRIPTEN__ + MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0); }, callback); +#else + callback(); +#endif + }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void clear_entities_render_thread(void *const viewer, void (*callback)()) + { + std::packaged_task lambda([=] + { + clear_entities(viewer); +#ifdef __EMSCRIPTEN__ + MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0); }, callback); +#else + callback(); +#endif + }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void set_camera_render_thread(void *const viewer, EntityId asset, + const char *nodeName, void (*callback)(bool)) + { + std::packaged_task lambda( + [=] + { + auto success = set_camera(viewer, asset, nodeName); +#ifdef __EMSCRIPTEN__ + MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0, $1); }, callback, success); +#else + callback(success); +#endif + return success; + }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void + get_morph_target_name_render_thread(void *sceneManager, EntityId assetEntity, + EntityId childEntity, char *const outPtr, int index, void (*callback)()) + { + std::packaged_task lambda([=] + { + get_morph_target_name(sceneManager, assetEntity, childEntity, outPtr, index); +#ifdef __EMSCRIPTEN__ + MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0); }, callback); +#else + callback(); +#endif + }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void + get_morph_target_name_count_render_thread(void *sceneManager, EntityId assetEntity, + EntityId childEntity, void (*callback)(int)) + { + std::packaged_task lambda([=] + { + auto count = get_morph_target_name_count(sceneManager, assetEntity, childEntity); +#ifdef __EMSCRIPTEN__ + MAIN_THREAD_EM_ASM({ + moduleArg.dartFilamentResolveCallback($0,$1); + }, callback, count); +#else + callback(count); +#endif + return count; }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void set_animation_frame_render_thread(void *const sceneManager, + EntityId asset, + int animationIndex, + int animationFrame) + { + std::packaged_task lambda([=] + { set_animation_frame(sceneManager, asset, animationIndex, animationFrame); }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void stop_animation_render_thread(void *const sceneManager, + EntityId asset, int index) + { + std::packaged_task lambda( + [=] + { stop_animation(sceneManager, asset, index); }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void get_animation_count_render_thread(void *const sceneManager, + EntityId asset, + void (*callback)(int)) + { + std::packaged_task lambda( + [=] + { + auto count = get_animation_count(sceneManager, asset); +#ifdef __EMSCRIPTEN__ + MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0, $1); }, callback, count); +#else + callback(count); +#endif + return count; + }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void get_animation_name_render_thread(void *const sceneManager, + EntityId asset, + char *const outPtr, + int index, + void (*callback)()) + { + std::packaged_task lambda( + [=] + { + get_animation_name(sceneManager, asset, outPtr, index); +#ifdef __EMSCRIPTEN__ + MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0); }, callback); +#else + callback(); +#endif + }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void set_post_processing_render_thread(void *const viewer, + bool enabled) + { + std::packaged_task lambda( + [=] + { set_post_processing(viewer, enabled); }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void + get_name_for_entity_render_thread(void *const sceneManager, const EntityId entityId, void (*callback)(const char *)) + { + std::packaged_task lambda( + [=] + { + auto name = get_name_for_entity(sceneManager, entityId); +#ifdef __EMSCRIPTEN__ + MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0, $1); }, callback, name); +#else + callback(name); +#endif + return name; + }); + auto fut = _rl->add_task(lambda); + } + + void set_morph_target_weights_render_thread(void *const sceneManager, + EntityId asset, + const float *const morphData, + int numWeights, + void (*callback)(bool)) + { + std::packaged_task lambda( + [=] + { + auto result = set_morph_target_weights(sceneManager, asset, morphData, numWeights); +#ifdef __EMSCRIPTEN__ + MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0, $1); }, callback, result); +#else + callback(result); +#endif + }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void set_bone_transform_render_thread( + void *sceneManager, + EntityId asset, + int skinIndex, + int boneIndex, + const float *const transform, + void (*callback)(bool)) + { + std::packaged_task lambda( + [=] + { + auto success = set_bone_transform(sceneManager, asset, skinIndex, boneIndex, transform); +#ifdef __EMSCRIPTEN__ + MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0, $1); }, callback, success); +#else + callback(success); +#endif + return success; + }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void update_bone_matrices_render_thread(void *sceneManager, + EntityId entity, void (*callback)(bool)) + { + std::packaged_task lambda( + [=] + { + auto success = update_bone_matrices(sceneManager, entity); +#ifdef __EMSCRIPTEN__ + MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0); }, callback, success); +#else + callback(success); +#endif + }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void reset_to_rest_pose_render_thread(void *const sceneManager, EntityId entityId, void (*callback)()) + { + std::packaged_task lambda( + [=] + { + reset_to_rest_pose(sceneManager, entityId); +#ifdef __EMSCRIPTEN__ + MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0); }, callback); +#else + callback(); +#endif + }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void create_geometry_render_thread( + void *const sceneManager, + float *vertices, + int numVertices, + float *normals, + int numNormals, + float *uvs, + int numUvs, + uint16_t *indices, + int numIndices, + int primitiveType, + TMaterialInstance *materialInstance, + bool keepData, + void (*callback)(EntityId)) + { + std::packaged_task lambda( + [=] + { + auto entity = create_geometry(sceneManager, vertices, numVertices, normals, numNormals, uvs, numUvs, indices, numIndices, primitiveType, materialInstance, keepData); +#ifdef __EMSCRIPTEN__ + MAIN_THREAD_EM_ASM({ moduleArg.dartFilamentResolveCallback($0, $1); }, callback, entity); +#else + callback(entity); +#endif + return entity; + }); + auto fut = _rl->add_task(lambda); + } + + EMSCRIPTEN_KEEPALIVE void unproject_texture_render_thread(void *const viewer, EntityId entity, uint8_t *input, uint32_t inputWidth, uint32_t inputHeight, uint8_t *out, uint32_t outWidth, uint32_t outHeight, void (*callback)()) + { + std::packaged_task lambda( + [=] + { + unproject_texture(viewer, entity, input, inputWidth, inputHeight, out, outWidth, outHeight); + callback(); + }); + auto fut = _rl->add_task(lambda); + } +} diff --git a/thermion_dart/test/integration_test.dart b/thermion_dart/test/integration_test.dart index d940f47e..83985a5a 100644 --- a/thermion_dart/test/integration_test.dart +++ b/thermion_dart/test/integration_test.dart @@ -92,7 +92,8 @@ void main() async { print(frustum.plane5.normal); print(frustum.plane5.constant); - await viewer.setCameraLensProjection(near:10.0, far:1000.0, aspect:1.0, focalLength:28.0); + await viewer.setCameraLensProjection( + near: 10.0, far: 1000.0, aspect: 1.0, focalLength: 28.0); frustum = await viewer.getCameraFrustum(); print(frustum.plane5.normal); print(frustum.plane5.constant); @@ -459,7 +460,7 @@ void main() async { expect(materialInstance, isNotNull); // with depth write enabled on both materials, cube2 renders behind the white cube - await _capture(viewer, "material_instance_depth_write_enabled"); + await _capture(viewer, "material_instance_depth_write_enabled"); // if we disable depth write on cube1, then cube2 will always appear in front // (relying on insertion order) @@ -468,12 +469,11 @@ void main() async { // set priority for the cube1 cube to 7 (render) last, cube1 renders in front await viewer.setPriority(cube1, 7); - await _capture(viewer, "material_instance_depth_write_disabled_with_priority"); + await _capture( + viewer, "material_instance_depth_write_disabled_with_priority"); }); }); - - // test('create instance from glb when keepData is true', () async { // var model = await viewer.loadGlb("$testDir/cube.glb", keepData: true); // await viewer.transformToUnitCube(model); @@ -901,6 +901,16 @@ void main() async { }); }); + group("render thread", () { + test("request frame on render thread", () async { + var viewer = await createViewer(); + viewer.requestFrame(); + + await Future.delayed(Duration(milliseconds: 20)); + await viewer.dispose(); + }); + }); + group("unproject", () { test("unproject", () async { final dimensions = (width: 1280, height: 768);