refactoring

This commit is contained in:
Nick Fisher
2025-03-18 16:26:47 +08:00
parent 07b80071a4
commit 77fe40848b
41 changed files with 1900 additions and 2342 deletions

View File

@@ -1,5 +1,7 @@
import 'dart:ffi';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
import 'package:thermion_dart/src/viewer/src/shared_types/layers.dart';
import 'package:vector_math/vector_math_64.dart';
import '../../../../utils/src/matrix.dart';
@@ -8,11 +10,10 @@ import 'thermion_dart.g.dart' as g;
class FFICamera extends Camera {
final Pointer<g.TCamera> camera;
final Pointer<g.TEngine> engine;
final Pointer<g.TTransformManager> transformManager;
final FFIFilamentApp app;
late ThermionEntity _entity;
FFICamera(this.camera, this.engine, this.transformManager) {
FFICamera(this.camera, this.app) {
_entity = g.Camera_getEntity(camera);
}
@@ -30,7 +31,8 @@ class FFICamera extends Camera {
@override
Future setTransform(Matrix4 transform) async {
var entity = g.Camera_getEntity(camera);
g.TransformManager_setTransform(transformManager, entity, matrix4ToDouble4x4(transform));
g.TransformManager_setTransform(
app.transformManager, entity, matrix4ToDouble4x4(transform));
}
@override
@@ -84,9 +86,8 @@ class FFICamera extends Camera {
@override
Future setProjection(Projection projection, double left, double right,
double bottom, double top, double near, double far) async {
double bottom, double top, double near, double far) async {
var pType = g.Projection.values[projection.index];
g.Camera_setProjection(camera, pType, left,
right, bottom, top, near, far);
g.Camera_setProjection(camera, pType, left, right, bottom, top, near, far);
}
}

View File

@@ -0,0 +1,150 @@
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_render_target.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_swapchain.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_texture.dart';
import 'package:thermion_dart/src/viewer/src/filament/filament.dart';
import 'package:thermion_dart/thermion_dart.dart';
typedef RenderCallback = Pointer<NativeFunction<Void Function(Pointer<Void>)>>;
class FFIFilamentConfig extends FilamentConfig<RenderCallback, Pointer<Void>> {
FFIFilamentConfig(
{required super.backend,
required super.resourceLoader,
required super.uberArchivePath});
}
class FFIFilamentApp extends FilamentApp<Pointer> {
static FFIFilamentApp? _instance;
final Pointer<TEngine> engine;
final Pointer<TGltfAssetLoader> gltfAssetLoader;
final Pointer<TGltfResourceLoader> gltfResourceLoader;
final Pointer<TRenderer> renderer;
final Pointer<TTransformManager> transformManager;
final Pointer<TLightManager> lightManager;
final Pointer<TRenderableManager> renderableManager;
final Pointer<TMaterialProvider> ubershaderMaterialProvider;
final Pointer<TRenderTicker> renderTicker;
FFIFilamentApp(
this.engine,
this.gltfAssetLoader,
this.gltfResourceLoader,
this.renderer,
this.transformManager,
this.lightManager,
this.renderableManager,
this.ubershaderMaterialProvider,
this.renderTicker)
: super(
engine: engine,
gltfAssetLoader: gltfAssetLoader,
gltfResourceLoader: gltfResourceLoader,
renderer: renderer,
transformManager: transformManager,
lightManager: lightManager,
renderableManager: renderableManager,
ubershaderMaterialProvider: ubershaderMaterialProvider);
Future<FFIFilamentApp> create(FFIFilamentConfig config) async {
if (_instance == null) {
RenderLoop_destroy();
RenderLoop_create();
final engine = await withPointerCallback<TEngine>((cb) =>
Engine_createRenderThread(
TBackend.values[config.backend.index].index,
config.platform ?? nullptr,
config.sharedContext ?? nullptr,
config.stereoscopicEyeCount,
config.disableHandleUseAfterFreeCheck,
cb));
final gltfResourceLoader = await withPointerCallback<TGltfResourceLoader>(
(cb) => GltfResourceLoader_createRenderThread(engine, cb));
final gltfAssetLoader = await withPointerCallback<TGltfAssetLoader>(
(cb) => GltfAssetLoader_createRenderThread(engine, nullptr, cb));
final renderer = await withPointerCallback<TRenderer>(
(cb) => Engine_createRendererRenderThread(engine, cb));
final ubershaderMaterialProvider =
await withPointerCallback<TMaterialProvider>(
(cb) => GltfAssetLoader_getMaterialProvider(gltfAssetLoader));
final transformManager = Engine_getTransformManager(engine);
final lightManager = Engine_getLightManager(engine);
final renderableManager = Engine_getRenderableManager(engine);
final renderTicker = await withPointerCallback<TRenderTicker>(
(cb) => RenderTicker_create());
_instance = FFIFilamentApp(
engine,
gltfAssetLoader,
gltfResourceLoader,
renderer,
transformManager,
lightManager,
renderableManager,
ubershaderMaterialProvider);
}
return _instance!;
}
@override
Future<SwapChain> createHeadlessSwapChain(int width, int height,
{bool hasStencilBuffer = false}) async {
var flags = TSWAP_CHAIN_CONFIG_TRANSPARENT | TSWAP_CHAIN_CONFIG_READABLE;
if (hasStencilBuffer) {
flags |= TSWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER;
}
final swapChain = await withPointerCallback<TSwapChain>((cb) =>
Engine_createHeadlessSwapChainRenderThread(
this.engine, width, height, flags, cb));
return FFISwapChain(swapChain);
}
///
///
///
@override
Future<SwapChain> createSwapChain(Pointer window,
{bool hasStencilBuffer = false}) async {
var flags = TSWAP_CHAIN_CONFIG_TRANSPARENT | TSWAP_CHAIN_CONFIG_READABLE;
if (hasStencilBuffer) {
flags |= TSWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER;
}
final swapChain = await withPointerCallback<TSwapChain>((cb) =>
Engine_createSwapChainRenderThread(
this.engine, window.cast<Void>(), flags, cb));
return FFISwapChain(swapChain);
}
///
///
///
Future destroySwapChain(SwapChain swapChain) async {
await withVoidCallback((callback) {
Engine_destroySwapChainRenderThread(
engine, (swapChain as FFISwapChain).swapChain, callback);
});
}
@override
Future destroy() {
throw UnimplementedError();
}
///
///
///
Future<RenderTarget> createRenderTarget(int width, int height,
{covariant FFITexture? color, covariant FFITexture? depth}) async {
final renderTarget = await withPointerCallback<TRenderTarget>((cb) {
RenderTarget_createRenderThread(engine, width, height,
color?.pointer ?? nullptr, depth?.pointer ?? nullptr, cb);
});
return FFIRenderTarget(renderTarget, this);
}
}

View File

@@ -1,25 +1,30 @@
import 'dart:ffi';
import 'package:thermion_dart/src/viewer/src/ffi/src/callbacks.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_texture.dart';
import 'package:thermion_dart/thermion_dart.dart';
class FFIRenderTarget extends RenderTarget {
final Pointer<TRenderTarget> renderTarget;
final Pointer<TViewer> viewer;
final Pointer<TEngine> engine;
final FFIFilamentApp app;
FFIRenderTarget(this.renderTarget, this.viewer, this.engine);
FFIRenderTarget(this.renderTarget, this.app);
@override
Future<Texture> getColorTexture() async {
final ptr = RenderTarget_getColorTexture(renderTarget);
return FFITexture(engine, ptr);
return FFITexture(app.engine, ptr);
}
@override
Future<Texture> getDepthTexture() async {
final ptr = RenderTarget_getDepthTexture(renderTarget);
return FFITexture(engine, ptr);
return FFITexture(app.engine, ptr);
}
@override
Future destroy() async {
await withVoidCallback((cb) => RenderTarget_destroy(app.engine, renderTarget));
}
}

View File

@@ -0,0 +1,19 @@
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_render_target.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_swapchain.dart';
import 'package:thermion_dart/src/viewer/src/shared_types/scene.dart';
import 'package:thermion_dart/src/viewer/src/shared_types/shared_types.dart';
import 'callbacks.dart';
import 'ffi_camera.dart';
class FFIScene extends Scene {
final Pointer<TScene> scene;
final FFIFilamentApp app;
FFIRenderTarget? renderTarget;
FFIScene(this.scene, this.app) {
}
}

View File

@@ -5,7 +5,6 @@ import 'package:thermion_dart/thermion_dart.dart';
class FFISwapChain extends SwapChain {
final Pointer<TSwapChain> swapChain;
final Pointer<TViewer> viewer;
FFISwapChain(this.swapChain, this.viewer);
FFISwapChain(this.swapChain);
}

View File

@@ -1,26 +1,22 @@
import 'dart:ffi';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_render_target.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_scene.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_swapchain.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/thermion_dart.g.dart';
import 'package:thermion_dart/src/viewer/src/shared_types/scene.dart';
import 'package:thermion_dart/src/viewer/src/shared_types/shared_types.dart';
import 'callbacks.dart';
import 'ffi_camera.dart';
import 'thermion_viewer_ffi.dart';
class FFIView extends View {
final Pointer<TView> view;
final Pointer<TViewer> viewer;
final Pointer<TEngine> engine;
final FFIFilamentApp app;
FFIRenderTarget? renderTarget;
FFIView(this.view, this.viewer, this.engine) {
FFIView(this.view, this.app) {
final renderTargetPtr = View_getRenderTarget(view);
if (renderTargetPtr != nullptr) {
renderTarget = FFIRenderTarget(
renderTargetPtr,
viewer,
engine
);
renderTarget = FFIRenderTarget(renderTargetPtr, app);
}
}
@@ -56,10 +52,9 @@ class FFIView extends View {
@override
Future<Camera> getCamera() async {
final engine = Viewer_getEngine(viewer);
final transformManager = Engine_getTransformManager(engine);
final transformManager = Engine_getTransformManager(app.engine);
final cameraPtr = View_getCamera(view);
return FFICamera(cameraPtr, engine, transformManager);
return FFICamera(cameraPtr, app.engine, transformManager);
}
@override
@@ -73,7 +68,7 @@ class FFIView extends View {
}
Future setRenderable(bool renderable, FFISwapChain swapChain) async {
Viewer_setViewRenderable(viewer, swapChain.swapChain, view, renderable);
throw UnimplementedError();
}
@override
@@ -90,8 +85,7 @@ class FFIView extends View {
@override
Future setToneMapper(ToneMapper mapper) async {
final engine = await Viewer_getEngine(viewer);
View_setToneMappingRenderThread(view, engine, mapper.index);
View_setToneMappingRenderThread(view, app.engine, mapper.index);
}
Future setStencilBufferEnabled(bool enabled) async {
@@ -114,4 +108,8 @@ class FFIView extends View {
Future setRenderQuality(QualityLevel quality) async {
View_setRenderQuality(view, TQualityLevel.values[quality.index]);
}
Future setScene(covariant FFIScene scene) async {
await withVoidCallback((cb) => View_setScene(view, scene.scene));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,11 +4,16 @@ import 'dart:math';
import 'dart:typed_data';
import 'package:animation_tools_dart/animation_tools_dart.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_asset.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_filament_app.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_gizmo.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_material.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_render_target.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_scene.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_swapchain.dart';
import 'package:thermion_dart/src/viewer/src/ffi/src/ffi_texture.dart';
import 'package:thermion_dart/src/viewer/src/filament/filament.dart';
import 'package:thermion_dart/src/viewer/src/shared_types/config.dart';
import 'package:thermion_dart/src/viewer/src/shared_types/layers.dart';
import 'package:vector_math/vector_math_64.dart';
import 'package:vector_math/vector_math_64.dart' as v64;
import '../../../../utils/src/matrix.dart';
@@ -19,84 +24,24 @@ import 'callbacks.dart';
import 'ffi_camera.dart';
import 'ffi_view.dart';
class FFIBindings {
final Pointer<TSceneManager> sceneManager;
final Pointer<TEngine> engine;
final Pointer<TMaterialProvider> unlitMaterialProvider;
final Pointer<TMaterialProvider> ubershaderMaterialProvider;
final Pointer<TTransformManager> transformManager;
final Pointer<TLightManager> lightManager;
final Pointer<TRenderableManager> renderableManager;
final Pointer<TViewer> viewer;
final Pointer<TAnimationManager> animationManager;
final Pointer<TNameComponentManager> nameComponentManager;
final Pointer<TRenderer> renderer;
FFIBindings(
{required this.renderer,
required this.sceneManager,
required this.engine,
required this.unlitMaterialProvider,
required this.ubershaderMaterialProvider,
required this.transformManager,
required this.lightManager,
required this.renderableManager,
required this.viewer,
required this.animationManager,
required this.nameComponentManager});
}
// ignore: constant_identifier_names
const ThermionEntity FILAMENT_ASSET_ERROR = 0;
typedef RenderCallback = Pointer<NativeFunction<Void Function(Pointer<Void>)>>;
///
///
///
class ThermionViewerFFI extends ThermionViewer {
final _logger = Logger("ThermionViewerFFI");
Pointer<TSceneManager>? _sceneManager;
Pointer<TEngine>? _engine;
Pointer<TMaterialProvider>? _unlitMaterialProvider;
Pointer<TMaterialProvider>? _ubershaderMaterialProvider;
Pointer<TTransformManager>? _transformManager;
Pointer<TLightManager>? _lightManager;
Pointer<TRenderableManager>? _renderableManager;
Pointer<TViewer>? _viewer;
Pointer<TAnimationManager>? _animationManager;
Pointer<TNameComponentManager>? _nameComponentManager;
late FFIBindings bindings;
final String? uberArchivePath;
final _initialized = Completer<bool>();
Future<bool> get initialized => _initialized.future;
final Pointer<Void> resourceLoader;
var _driver = nullptr.cast<Void>();
late final RenderCallback _renderCallback;
var _renderCallbackOwner = nullptr.cast<Void>();
var _sharedContext = nullptr.cast<Void>();
late NativeCallable<PickCallbackFunction> _onPickResultCallable;
///
///
///
ThermionViewerFFI(
{RenderCallback? renderCallback,
Pointer<Void>? renderCallbackOwner,
required this.resourceLoader,
Pointer<Void>? driver,
Pointer<Void>? sharedContext,
this.uberArchivePath}) {
this._renderCallbackOwner = renderCallbackOwner ?? nullptr;
this._renderCallback = renderCallback ?? nullptr;
this._driver = driver ?? nullptr;
this._sharedContext = sharedContext ?? nullptr;
late final FFIFilamentApp app;
late final FFIRenderTarget? renderTarget;
///
///
///
ThermionViewerFFI(this.app, {this.renderTarget}) {
_onPickResultCallable =
NativeCallable<PickCallbackFunction>.listener(_onPickResult);
@@ -107,49 +52,21 @@ class ThermionViewerFFI extends ThermionViewer {
///
///
Future<RenderTarget> createRenderTarget(int width, int height,
{int? colorTextureHandle, int? depthTextureHandle}) async {
{covariant FFITexture? color, covariant FFITexture? depth}) async {
final renderTarget = await withPointerCallback<TRenderTarget>((cb) {
Viewer_createRenderTargetRenderThread(_viewer!, colorTextureHandle ?? 0,
depthTextureHandle ?? 0, width, height, cb);
RenderTarget_createRenderThread(app.engine, width, height,
color?.pointer ?? nullptr, depth?.pointer ?? nullptr, cb);
});
return FFIRenderTarget(renderTarget, _viewer!, _engine!);
}
///
///
///
@override
Future destroyRenderTarget(FFIRenderTarget renderTarget) async {
if (_disposing || _viewer == null) {
_logger.info(
"Viewer is being (or has been) disposed; this will clean up all render targets.");
} else {
await withVoidCallback((cb) {
Viewer_destroyRenderTargetRenderThread(
_viewer!, renderTarget.renderTarget, cb);
});
}
}
///
///
///
Future<View> createView() async {
var view = await withPointerCallback<TView>((cb) {
Viewer_createViewRenderThread(_viewer!, cb);
});
if (view == nullptr) {
throw Exception("Failed to create view");
}
return FFIView(view, _viewer!, _engine!);
return FFIRenderTarget(renderTarget, app);
}
///
///
///
Future setViewportAndCameraProjection(double width, double height) async {
var mainView = FFIView(Viewer_getViewAt(_viewer!, 0), _viewer!, _engine!);
var mainView =
FFIView(Viewer_getViewAt(_viewer!, 0), _viewer!, app.engine!);
mainView.setViewport(width.toInt(), height.toInt());
final cameraCount = await getCameraCount();
@@ -175,86 +92,25 @@ class ThermionViewerFFI extends ThermionViewer {
}
}
///
///
///
Future<SwapChain> createHeadlessSwapChain(int width, int height) async {
var swapChain = await withPointerCallback<TSwapChain>((callback) {
return Viewer_createHeadlessSwapChainRenderThread(
_viewer!, width, height, callback);
});
return FFISwapChain(swapChain, _viewer!);
}
///
///
///
Future<SwapChain> createSwapChain(int surface) async {
var swapChain = await withPointerCallback<TSwapChain>((callback) {
return Viewer_createSwapChainRenderThread(
_viewer!, Pointer<Void>.fromAddress(surface), callback);
});
return FFISwapChain(swapChain, _viewer!);
}
///
///
///
Future destroySwapChain(SwapChain swapChain) async {
if (_viewer != null) {
await withVoidCallback((callback) {
Viewer_destroySwapChainRenderThread(
_viewer!, (swapChain as FFISwapChain).swapChain, callback);
});
}
}
late final FFIView view;
late final FFIScene scene;
late final FFICamera camera;
Future _initialize() async {
_logger.info("Initializing ThermionViewerFFI");
Viewer_destroyOnRenderThread(nullptr);
RenderLoop_destroy();
RenderLoop_create();
final uberarchivePtr =
uberArchivePath?.toNativeUtf8(allocator: allocator).cast<Char>() ??
nullptr;
_viewer = await withPointerCallback(
(Pointer<NativeFunction<Void Function(Pointer<TViewer>)>> callback) {
Viewer_createOnRenderThread(_sharedContext, _driver, uberarchivePtr,
resourceLoader, _renderCallback, _renderCallbackOwner, callback);
});
allocator.free(uberarchivePtr);
if (_viewer!.address == 0) {
throw Exception("Failed to create viewer. Check logs for details");
view = FFIView(
await withPointerCallback<TView>(
(cb) => Engine_createViewRenderThread(app.engine, cb)),
app);
scene = FFIScene(Engine_createScene(app.engine), app);
await view.setScene(scene);
camera = FFICamera(
await withPointerCallback<TCamera>(
(cb) => Engine_createCameraRenderThread(app.engine, cb)),
app);
if (renderTarget != null) {
await view.setRenderTarget(renderTarget);
}
_sceneManager = Viewer_getSceneManager(_viewer!);
_unlitMaterialProvider =
SceneManager_getUnlitMaterialProvider(_sceneManager!);
_ubershaderMaterialProvider =
SceneManager_getUbershaderMaterialProvider(_sceneManager!);
_engine = Viewer_getEngine(_viewer!);
_transformManager = Engine_getTransformManager(_engine!);
_lightManager = Engine_getLightManager(_engine!);
_animationManager = SceneManager_getAnimationManager(_sceneManager!);
_nameComponentManager =
SceneManager_getNameComponentManager(_sceneManager!);
_renderableManager = Engine_getRenderableManager(_engine!);
bindings = FFIBindings(
sceneManager: _sceneManager!,
engine: _engine!,
unlitMaterialProvider: _unlitMaterialProvider!,
ubershaderMaterialProvider: _ubershaderMaterialProvider!,
transformManager: _transformManager!,
lightManager: _lightManager!,
renderableManager: _renderableManager!,
viewer: _viewer!,
animationManager: _animationManager!,
nameComponentManager: _nameComponentManager!,
renderer: Viewer_getRenderer(_viewer!));
this._initialized.complete(true);
}
@@ -272,32 +128,24 @@ class ThermionViewerFFI extends ThermionViewer {
@override
Future setRendering(bool render) async {
_rendering = render;
// await withVoidCallback((cb) {
// set_rendering_render_thread(_viewer!, render, cb);
// });
}
///
///
///
@override
Future render({FFISwapChain? swapChain}) async {
final view = (await getViewAt(0)) as FFIView;
swapChain ??= FFISwapChain(Viewer_getSwapChainAt(_viewer!, 0), _viewer!);
Viewer_renderRenderThread(_viewer!, view.view, swapChain.swapChain);
Future render() async {
RenderTicker_renderRenderThread(app.renderTicker, 0);
// Viewer_renderRenderThread(_viewer!, view.view, swapChain.swapChain);
}
///
///
///
@override
Future<List<Uint8List>> capture(
List<({View view, SwapChain? swapChain, RenderTarget? renderTarget})>
targets) async {
var renderer = Viewer_getRenderer(_viewer!);
Future<Uint8List> capture() async {
final fence = await withPointerCallback<TFence>((cb) {
Engine_createFenceRenderThread(_engine!, cb);
Engine_createFenceRenderThread(app.engine!, cb);
});
var pixelBuffers = <Uint8List>[];
@@ -337,11 +185,11 @@ class ThermionViewerFFI extends ThermionViewer {
});
await withVoidCallback((cb) {
Engine_flushAndWaitRenderThead(_engine!, cb);
Engine_flushAndWaitRenderThead(app.engine!, cb);
});
await withVoidCallback((cb) {
Engine_destroyFenceRenderThread(_engine!, fence, cb);
Engine_destroyFenceRenderThread(app.engine!, fence, cb);
});
// await withVoidCallback((cb) {
@@ -400,9 +248,6 @@ class ThermionViewerFFI extends ThermionViewer {
await mInstance.dispose();
}
await destroyLights();
Viewer_destroyOnRenderThread(_viewer!);
RenderLoop_destroy();
_sceneManager = null;
_viewer = null;
@@ -410,7 +255,7 @@ class ThermionViewerFFI extends ThermionViewer {
for (final callback in _onDispose) {
await callback.call();
}
await app.destroy();
_onDispose.clear();
_disposing = false;
}
@@ -635,7 +480,7 @@ class ThermionViewerFFI extends ThermionViewer {
}
var thermionAsset = FFIAsset(
asset, _sceneManager!, _engine!, _unlitMaterialProvider!, this);
asset, _sceneManager!, app.engine!, _unlitMaterialProvider!, this);
return thermionAsset;
}
@@ -675,7 +520,7 @@ class ThermionViewerFFI extends ThermionViewer {
throw Exception("An error occurred loading GLB from buffer");
}
return FFIAsset(
assetPtr, _sceneManager!, _engine!, _unlitMaterialProvider!, this);
assetPtr, _sceneManager!, app.engine!, _unlitMaterialProvider!, this);
}
///
@@ -697,7 +542,7 @@ class ThermionViewerFFI extends ThermionViewer {
}
return FFIAsset(
assetPtr, _sceneManager!, _engine!, _unlitMaterialProvider!, this);
assetPtr, _sceneManager!, app.engine!, _unlitMaterialProvider!, this);
}
///
@@ -1780,7 +1625,7 @@ class ThermionViewerFFI extends ThermionViewer {
}
var asset = FFIAsset(
assetPtr, _sceneManager!, _engine!, _unlitMaterialProvider!, this);
assetPtr, _sceneManager!, app.engine!, _unlitMaterialProvider!, this);
return asset;
}
@@ -1841,7 +1686,7 @@ class ThermionViewerFFI extends ThermionViewer {
///
@override
Future setPriority(ThermionEntity entityId, int priority) async {
RenderableManager_setPriority(_renderableManager!, entityId, priority);
RenderableManager_setPriority(app.renderableManager, entityId, priority);
}
///
@@ -1899,7 +1744,7 @@ class ThermionViewerFFI extends ThermionViewer {
}
});
_grid = FFIAsset(
ptr, _sceneManager!, _engine!, _unlitMaterialProvider!, this);
ptr, _sceneManager!, app.engine!, _unlitMaterialProvider!, this);
}
await _grid!.addToScene();
await setLayerVisibility(VisibilityLayers.OVERLAY, true);
@@ -1929,7 +1774,7 @@ class ThermionViewerFFI extends ThermionViewer {
var bitmask = flags.fold(0, (a, b) => a | b.index);
final texturePtr = await withPointerCallback<TTexture>((cb) {
Texture_buildRenderThread(
_engine!,
app.engine!,
width,
height,
depth,
@@ -1944,7 +1789,7 @@ class ThermionViewerFFI extends ThermionViewer {
throw Exception("Failed to create texture");
}
return FFITexture(
_engine!,
app.engine!,
texturePtr,
);
}
@@ -1958,7 +1803,6 @@ class ThermionViewerFFI extends ThermionViewer {
double anisotropy = 0.0,
TextureCompareMode compareMode = TextureCompareMode.NONE,
TextureCompareFunc compareFunc = TextureCompareFunc.LESS_EQUAL}) async {
return FFITextureSampler(TextureSampler_create());
final samplerPtr = TextureSampler_create();
TextureSampler_setMinFilter(
samplerPtr, TSamplerMinFilter.values[minFilter.index]);
@@ -2011,9 +1855,10 @@ class ThermionViewerFFI extends ThermionViewer {
///
Future<Material> createMaterial(Uint8List data) async {
var ptr = await withPointerCallback<TMaterial>((cb) {
Engine_buildMaterialRenderThread(_engine!, data.address, data.length, cb);
Engine_buildMaterialRenderThread(
app.engine!, data.address, data.length, cb);
});
return FFIMaterial(ptr, _engine!, _sceneManager!);
return FFIMaterial(ptr, app.engine!, _sceneManager!);
}
///
@@ -2098,7 +1943,7 @@ class ThermionViewerFFI extends ThermionViewer {
final materialInstance = await withPointerCallback<TMaterialInstance>((cb) {
MaterialProvider_createMaterialInstanceRenderThread(
_ubershaderMaterialProvider!, key.address, cb);
app.ubershaderMaterialProvider, key.address, cb);
});
if (materialInstance == nullptr) {
throw Exception("Failed to create material instance");
@@ -2148,7 +1993,7 @@ class ThermionViewerFFI extends ThermionViewer {
Future<MaterialInstance> getMaterialInstanceAt(
ThermionEntity entity, int index) async {
final instancePtr = RenderableManager_getMaterialInstanceAt(
_renderableManager!, entity, index);
app.renderableManager, entity, index);
final instance = FFIMaterialInstance(instancePtr, _sceneManager!);
return instance;
@@ -2254,7 +2099,7 @@ class ThermionViewerFFI extends ThermionViewer {
if (view == nullptr) {
throw Exception("Failed to get view");
}
return FFIView(view, _viewer!, _engine!);
return FFIView(view, _viewer!, app.engine!);
}
@override
@@ -2278,7 +2123,7 @@ class ThermionViewerFFI extends ThermionViewer {
view,
gizmo.cast<TSceneAsset>(),
_sceneManager!,
_engine!,
app.engine!,
nullptr,
this,
gizmoEntities.toSet()
@@ -2289,14 +2134,15 @@ class ThermionViewerFFI extends ThermionViewer {
///
///
Future setCastShadows(ThermionEntity entity, bool castShadows) async {
RenderableManager_setCastShadows(_renderableManager!, entity, castShadows);
RenderableManager_setCastShadows(
app.renderableManager, entity, castShadows);
}
///
///
///
Future<bool> isCastShadowsEnabled(ThermionEntity entity) async {
return RenderableManager_isShadowCaster(_renderableManager!, entity);
return RenderableManager_isShadowCaster(app.renderableManager, entity);
}
///
@@ -2304,14 +2150,14 @@ class ThermionViewerFFI extends ThermionViewer {
///
Future setReceiveShadows(ThermionEntity entity, bool receiveShadows) async {
RenderableManager_setReceiveShadows(
_renderableManager!, entity, receiveShadows);
app.renderableManager, entity, receiveShadows);
}
///
///
///
Future<bool> isReceiveShadowsEnabled(ThermionEntity entity) async {
return RenderableManager_isShadowReceiver(_renderableManager!, entity);
return RenderableManager_isShadowReceiver(app.renderableManager, entity);
}
///
@@ -2319,8 +2165,7 @@ class ThermionViewerFFI extends ThermionViewer {
///
Future setClearOptions(
Vector4 clearColor, int clearStencil, bool clear, bool discard) async {
final renderer = Viewer_getRenderer(_viewer!);
Renderer_setClearOptions(renderer, clearColor.r, clearColor.g, clearColor.b,
clearColor.a, clearStencil, clear, discard);
Renderer_setClearOptions(app.renderer, clearColor.r, clearColor.g,
clearColor.b, clearColor.a, clearStencil, clear, discard);
}
}

View File

@@ -0,0 +1,78 @@
import 'package:thermion_dart/src/viewer/src/shared_types/engine.dart';
import 'package:thermion_dart/thermion_dart.dart';
class FilamentConfig<T, U> {
final Backend backend;
final T? renderCallback;
final U? renderCallbackOwner;
final U resourceLoader;
final U? platform;
final U? driver;
final U? sharedContext;
final String uberArchivePath;
final int stereoscopicEyeCount;
final bool disableHandleUseAfterFreeCheck;
FilamentConfig(
{required this.backend,
required this.resourceLoader,
required this.uberArchivePath,
this.renderCallback,
this.renderCallbackOwner,
this.platform,
this.driver,
this.sharedContext,
this.stereoscopicEyeCount = 1,
this.disableHandleUseAfterFreeCheck = false});
}
abstract class FilamentApp<T> {
final T engine;
final T gltfAssetLoader;
final T gltfResourceLoader;
final T renderer;
final T transformManager;
final T lightManager;
final T renderableManager;
final T ubershaderMaterialProvider;
FilamentApp(
{required this.engine,
required this.gltfAssetLoader,
required this.gltfResourceLoader,
required this.renderer,
required this.transformManager,
required this.lightManager,
required this.renderableManager,
required this.ubershaderMaterialProvider
});
///
///
///
Future<SwapChain> createHeadlessSwapChain(int width, int height,
{bool hasStencilBuffer = false});
///
///
///
Future<SwapChain> createSwapChain(T handle, {bool hasStencilBuffer = false});
///
///
///
Future destroySwapChain(SwapChain swapChain);
///
///
///
Future destroy();
///
///
///
Future<RenderTarget> createRenderTarget(
int width, int height, { covariant Texture? color, covariant Texture? depth });
}

View File

@@ -0,0 +1,28 @@
enum Backend {
/// !< Automatically selects an appropriate driver for the platform.
DEFAULT(0),
/// !< Selects the OpenGL/ES driver (default on Android)
OPENGL(1),
/// !< Selects the Vulkan driver if the platform supports it (default on Linux/Windows)
VULKAN(2),
/// !< Selects the Metal driver if the platform supports it (default on MacOS/iOS).
METAL(3),
/// !< Selects the no-op driver for testing purposes.
NOOP(4);
final int value;
const Backend(this.value);
static Backend fromValue(int value) => switch (value) {
0 => DEFAULT,
1 => OPENGL,
2 => VULKAN,
3 => METAL,
4 => NOOP,
_ => throw ArgumentError("Unknown value for TBackend: $value"),
};
}

View File

@@ -0,0 +1,3 @@
import 'package:thermion_dart/thermion_dart.dart';
final ThermionEntity FILAMENT_ENTITY_NULL = 0;

View File

@@ -0,0 +1,18 @@
const double kNear = 0.05;
const double kFar = 1000.0;
const double kFocalLength = 28.0;
enum VisibilityLayers {
DEFAULT_ASSET(0),
LAYER_1(1),
LAYER_2(2),
LAYER_3(3),
LAYER_4(4),
LAYER_5(5),
BACKGROUND(6),
OVERLAY(7);
final int value;
const VisibilityLayers(this.value);
}

View File

@@ -3,4 +3,5 @@ import 'package:thermion_dart/thermion_dart.dart';
abstract class RenderTarget {
Future<Texture> getColorTexture();
Future<Texture> getDepthTexture();
Future destroy();
}

View File

@@ -0,0 +1,4 @@
abstract class Scene {
}

View File

@@ -1,3 +1,5 @@
import 'package:thermion_dart/src/viewer/src/shared_types/layers.dart';
import '../../utils/src/gizmo.dart';
import 'shared_types/shared_types.dart';
export 'shared_types/shared_types.dart';
@@ -8,32 +10,12 @@ import 'package:vector_math/vector_math_64.dart';
import 'dart:async';
import 'package:animation_tools_dart/animation_tools_dart.dart';
const double kNear = 0.05;
const double kFar = 1000.0;
const double kFocalLength = 28.0;
enum VisibilityLayers {
DEFAULT_ASSET(0),
LAYER_1(1),
LAYER_2(2),
LAYER_3(3),
LAYER_4(4),
LAYER_5(5),
BACKGROUND(6),
OVERLAY(7);
final int value;
const VisibilityLayers(this.value);
}
final ThermionEntity FILAMENT_ENTITY_NULL = 0;
///
/// A high-level interface for interacting with a 3D scene.
/// This broadly maps to a single scene/view
///
abstract class ThermionViewer {
///
/// A Future that resolves when the underlying rendering context has been successfully created.
///
Future<bool> get initialized;
///
/// Whether the controller is currently rendering at [framerate].
///
@@ -47,7 +29,7 @@ abstract class ThermionViewer {
///
/// Render a single frame immediately.
///
Future render({covariant SwapChain? swapChain});
Future render();
///
/// Requests a single frame to be rendered. This is only intended to be used internally.
@@ -60,31 +42,6 @@ abstract class ThermionViewer {
Future<List<Uint8List>> capture(
covariant List<({View view, SwapChain? swapChain, RenderTarget? renderTarget})> targets);
///
///
///
Future<SwapChain> createHeadlessSwapChain(int width, int height);
///
///
///
Future<SwapChain> createSwapChain(int handle);
///
///
///
Future destroySwapChain(covariant SwapChain swapChain);
///
///
///
Future<RenderTarget> createRenderTarget(
int width, int height, { int? colorTextureHandle, int? depthTextureHandle });
///
///
///
Future destroyRenderTarget(covariant RenderTarget renderTarget);
///
///

View File

@@ -312,12 +312,6 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError();
}
@override
Future setBloom(double bloom) {
// TODO: implement setBloom
throw UnimplementedError();
}
@override
Future setBoneTransform(
ThermionEntity entity, int boneIndex, Matrix4 transform,
@@ -768,12 +762,6 @@ class ThermionViewerStub extends ThermionViewer {
throw UnimplementedError();
}
@override
Future<RenderTarget> createRenderTarget(int width, int height, int textureHandle) {
// TODO: implement createRenderTarget
throw UnimplementedError();
}
@override
Future setRenderTarget(covariant RenderTarget renderTarget) {
// TODO: implement setRenderTarget
@@ -799,13 +787,7 @@ class ThermionViewerStub extends ThermionViewer {
// TODO: implement createHeadlessSwapChain
throw UnimplementedError();
}
@override
Future<Uint8List> capture({covariant SwapChain? swapChain, covariant View? view, covariant RenderTarget? renderTarget}) {
// TODO: implement capture
throw UnimplementedError();
}
@override
Future<SwapChain> createSwapChain(handle) {
// TODO: implement createSwapChain
@@ -1089,6 +1071,88 @@ class ThermionViewerStub extends ThermionViewer {
// TODO: implement setParent
throw UnimplementedError();
}
@override
Future<LinearImage> createImage(int width, int height, int channels) {
// TODO: implement createImage
throw UnimplementedError();
}
@override
Future<Texture> createTexture(int width, int height, {int depth = 1, int levels = 1, TextureSamplerType textureSamplerType = TextureSamplerType.SAMPLER_2D, TextureFormat textureFormat = TextureFormat.RGBA32F}) {
// TODO: implement createTexture
throw UnimplementedError();
}
@override
Future<TextureSampler> createTextureSampler({TextureMinFilter minFilter = TextureMinFilter.LINEAR, TextureMagFilter magFilter = TextureMagFilter.LINEAR, TextureWrapMode wrapS = TextureWrapMode.CLAMP_TO_EDGE, TextureWrapMode wrapT = TextureWrapMode.CLAMP_TO_EDGE, TextureWrapMode wrapR = TextureWrapMode.CLAMP_TO_EDGE, double anisotropy = 0.0, TextureCompareMode compareMode = TextureCompareMode.NONE, TextureCompareFunc compareFunc = TextureCompareFunc.LESS_EQUAL}) {
// TODO: implement createTextureSampler
throw UnimplementedError();
}
@override
Future<LinearImage> decodeImage(Uint8List data) {
// TODO: implement decodeImage
throw UnimplementedError();
}
@override
Future destroyCamera(covariant Camera camera) {
// TODO: implement destroyCamera
throw UnimplementedError();
}
@override
Future<bool> isCastShadowsEnabled(ThermionEntity entity) {
// TODO: implement isCastShadowsEnabled
throw UnimplementedError();
}
@override
Future<bool> isReceiveShadowsEnabled(ThermionEntity entity) {
// TODO: implement isReceiveShadowsEnabled
throw UnimplementedError();
}
@override
// TODO: implement msPerFrame
double get msPerFrame => throw UnimplementedError();
@override
Future setCastShadows(ThermionEntity entity, bool castShadows) {
// TODO: implement setCastShadows
throw UnimplementedError();
}
@override
Future setClearOptions(Vector4 clearColor, int clearStencil, bool clear, bool discard) {
// TODO: implement setClearOptions
throw UnimplementedError();
}
@override
Future setReceiveShadows(ThermionEntity entity, bool receiveShadows) {
// TODO: implement setReceiveShadows
throw UnimplementedError();
}
@override
Future<List<Uint8List>> capture(covariant List<({RenderTarget? renderTarget, SwapChain? swapChain, View view})> targets) {
// TODO: implement capture
throw UnimplementedError();
}
@override
Future<RenderTarget> createRenderTarget(int width, int height, {int? colorTextureHandle, int? depthTextureHandle}) {
// TODO: implement createRenderTarget
throw UnimplementedError();
}
@override
Future setBloom(bool enabled, double strength) {
// TODO: implement setBloom
throw UnimplementedError();
}
}

View File

@@ -1,3 +1,5 @@
import 'package:vector_math/vector_math_64.dart';
import '../../../viewer.dart';
class ThermionWasmMaterialInstance extends MaterialInstance {
@@ -94,4 +96,52 @@ class ThermionWasmMaterialInstance extends MaterialInstance {
// TODO: implement setStencilWriteEnabled
throw UnimplementedError();
}
@override
Future dispose() {
// TODO: implement dispose
throw UnimplementedError();
}
@override
Future setParameterBool(String name, bool value) {
// TODO: implement setParameterBool
throw UnimplementedError();
}
@override
Future setParameterFloat3(String name, double x, double y, double z) {
// TODO: implement setParameterFloat3
throw UnimplementedError();
}
@override
Future setParameterFloat3Array(String name, List<Vector3> data) {
// TODO: implement setParameterFloat3Array
throw UnimplementedError();
}
@override
Future setParameterTexture(String name, covariant Texture texture, covariant TextureSampler sampler) {
// TODO: implement setParameterTexture
throw UnimplementedError();
}
@override
Future setStencilReadMask(int mask) {
// TODO: implement setStencilReadMask
throw UnimplementedError();
}
@override
Future setStencilWriteMask(int mask) {
// TODO: implement setStencilWriteMask
throw UnimplementedError();
}
@override
Future setTransparencyMode(TransparencyMode mode) {
// TODO: implement setTransparencyMode
throw UnimplementedError();
}
}

View File

@@ -121,7 +121,7 @@
// [context!, loader, null, uberArchivePath?.toJS].toJS,
// null) as JSNumber;
// await createSwapChain(width.ceil(), height.ceil());
// updateViewportAndCameraProjection(width.ceil(), height.ceil(), 1.0);
// setViewportAndCameraProjection(width.ceil(), height.ceil(), 1.0);
// _sceneManager = _module!.ccall("get_scene_manager", "void*",
// ["void*".toJS].toJS, [_viewer!].toJS, null) as JSNumber;
@@ -177,7 +177,7 @@
// [_viewer!].toJS, null);
// }
// void updateViewportAndCameraProjection(
// void setViewportAndCameraProjection(
// int width, int height, double scaleFactor) {
// if (width == 0 || height == 0) {
// throw Exception("Width/height must be greater than zero");

View File

@@ -1,176 +0,0 @@
#pragma once
#include <filament/Camera.h>
#include <filament/Frustum.h>
#include <filament/ColorGrading.h>
#include <filament/Engine.h>
#include <filament/IndexBuffer.h>
#include <filament/RenderableManager.h>
#include <filament/Renderer.h>
#include <filament/Scene.h>
#include <filament/Skybox.h>
#include <filament/TransformManager.h>
#include <filament/VertexBuffer.h>
#include <filament/View.h>
#include <filament/LightManager.h>
#include <gltfio/AssetLoader.h>
#include <gltfio/FilamentAsset.h>
#include <gltfio/ResourceLoader.h>
#include <camutils/Manipulator.h>
#include <utils/NameComponentManager.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <math/mat3.h>
#include <math/norm.h>
#include <fstream>
#include <iostream>
#include <string>
#include <chrono>
#include "ResourceBuffer.hpp"
#include "scene/SceneManager.hpp"
#include "ThreadPool.hpp"
namespace thermion
{
typedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point_t;
using namespace std::chrono;
using namespace gltfio;
using namespace camutils;
class FilamentViewer
{
typedef int32_t EntityId;
public:
FilamentViewer(const void *context, const ResourceLoaderWrapperImpl *const resourceLoaderWrapper, void *const platform = nullptr, const char *uberArchivePath = nullptr);
~FilamentViewer();
View* createView();
View* getViewAt(int index);
void loadSkybox(const char *const skyboxUri);
void removeSkybox();
void loadIbl(const char *const iblUri, float intensity);
void removeIbl();
void rotateIbl(const math::mat3f &matrix);
void createIbl(float r, float g, float b, float intensity);
void render(
uint64_t frameTimeInNanos
);
void setFrameInterval(float interval);
void setMainCamera(View *view);
EntityId getMainCamera();
float getCameraFov(bool horizontal);
void setCameraFov(double fovDegrees, bool horizontal);
SwapChain* createSwapChain(const void *surface);
SwapChain* createSwapChain(uint32_t width, uint32_t height);
void destroySwapChain(SwapChain* swapChain);
RenderTarget* createRenderTarget(intptr_t colorTextureId, intptr_t depthTextureId, uint32_t width, uint32_t height);
void destroyRenderTarget(RenderTarget* renderTarget);
Renderer *getRenderer();
std::map<SwapChain*, std::vector<View*>> _renderable;
void setRenderable(View* view, SwapChain* swapChain, bool renderable);
void setBackgroundColor(const float r, const float g, const float b, const float a);
void setBackgroundImage(const char *resourcePath, bool fillHeight, uint32_t width, uint32_t height);
void clearBackgroundImage();
void setBackgroundImagePosition(float x, float y, bool clamp, uint32_t width, uint32_t height);
Engine* getEngine() {
return _engine;
}
void capture(View* view, uint8_t *out, bool useFence, SwapChain* swapChain, void (*onComplete)());
void capture(View* view, uint8_t *out, bool useFence, SwapChain* swapChain, RenderTarget* renderTarget, void (*onComplete)());
SceneManager *const getSceneManager()
{
return (SceneManager *const)_sceneManager;
}
SwapChain* getSwapChainAt(int index) {
if(index < _swapChains.size()) {
return _swapChains[index];
}
Log("Error: index %d is greater than available swapchains", index);
return nullptr;
}
private:
const ResourceLoaderWrapperImpl *const _resourceLoaderWrapper;
Scene *_scene = nullptr;
Engine *_engine = nullptr;
thermion::ThreadPool *_tp = nullptr;
Renderer *_renderer = nullptr;
SceneManager *_sceneManager = nullptr;
std::vector<RenderTarget*> _renderTargets;
std::vector<SwapChain*> _swapChains;
std::vector<View*> _views;
std::mutex _renderMutex; // mutex to ensure thread safety when removing assets
Texture *_skyboxTexture = nullptr;
Skybox *_skybox = nullptr;
Texture *_iblTexture = nullptr;
IndirectLight *_indirectLight = nullptr;
float _frameInterval = 1000.0 / 60.0;
// Camera properties
Camera *_mainCamera = nullptr; // the default camera added to every scene. If you want the *active* camera, access via View.
// background image properties
uint32_t _imageHeight = 0;
uint32_t _imageWidth = 0;
filament::math::mat4f _imageScale;
Texture *_imageTexture = nullptr;
Texture *_dummyImageTexture = nullptr;
utils::Entity _imageEntity;
VertexBuffer *_imageVb = nullptr;
IndexBuffer *_imageIb = nullptr;
Material *_imageMaterial = nullptr;
TextureSampler _imageSampler;
void loadKtx2Texture(std::string path, ResourceBuffer data);
void loadKtxTexture(std::string path, ResourceBuffer data);
void loadPngTexture(std::string path, ResourceBuffer data);
void loadTextureFromPath(std::string path);
void savePng(void *data, size_t size, int frameNumber);
void createBackgroundImage();
time_point_t _fpsCounterStartTime = std::chrono::high_resolution_clock::now();
std::mutex _imageMutex;
double _cumulativeAnimationUpdateTime = 0;
int _frameCount = 0;
int _skippedFrames = 0;
};
struct FrameCallbackData
{
FilamentViewer *viewer;
uint32_t frameNumber;
};
}

View File

@@ -0,0 +1,48 @@
#pragma once
#include <filament/Engine.h>
#include <filament/Renderer.h>
#include <filament/View.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <math/mat3.h>
#include <math/norm.h>
#include <fstream>
#include <iostream>
#include <string>
#include <chrono>
#include "scene/SceneManager.hpp"
namespace thermion
{
typedef std::chrono::time_point<std::chrono::high_resolution_clock> time_point_t;
using namespace std::chrono;
class RenderTicker
{
public:
RenderTicker(filament::Renderer renderer, thermion::SceneManager sceneManager) : mRenderer(renderer), mSceneManager(sceneManager) { }
~RenderTicker();
void render(
uint64_t frameTimeInNanos
);
void setRenderable(SwapChain *swapChain, View **view, uint8_t numViews);
private:
std::mutex mMutex;
Renderer *mRenderer = nullptr;
SceneManager *mSceneManager = nullptr;
std::vector<SwapChain*> mSwapChains;
std::map<SwapChain*, std::vector<View*>> mRenderable;
};
}

View File

@@ -8,7 +8,7 @@ extern "C"
#include <stdint.h>
#include "APIExport.h"
typedef TRenderTicker TRenderTicker;
typedef int32_t EntityId;
typedef struct TCamera TCamera;
typedef struct TEngine TEngine;
@@ -110,6 +110,11 @@ extern "C"
double z;
} double3;
typedef struct {
double3 col1;
double3 col2;
double3 col3;
} double3x3;
typedef struct {
double x;

View File

@@ -21,10 +21,11 @@ enum TBackend {
BACKEND_NOOP = 4, //!< Selects the no-op driver for testing purposes.
};
EMSCRIPTEN_KEEPALIVE TEngine *Engine_create(TBackend backend);
EMSCRIPTEN_KEEPALIVE TEngine *Engine_create(TBackend backend, void* platform, void* sharedContext, uint8_t stereoscopicEyeCount, bool disableHandleUseAfterFreeCheck);
EMSCRIPTEN_KEEPALIVE TRenderer *Engine_createRenderer(TEngine *tEngine);
EMSCRIPTEN_KEEPALIVE TSwapChain *Engine_createSwapChain(TEngine *tEngine, void *window, uint64_t flags);
EMSCRIPTEN_KEEPALIVE TSwapChain *Engine_createHeadlessSwapChain(TEngine *tEngine, uint32_t width, uint32_t height, uint64_t flags);
EMSCRIPTEN_KEEPALIVE void Engine_destroySwapChain(TEngine *tEngine, TSwapChain *tSwapChain);
EMSCRIPTEN_KEEPALIVE TCamera *Engine_createCamera(TEngine* tEngine);
EMSCRIPTEN_KEEPALIVE TView *Engine_createView(TEngine *tEngine);

View File

@@ -23,6 +23,7 @@ EMSCRIPTEN_KEEPALIVE TFilamentAsset *GltfAssetLoader_load(
uint8_t numInstances
);
EMSCRIPTEN_KEEPALIVE TMaterialInstance *GltfAssetLoader_getMaterialInstance(TRenderableManager *tRenderableManager, TFilamentAsset *tAsset);
EMSCRIPTEN_KEEPALIVE TMaterialProvider *GltfAssetLoader_getMaterialProvider(TGltfAssetLoader *tAssetLoader);
#ifdef __cplusplus
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include "APIExport.h"
#include "APIBoundaryTypes.h"
#include "TMaterialInstance.h"
#include "TTexture.h"
#include "ResourceBuffer.hpp"
#include "MathUtils.hpp"
#ifdef __cplusplus
extern "C"
{
#endif
EMSCRIPTEN_KEEPALIVE void IndirectLight_setRotation(TIndirectLight TIndirectLight, double3x3 rotation);
#ifdef __cplusplus
}
#endif

View File

@@ -21,6 +21,11 @@ EMSCRIPTEN_KEEPALIVE TRenderTarget *RenderTarget_create(
TTexture *depth
);
EMSCRIPTEN_KEEPALIVE void RenderTarget_destroy(
TEngine *tEngine,
TRenderTarget *tRenderTarget
);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,17 @@
#pragma once
#include "APIExport.h"
#include "APIBoundaryTypes.h"
#ifdef __cplusplus
extern "C"
{
#endif
EMSCRIPTEN_KEEPALIVE TRenderTicker *RenderTicker_create(TRenderer *tRenderer, TSceneManager *tSceneManager);
EMSCRIPTEN_KEEPALIVE void RenderTicker_render(TRenderTicker *tRenderTicker, uint64_t frameTimeInNanos);
EMSCRIPTEN_KEEPALIVE void RenderTicker_setRenderable(TRenderTicker *tFilamentRender, TSwapChain *swapChain, TView **views, uint8_t numViews);
#ifdef __cplusplus
}
#endif

View File

@@ -23,6 +23,13 @@ EMSCRIPTEN_KEEPALIVE void Renderer_readPixels(
TPixelDataType tPixelDataType,
uint8_t *out
);
EMSCRIPTEN_KEEPALIVE void Renderer_setFrameInterval(
TRenderer *tRenderer,
float headRoomRatio,
float scaleRate,
uint8_t history,
uint8_t interval
);

View File

@@ -1,85 +0,0 @@
#ifndef _FLUTTER_FILAMENT_API_H
#define _FLUTTER_FILAMENT_API_H
#include "APIExport.h"
#include "APIBoundaryTypes.h"
#include "TMaterialInstance.h"
#include "ResourceBuffer.hpp"
#include "MathUtils.hpp"
#ifdef __cplusplus
extern "C"
{
#endif
EMSCRIPTEN_KEEPALIVE TViewer *Viewer_create(const void *const context, const void *const loader, void *const platform, const char *uberArchivePath);
EMSCRIPTEN_KEEPALIVE TRenderer *Viewer_getRenderer(TViewer *tViewer);
EMSCRIPTEN_KEEPALIVE void Viewer_destroy(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE TSceneManager *Viewer_getSceneManager(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE TRenderTarget* Viewer_createRenderTarget(TViewer *viewer, intptr_t colorTextureId, intptr_t depthTextureId, uint32_t width, uint32_t height);
EMSCRIPTEN_KEEPALIVE void Viewer_destroyRenderTarget(TViewer *viewer, TRenderTarget* tRenderTarget);
EMSCRIPTEN_KEEPALIVE TSwapChain *Viewer_createSwapChain(TViewer *viewer, const void *const window);
EMSCRIPTEN_KEEPALIVE TSwapChain *Viewer_createHeadlessSwapChain(TViewer *viewer, uint32_t width, uint32_t height);
EMSCRIPTEN_KEEPALIVE void Viewer_destroySwapChain(TViewer *viewer, TSwapChain* swapChain);
EMSCRIPTEN_KEEPALIVE void Viewer_render(
TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void Viewer_capture(
TViewer *viewer,
TView *view,
TSwapChain *swapChain,
uint8_t *pixelBuffer,
bool useFence,
void (*callback)(void));
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderTarget(
TViewer *viewer,
TView *view,
TSwapChain *swapChain,
TRenderTarget *renderTarget,
uint8_t *pixelBuffer,
bool useFence,
void (*callback)(void));
EMSCRIPTEN_KEEPALIVE TView* Viewer_createView(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE TView* Viewer_getViewAt(TViewer *viewer, int index);
EMSCRIPTEN_KEEPALIVE void Viewer_setMainCamera(TViewer *tViewer, TView *tView);
EMSCRIPTEN_KEEPALIVE TSwapChain* Viewer_getSwapChainAt(TViewer *tViewer, int index);
EMSCRIPTEN_KEEPALIVE void Viewer_setViewRenderable(TViewer *viewer, TSwapChain *swapChain, TView* view, bool renderable);
// Engine
EMSCRIPTEN_KEEPALIVE TEngine *Viewer_getEngine(TViewer* viewer);
EMSCRIPTEN_KEEPALIVE void clear_background_image(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void set_background_image(TViewer *viewer, const char *path, bool fillHeight);
EMSCRIPTEN_KEEPALIVE void set_background_image_position(TViewer *viewer, float x, float y, bool clamp);
EMSCRIPTEN_KEEPALIVE void set_background_color(TViewer *viewer, const float r, const float g, const float b, const float a);
EMSCRIPTEN_KEEPALIVE EntityId Viewer_getMainCamera(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void Viewer_loadSkybox(TViewer *viewer, const char *skyboxPath);
EMSCRIPTEN_KEEPALIVE void Viewer_removeSkybox(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void Viewer_loadIbl(TViewer *viewer, const char *iblPath, float intensity);
EMSCRIPTEN_KEEPALIVE void Viewer_removeIbl(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void create_ibl(TViewer *viewer, float r, float g, float b, float intensity);
EMSCRIPTEN_KEEPALIVE void rotate_ibl(TViewer *viewer, float *rotationMatrix);
EMSCRIPTEN_KEEPALIVE void set_frame_interval(TViewer *viewer, float interval);
EMSCRIPTEN_KEEPALIVE void queue_relative_position_update_world_axis(TSceneManager *sceneManager, EntityId entity, float viewportX, float viewportY, float x, float y, float z);
EMSCRIPTEN_KEEPALIVE void queue_position_update_from_viewport_coords(TSceneManager *sceneManager, TView *view, EntityId entity, float viewportX, float viewportY);
EMSCRIPTEN_KEEPALIVE void ios_dummy();
EMSCRIPTEN_KEEPALIVE void thermion_flutter_free(void *ptr);
EMSCRIPTEN_KEEPALIVE void add_collision_component(TSceneManager *sceneManager, EntityId entityId, void (*callback)(const EntityId entityId1, const EntityId entityId2), bool affectsCollidingTransform);
EMSCRIPTEN_KEEPALIVE void remove_collision_component(TSceneManager *sceneManager, EntityId entityId);
EMSCRIPTEN_KEEPALIVE void test_collisions(TSceneManager *sceneManager, EntityId entity);
EMSCRIPTEN_KEEPALIVE Aabb2 get_bounding_box(TSceneManager *sceneManager, TView *view, EntityId entity);
EMSCRIPTEN_KEEPALIVE void get_bounding_box_to_out(TSceneManager *sceneManager, TView *view, EntityId entity, float *minX, float *minY, float *maxX, float *maxY);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,7 +1,5 @@
#ifndef _DART_FILAMENT_FFI_API_H
#define _DART_FILAMENT_FFI_API_H
#pragma once
#include "ThermionDartApi.h"
#include "TView.h"
#include "TTexture.h"
#include "TMaterialProvider.h"
@@ -13,49 +11,30 @@ namespace thermion
{
#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 RenderLoop_create();
EMSCRIPTEN_KEEPALIVE void RenderLoop_destroy();
EMSCRIPTEN_KEEPALIVE void Viewer_createOnRenderThread(
void *const context,
void *const platform,
const char *uberArchivePath,
const void *const loader,
void (*renderCallback)(void *const renderCallbackOwner),
void *const renderCallbackOwner,
void (*callback)(TViewer *viewer));
EMSCRIPTEN_KEEPALIVE void Viewer_createViewRenderThread(TViewer *viewer, void (*onComplete)(TView *tView));
EMSCRIPTEN_KEEPALIVE void Viewer_destroyOnRenderThread(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void Viewer_createSwapChainRenderThread(TViewer *viewer, void *const surface, void (*onComplete)(TSwapChain *));
EMSCRIPTEN_KEEPALIVE void Viewer_createHeadlessSwapChainRenderThread(TViewer *viewer, uint32_t width, uint32_t height, void (*onComplete)(TSwapChain *));
EMSCRIPTEN_KEEPALIVE void Viewer_destroySwapChainRenderThread(TViewer *viewer, TSwapChain *swapChain, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_renderRenderThread(TViewer *viewer, TView *view, TSwapChain *swapChain);
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderThread(TViewer *viewer, TView *view, TSwapChain *swapChain, uint8_t *out, bool useFence, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderTargetRenderThread(TViewer *viewer, TView *view, TSwapChain *swapChain, TRenderTarget *renderTarget, uint8_t *out, bool useFence, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_requestFrameRenderThread(TViewer *viewer, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_loadIblRenderThread(TViewer *viewer, const char *iblPath, float intensity, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_removeIblRenderThread(TViewer *viewer, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_createRenderTargetRenderThread(TViewer *viewer, intptr_t colorTexture, intptr_t depthTexture, uint32_t width, uint32_t height, void (*onComplete)(TRenderTarget *));
EMSCRIPTEN_KEEPALIVE void Viewer_destroyRenderTargetRenderThread(TViewer *viewer, TRenderTarget *tRenderTarget, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_loadSkyboxRenderThread(TViewer *viewer, const char *skyboxPath, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Viewer_removeSkyboxRenderThread(TViewer *viewer, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void RenderTicker_renderRenderThread(TRenderTicker *tRenderTicker, uint64_t frameTimeInNanos,);
// EMSCRIPTEN_KEEPALIVE void RenderLoop_addTask(TRenderLoop* tRenderLoop, void (*task)());
EMSCRIPTEN_KEEPALIVE void Engine_createRenderThread(TBackend backend, void (*onComplete)(TEngine *));
EMSCRIPTEN_KEEPALIVE void Engine_createRenderThread(
TBackend backend,
void* platform,
void* sharedContext,
uint8_t stereoscopicEyeCount,
bool disableHandleUseAfterFreeCheck,
void (*onComplete)(TEngine *)
);
EMSCRIPTEN_KEEPALIVE void Engine_createRendererRenderThread(TEngine *tEngine, void (*onComplete)(TRenderer *));
EMSCRIPTEN_KEEPALIVE void Engine_createSwapChainRenderThread(TEngine *tEngine, void *window, uint64_t flags, void (*onComplete)(TSwapChain *));
EMSCRIPTEN_KEEPALIVE void Engine_createHeadlessSwapChainRenderThread(TEngine *tEngine, uint32_t width, uint32_t height, uint64_t flags, void (*onComplete)(TSwapChain *));
EMSCRIPTEN_KEEPALIVE void Engine_createCameraRenderThread(TEngine* tEngine, void (*onComplete)(TCamera *));
EMSCRIPTEN_KEEPALIVE void Engine_createViewRenderThread(TEngine *tEngine, void (*onComplete)(TView *));
EMSCRIPTEN_KEEPALIVE void Engine_buildMaterialRenderThread(TEngine *tEngine, const uint8_t *materialData, size_t length, void (*onComplete)(TMaterial *));
EMSCRIPTEN_KEEPALIVE void Engine_destroySwapChainRenderThread(TEngine *tEngine, TSwapChain *tSwapChain, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Engine_destroyMaterialRenderThread(TEngine *tEngine, TMaterial *tMaterial, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Engine_destroySkyboxRenderThread(TEngine *tEngine, TSkybox *tSkybox, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void Engine_destroyIndirectLightRenderThread(TEngine *tEngine, TIndirectLight *tIndirectLight, void (*onComplete)());
@@ -98,13 +77,6 @@ namespace thermion
EMSCRIPTEN_KEEPALIVE void View_setCameraRenderThread(TView *tView, TCamera *tCamera, void (*callback)());
FilamentRenderCallback make_render_callback_fn_pointer(FilamentRenderCallback);
EMSCRIPTEN_KEEPALIVE void set_rendering_render_thread(TViewer *viewer, bool rendering, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void set_frame_interval_render_thread(TViewer *viewer, float frameInterval);
EMSCRIPTEN_KEEPALIVE void set_background_color_render_thread(TViewer *viewer, const float r, const float g, const float b, const float a);
EMSCRIPTEN_KEEPALIVE void clear_background_image_render_thread(TViewer *viewer);
EMSCRIPTEN_KEEPALIVE void set_background_image_render_thread(TViewer *viewer, const char *path, bool fillHeight, void (*onComplete)());
EMSCRIPTEN_KEEPALIVE void set_background_image_position_render_thread(TViewer *viewer, float x, float y, bool clamp);
EMSCRIPTEN_KEEPALIVE void SceneManager_createGridRenderThread(TSceneManager *tSceneManager, TMaterial *tMaterial, void (*callback)(TSceneAsset *));
@@ -323,7 +295,7 @@ namespace thermion
int boneIndex,
const float *const transform,
void (*callback)(bool));
EMSCRIPTEN_KEEPALIVE void set_post_processing_render_thread(TViewer *viewer, bool enabled);
EMSCRIPTEN_KEEPALIVE void reset_to_rest_pose_render_thread(TSceneManager *sceneManager, EntityId entityId, void (*callback)());
EMSCRIPTEN_KEEPALIVE void GltfAssetLoader_createRenderThread(TEngine *tEngine, TMaterialProvider *tMaterialProvider, void (*callback)(TGltfAssetLoader *));
@@ -345,4 +317,3 @@ namespace thermion
}
#endif
#endif // _DART_FILAMENT_FFI_API_H

View File

@@ -0,0 +1,87 @@
#pragma once
#include <chrono>
#include <condition_variable>
#include <deque>
#include <future>
#include <mutex>
#include <thread>
#include "RenderTicker.hpp"
namespace thermion {
/**
* @brief A render loop implementation that manages rendering on a separate thread.
*
* This class handles frame rendering requests, viewer creation, and maintains
* a task queue for rendering operations.
*/
class RenderLoop {
public:
/**
* @brief Constructs a new RenderLoop and starts the render thread.
*/
explicit RenderLoop();
/**
* @brief Destroys the RenderLoop and stops the render thread.
*/
~RenderLoop();
/**
* @brief Requests a frame to be rendered.
*
* @param callback Callback function to be called after rendering completes
*/
void requestFrame(void (*callback)());
/**
* @brief Sets the render ticker used.
*/
void setRenderTicker(RenderTicker *renderTicker) {
mRenderTicker = renderTicker;
}
/**
* @brief Adds a task to the render thread's task queue.
*
* @param pt The packaged task to be executed
* @return std::future<Rt> Future for the task result
*/
template <class Rt>
auto add_task(std::packaged_task<Rt()>& pt) -> std::future<Rt>;
private:
/**
* @brief Main iteration of the render loop.
*/
void iter();
void (*_requestFrameRenderCallback)() = nullptr;
bool _stop = false;
std::mutex _mutex;
std::mutex _taskMutex;
std::condition_variable _cv;
std::deque<std::function<void()>> _tasks;
std::chrono::high_resolution_clock::time_point _lastFrameTime;
int _frameCount = 0;
float _accumulatedTime = 0.0f;
float _fps = 0.0f;
std::thread* t = nullptr;
RenderTicker* mRenderTicker = nullptr;
};
// Template implementation
template <class Rt>
auto RenderLoop::add_task(std::packaged_task<Rt()>& pt) -> std::future<Rt> {
std::unique_lock<std::mutex> lock(_taskMutex);
auto ret = pt.get_future();
_tasks.push_back([pt = std::make_shared<std::packaged_task<Rt()>>(
std::move(pt))]
{ (*pt)(); });
_cv.notify_one();
return ret;
}
} // namespace thermion

View File

@@ -0,0 +1,103 @@
#if __APPLE__
#include "TargetConditionals.h"
#endif
#ifdef _WIN32
#pragma comment(lib, "Ws2_32.lib")
#endif
#include <filament/Camera.h>
#include <filament/SwapChain.h>
#include <backend/DriverEnums.h>
#include <backend/platforms/OpenGLPlatform.h>
#ifdef __EMSCRIPTEN__
#include <backend/platforms/PlatformWebGL.h>
#include <emscripten/emscripten.h>
#include <emscripten/bind.h>
#include <emscripten/html5.h>
#include <emscripten/threading.h>
#include <emscripten/val.h>
#endif
#include <filament/Engine.h>
#include <filament/Options.h>
#include <filament/Renderer.h>
#include <filament/View.h>
#include <filament/RenderableManager.h>
#include <iostream>
#include <streambuf>
#include <sstream>
#include <istream>
#include <fstream>
#include <filesystem>
#include <mutex>
#include <iomanip>
#include <unordered_set>
#include "Log.hpp"
#include "RenderTicker.hpp"
namespace thermion
{
using namespace filament;
using namespace filament::math;
using namespace utils;
using namespace std::chrono;
using std::string;
static constexpr filament::math::float4 sFullScreenTriangleVertices[3] = {
{-1.0f, -1.0f, 1.0f, 1.0f},
{3.0f, -1.0f, 1.0f, 1.0f},
{-1.0f, 3.0f, 1.0f, 1.0f}};
static const uint16_t sFullScreenTriangleIndices[3] = {0, 1, 2};
void RenderTicker::setRenderable(SwapChain *swapChain, View **views, uint8_t numViews) {
{
std::lock_guard lock(mMutex);
auto swapChainViews = mRenderable[swapChain];
swapChainViews.clear();
for(int i = 0; i < numViews; i++) {
swapChainViews.push_back(views[i]);
}
mRenderable[swapChain] = swapChainViews;
}
void RenderTicker::render(uint64_t frameTimeInNanos)
{
std::lock_guard lock(mMutex);
mSceneManager->update();
for (auto swapChain : mSwapChains)
{
auto views = mRenderable[swapChain];
if (views.size() > 0)
{
bool beginFrame = mRenderer->beginFrame(swapChain, frameTimeInNanos);
if (beginFrame)
{
for (auto view : views)
{
mRenderer->render(view);
}
}
mRenderer->endFrame();
}
}
#ifdef __EMSCRIPTEN__
_engine->execute();
#endif
}
} // namespace thermion

View File

@@ -44,9 +44,17 @@ namespace thermion
uint64_t TSWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER = filament::backend::SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER;
uint64_t TSWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER = filament::backend::SWAP_CHAIN_CONFIG_HAS_STENCIL_BUFFER;
EMSCRIPTEN_KEEPALIVE TEngine *Engine_create(TBackend backend)
EMSCRIPTEN_KEEPALIVE TEngine *Engine_create(
TBackend backend,
void* platform,
void* sharedContext,
uint8_t stereoscopicEyeCount,
bool disableHandleUseAfterFreeCheck)
{
auto *engine = filament::Engine::create(static_cast<filament::Engine::Backend>(backend));
filament::Engine::Config config;
config.stereoscopicEyeCount = stereoscopicEyeCount;
config.disableHandleUseAfterFreeCheck = disableHandleUseAfterFreeCheck;
auto *engine = filament::Engine::create(static_cast<filament::Engine::Backend>(backend), platform, sharedContext, &config);
return reinterpret_cast<TEngine *>(engine);
}
@@ -71,6 +79,12 @@ namespace thermion
return reinterpret_cast<TSwapChain *>(swapChain);
}
EMSCRIPTEN_KEEPALIVE void Engine_destroySwapChain(TEngine *tEngine, TSwapChain *tSwapChain) {
auto *engine = reinterpret_cast<Engine *>(tEngine);
auto *swapChain = reinterpret_cast<SwapChain *>(tSwapChain);
engine->destroy(swapChain);
}
EMSCRIPTEN_KEEPALIVE TView *Engine_createView(TEngine *tEngine)
{
auto *engine = reinterpret_cast<Engine *>(tEngine);
@@ -279,12 +293,21 @@ namespace thermion
EMSCRIPTEN_KEEPALIVE void Engine_destroySkybox(TEngine *tEngine, TSkybox *tSkybox) {
auto *engine = reinterpret_cast<filament::Engine *>(tEngine);
auto *skybox = reinterpret_cast<filament::Skybox *>(tSkybox);
if(skybox->getTexture()) {
engine->destroy(skybox->getTexture());
}
engine->destroy(skybox);
}
EMSCRIPTEN_KEEPALIVE void Engine_destroyIndirectLight(TEngine *tEngine, TIndirectLight *tIndirectLight) {
auto *engine = reinterpret_cast<filament::Engine *>(tEngine);
auto *indirectLight = reinterpret_cast<filament::IndirectLight *>(tIndirectLight);
if(indirectLight->getReflectionsTexture()) {
engine->destroy(indirectLight->getReflectionsTexture());
}
if(indirectLight->getIrradianceTexture()) {
engine->destroy(indirectLight->getIrradianceTexture());
}
engine->destroy(indirectLight);
}

View File

@@ -111,6 +111,12 @@ EMSCRIPTEN_KEEPALIVE TMaterialInstance *GltfAssetLoader_getMaterialInstance(TRen
return reinterpret_cast<TMaterialInstance*>(mi);
}
EMSCRIPTEN_KEEPALIVE TMaterialProvider *GltfAssetLoader_getMaterialProvider(TGltfAssetLoader *tAssetLoader) {
auto *assetLoader = reinterpret_cast<gltfio::AssetLoader>(tAssetLoader);
auto materialProvider = assetLoader->getMaterialProvider();
return reinterpret_cast<TMaterialProvider *>(&materialProvider);
}
#ifdef __cplusplus
}
}

View File

@@ -0,0 +1,41 @@
#include "c_api/TScene.h"
#include <filament/Engine.h>
#include <filament/Fence.h>
#include <filament/IndirectLight.h>
#include <filament/Material.h>
#include <filament/Scene.h>
#include <filament/Skybox.h>
#include <filament/Texture.h>
#include <filament/TextureSampler.h>
#include <filament/TransformManager.h>
#include <filament/View.h>
#include <gltfio/FilamentAsset.h>
#include <gltfio/FilamentInstance.h>
#include "Log.hpp"
#ifdef __cplusplus
namespace thermion
{
extern "C"
{
using namespace filament;
#endif
EMSCRIPTEN_KEEPALIVE void IndirectLight_setRotation(TIndirectLight *tIndirectLight, double3x3 rotation)
{
auto *indirectLight = reinterpret_cast<filament::IndirectLight *>(tIndirectLight);
const filament::math::mat3f fRotation {
filament::math::float3 { rotation.col1.x, rotation.col1.y, rotation.col1.z },
filament::math::float3 { rotation.col2.x, rotation.col2.y, rotation.col2.z },
filament::math::float3 { rotation.col3.x, rotation.col3.y, rotation.col3.z },
};
indirectLight->setRotation(fRotation);
}
#ifdef __cplusplus
}
}
#endif

View File

@@ -34,6 +34,16 @@ namespace thermion
return reinterpret_cast<TRenderTarget *>(rt);
}
EMSCRIPTEN_KEEPALIVE void RenderTarget_destroy(
TEngine *tEngine,
TRenderTarget *tRenderTarget
) {
auto engine = reinterpret_cast<filament::Engine *>(tEngine);
auto *renderTarget = reinterpret_cast<filament::RenderTarget *>(tRenderTarget);
engine->destroy(renderTarget);
}
#ifdef __cplusplus
}

View File

@@ -0,0 +1,38 @@
#ifdef _WIN32
#include "ThermionWin32.h"
#endif
#include <thread>
#include <functional>
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
#include "filament/LightManager.h"
#include "Log.hpp"
using namespace thermion;
extern "C"
{
#include "c_api/TRenderTicker.hpp"
EMSCRIPTEN_KEEPALIVE TRenderTicker *RenderTicker_create(TRenderer *tRenderer, TSceneManager *tSceneManager) {
auto *renderer = reinterpret_cast<filament::Renderer *>(tRenderer);
auto *sceneManager = reinterpret_cast<thermion::SceneManager *>(tSceneManager);
auto *renderTicker = new RenderTicker(renderer, sceneManager);
return reinterpret_cast<TRenderTicker *>(renderTicker);
}
EMSCRIPTEN_KEEPALIVE void RenderTicker_render(TRenderTicker *tRenderTicker, uint64_t frameTimeInNanos) {
auto *renderTicker = reinterpret_cast<RenderTicker *>
renderTicker->render(frameTimeInNanos);
}
EMSCRIPTEN_KEEPALIVE void RenderTicker_setRenderable(TRenderTicker *tRenderTicker, TSwapChain *swapChain, TView **views, uint8_t numViews) {
auto *renderTicker = reinterpret_cast<RenderTicker *>
renderTicker->setRenderable(swapChain, views, numViews);
}
}

View File

@@ -61,6 +61,22 @@ EMSCRIPTEN_KEEPALIVE void Renderer_renderStandaloneView(TRenderer *tRenderer, TV
renderer->renderStandaloneView(view);
}
EMSCRIPTEN_KEEPALIVE void Renderer_setFrameRateOptions(
TRenderer *tRenderer,
float headRoomRatio,
float scaleRate,
uint8_t history,
uint8_t interval
) {
auto *renderer = reinterpret_cast<filament::Renderer *>(tRenderer);
filament::Renderer::FrameRateOptions fro;
fro.headRoomRatio = headRoomRatio;
fro.scaleRate = scaleRate;
fro.interval = interval;
fro.interval = interval;
renderer->setFrameRateOptions(fro);
}
class CaptureCallbackHandler : public filament::backend::CallbackHandler
{
void post(void *user, Callback callback)

View File

@@ -1,415 +0,0 @@
#ifdef _WIN32
#include "ThermionWin32.h"
#endif
#include <thread>
#include <functional>
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#endif
#include "filament/LightManager.h"
#include "ResourceBuffer.hpp"
#include "FilamentViewer.hpp"
#include "Log.hpp"
#include "ThreadPool.hpp"
using namespace thermion;
extern "C"
{
#include "c_api/ThermionDartApi.h"
EMSCRIPTEN_KEEPALIVE TViewer *Viewer_create(const void *context, const void *const loader, void *const platform, const char *uberArchivePath)
{
const auto *loaderImpl = new ResourceLoaderWrapperImpl((ResourceLoaderWrapper *)loader);
auto viewer = new FilamentViewer(context, loaderImpl, platform, uberArchivePath);
return reinterpret_cast<TViewer *>(viewer);
}
EMSCRIPTEN_KEEPALIVE TEngine *Viewer_getEngine(TViewer *viewer)
{
auto *engine = reinterpret_cast<FilamentViewer *>(viewer)->getEngine();
return reinterpret_cast<TEngine *>(engine);
}
EMSCRIPTEN_KEEPALIVE TRenderer *Viewer_getRenderer(TViewer *tViewer) {
auto *viewer = reinterpret_cast<FilamentViewer *>(tViewer);
return reinterpret_cast<TRenderer *>(viewer->getRenderer());
}
EMSCRIPTEN_KEEPALIVE TRenderTarget *Viewer_createRenderTarget(TViewer *tViewer, intptr_t colorTexture, intptr_t depthTexture, uint32_t width, uint32_t height)
{
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto renderTarget = viewer->createRenderTarget(colorTexture, depthTexture, width, height);
return reinterpret_cast<TRenderTarget *>(renderTarget);
}
EMSCRIPTEN_KEEPALIVE void Viewer_destroyRenderTarget(TViewer *tViewer, TRenderTarget *tRenderTarget)
{
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto renderTarget = reinterpret_cast<RenderTarget *>(tRenderTarget);
viewer->destroyRenderTarget(renderTarget);
}
EMSCRIPTEN_KEEPALIVE void Viewer_destroy(TViewer *tViewer)
{
auto *viewer = reinterpret_cast<FilamentViewer*>(tViewer);
delete viewer;
}
EMSCRIPTEN_KEEPALIVE void set_background_color(TViewer *viewer, const float r, const float g, const float b, const float a)
{
((FilamentViewer *)viewer)->setBackgroundColor(r, g, b, a);
}
EMSCRIPTEN_KEEPALIVE void clear_background_image(TViewer *viewer)
{
((FilamentViewer *)viewer)->clearBackgroundImage();
}
EMSCRIPTEN_KEEPALIVE void set_background_image(TViewer *viewer, const char *path, bool fillHeight)
{
((FilamentViewer *)viewer)->setBackgroundImage(path, fillHeight, 100, 100);
}
EMSCRIPTEN_KEEPALIVE void set_background_image_position(TViewer *viewer, float x, float y, bool clamp)
{
((FilamentViewer *)viewer)->setBackgroundImagePosition(x, y, clamp, 100, 100);
}
EMSCRIPTEN_KEEPALIVE void Viewer_loadSkybox(TViewer *viewer, const char *skyboxPath)
{
((FilamentViewer *)viewer)->loadSkybox(skyboxPath);
}
EMSCRIPTEN_KEEPALIVE void Viewer_removeSkybox(TViewer *viewer)
{
((FilamentViewer *)viewer)->removeSkybox();
}
EMSCRIPTEN_KEEPALIVE void create_ibl(TViewer *viewer, float r, float g, float b, float intensity)
{
((FilamentViewer *)viewer)->createIbl(r, g, b, intensity);
}
EMSCRIPTEN_KEEPALIVE void Viewer_loadIbl(TViewer *viewer, const char *iblPath, float intensity)
{
((FilamentViewer *)viewer)->loadIbl(iblPath, intensity);
}
EMSCRIPTEN_KEEPALIVE void Viewer_removeIbl(TViewer *viewer)
{
((FilamentViewer *)viewer)->removeIbl();
}
EMSCRIPTEN_KEEPALIVE void rotate_ibl(TViewer *viewer, float *rotationMatrix)
{
math::mat3f matrix(rotationMatrix[0], rotationMatrix[1],
rotationMatrix[2],
rotationMatrix[3],
rotationMatrix[4],
rotationMatrix[5],
rotationMatrix[6],
rotationMatrix[7],
rotationMatrix[8]);
((FilamentViewer *)viewer)->rotateIbl(matrix);
}
EMSCRIPTEN_KEEPALIVE int get_instance_count(TSceneManager *sceneManager, EntityId entityId)
{
return ((SceneManager *)sceneManager)->getInstanceCount(entityId);
}
EMSCRIPTEN_KEEPALIVE void get_instances(TSceneManager *sceneManager, EntityId entityId, EntityId *out)
{
return ((SceneManager *)sceneManager)->getInstances(entityId, out);
}
EMSCRIPTEN_KEEPALIVE void Viewer_setMainCamera(TViewer *tViewer, TView *tView)
{
auto *viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto *view = reinterpret_cast<View *>(tView);
viewer->setMainCamera(view);
}
EMSCRIPTEN_KEEPALIVE EntityId Viewer_getMainCamera(TViewer *viewer)
{
return ((FilamentViewer *)viewer)->getMainCamera();
}
EMSCRIPTEN_KEEPALIVE float get_camera_fov(TCamera *camera, bool horizontal)
{
auto cam = reinterpret_cast<filament::Camera *>(camera);
return cam->getFieldOfViewInDegrees(horizontal ? Camera::Fov::HORIZONTAL : Camera::Fov::VERTICAL);
}
EMSCRIPTEN_KEEPALIVE double get_camera_focal_length(TCamera *const camera)
{
auto cam = reinterpret_cast<filament::Camera *>(camera);
return cam->getFocalLength();
}
EMSCRIPTEN_KEEPALIVE void set_camera_projection_from_fov(TCamera *camera, double fovInDegrees, double aspect, double near, double far, bool horizontal)
{
auto cam = reinterpret_cast<filament::Camera *>(camera);
cam->setProjection(fovInDegrees, aspect, near, far, horizontal ? Camera::Fov::HORIZONTAL : Camera::Fov::VERTICAL);
}
EMSCRIPTEN_KEEPALIVE double4x4 get_camera_model_matrix(TCamera *camera)
{
const auto &mat = reinterpret_cast<filament::Camera *>(camera)->getModelMatrix();
return convert_mat4_to_double4x4(mat);
}
EMSCRIPTEN_KEEPALIVE double4x4 get_camera_view_matrix(TCamera *camera)
{
const auto &mat = reinterpret_cast<filament::Camera *>(camera)->getViewMatrix();
return convert_mat4_to_double4x4(mat);
}
EMSCRIPTEN_KEEPALIVE double4x4 get_camera_projection_matrix(TCamera *camera)
{
const auto &mat = reinterpret_cast<filament::Camera *>(camera)->getProjectionMatrix();
return convert_mat4_to_double4x4(mat);
}
EMSCRIPTEN_KEEPALIVE double4x4 get_camera_culling_projection_matrix(TCamera *camera)
{
const auto &mat = reinterpret_cast<filament::Camera *>(camera)->getCullingProjectionMatrix();
return convert_mat4_to_double4x4(mat);
}
EMSCRIPTEN_KEEPALIVE void set_camera_projection_matrix(TCamera *camera, double4x4 matrix, double near, double far)
{
auto cam = reinterpret_cast<filament::Camera *>(camera);
const auto &mat = convert_double4x4_to_mat4(matrix);
cam->setCustomProjection(mat, near, far);
}
EMSCRIPTEN_KEEPALIVE void Camera_setLensProjection(TCamera *camera, double near, double far, double aspect, double focalLength)
{
auto cam = reinterpret_cast<filament::Camera *>(camera);
cam->setLensProjection(focalLength, aspect, near, far);
}
EMSCRIPTEN_KEEPALIVE void Camera_setModelMatrix(TCamera *camera, double4x4 matrix)
{
auto cam = reinterpret_cast<filament::Camera *>(camera);
cam->setModelMatrix(convert_double4x4_to_mat4(matrix));
}
EMSCRIPTEN_KEEPALIVE double get_camera_near(TCamera *camera)
{
auto cam = reinterpret_cast<filament::Camera *>(camera);
return cam->getNear();
}
EMSCRIPTEN_KEEPALIVE double get_camera_culling_far(TCamera *camera)
{
auto cam = reinterpret_cast<filament::Camera *>(camera);
return cam->getCullingFar();
}
EMSCRIPTEN_KEEPALIVE const double *const get_camera_frustum(TCamera *camera)
{
const auto frustum = reinterpret_cast<filament::Camera *>(camera)->getFrustum();
const math::float4 *planes = frustum.getNormalizedPlanes();
double *array = (double *)calloc(24, sizeof(double));
for (int i = 0; i < 6; i++)
{
auto plane = planes[i];
array[i * 4] = double(plane.x);
array[i * 4 + 1] = double(plane.y);
array[i * 4 + 2] = double(plane.z);
array[i * 4 + 3] = double(plane.w);
}
return array;
}
EMSCRIPTEN_KEEPALIVE void set_camera_focus_distance(TCamera *camera, float distance)
{
auto *cam = reinterpret_cast<filament::Camera *>(camera);
cam->setFocusDistance(distance);
}
EMSCRIPTEN_KEEPALIVE void set_camera_exposure(TCamera *camera, float aperture, float shutterSpeed, float sensitivity)
{
auto *cam = reinterpret_cast<filament::Camera *>(camera);
cam->setExposure(aperture, shutterSpeed, sensitivity);
}
EMSCRIPTEN_KEEPALIVE void set_camera_model_matrix(TCamera *camera, double4x4 matrix)
{
auto *cam = reinterpret_cast<filament::Camera *>(camera);
const filament::math::mat4 &mat = convert_double4x4_to_mat4(matrix);
cam->setModelMatrix(mat);
}
EMSCRIPTEN_KEEPALIVE void Viewer_render(
TViewer *tViewer)
{
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
viewer->render(0);
}
EMSCRIPTEN_KEEPALIVE void Viewer_setViewRenderable(TViewer *tViewer, TSwapChain *tSwapChain, TView *tView, bool renderable)
{
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto swapChain = reinterpret_cast<SwapChain *>(tSwapChain);
auto *view = reinterpret_cast<View *>(tView);
viewer->setRenderable(view, swapChain, renderable);
}
EMSCRIPTEN_KEEPALIVE void Viewer_capture(
TViewer *tViewer,
TView *tView,
TSwapChain *tSwapChain,
uint8_t *pixelBuffer,
bool useFence,
void (*callback)(void))
{
#ifdef __EMSCRIPTEN__
useFence = true;
#endif
auto swapChain = reinterpret_cast<SwapChain *>(tSwapChain);
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto *view = reinterpret_cast<View *>(tView);
viewer->capture(view, pixelBuffer, useFence, swapChain, callback);
};
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderTarget(
TViewer *tViewer,
TView *tView,
TSwapChain *tSwapChain,
TRenderTarget *tRenderTarget,
uint8_t *pixelBuffer,
bool useFence,
void (*callback)(void))
{
#ifdef __EMSCRIPTEN__
useFence = true;
#endif
auto swapChain = reinterpret_cast<SwapChain *>(tSwapChain);
auto renderTarget = reinterpret_cast<RenderTarget *>(tRenderTarget);
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto *view = reinterpret_cast<View *>(tView);
viewer->capture(view, pixelBuffer, useFence, swapChain, renderTarget, callback);
};
EMSCRIPTEN_KEEPALIVE void set_frame_interval(
TViewer *viewer,
float frameInterval)
{
((FilamentViewer *)viewer)->setFrameInterval(frameInterval);
}
EMSCRIPTEN_KEEPALIVE void Viewer_destroySwapChain(TViewer *tViewer, TSwapChain *tSwapChain)
{
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto swapChain = reinterpret_cast<SwapChain *>(tSwapChain);
viewer->destroySwapChain(swapChain);
}
EMSCRIPTEN_KEEPALIVE TSwapChain *Viewer_createHeadlessSwapChain(TViewer *tViewer, uint32_t width, uint32_t height)
{
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto swapChain = viewer->createSwapChain(width, height);
return reinterpret_cast<TSwapChain *>(swapChain);
}
EMSCRIPTEN_KEEPALIVE TSwapChain *Viewer_createSwapChain(TViewer *tViewer, const void *const window)
{
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto swapChain = viewer->createSwapChain(window);
return reinterpret_cast<TSwapChain *>(swapChain);
}
EMSCRIPTEN_KEEPALIVE TSwapChain *Viewer_getSwapChainAt(TViewer *tViewer, int index)
{
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto swapChain = viewer->getSwapChainAt(index);
return reinterpret_cast<TSwapChain *>(swapChain);
}
EMSCRIPTEN_KEEPALIVE TView *Viewer_createView(TViewer *tViewer)
{
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto view = viewer->createView();
return reinterpret_cast<TView *>(view);
}
EMSCRIPTEN_KEEPALIVE TView *Viewer_getViewAt(TViewer *tViewer, int32_t index)
{
auto viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto view = viewer->getViewAt(index);
return reinterpret_cast<TView *>(view);
}
EMSCRIPTEN_KEEPALIVE TSceneManager *Viewer_getSceneManager(TViewer *tViewer)
{
auto *viewer = reinterpret_cast<FilamentViewer *>(tViewer);
auto *sceneManager = viewer->getSceneManager();
return reinterpret_cast<TSceneManager *>(sceneManager);
}
EMSCRIPTEN_KEEPALIVE void queue_position_update_from_viewport_coords(TSceneManager *sceneManager, TView *tView, EntityId entity, float viewportX, float viewportY)
{
auto *view = reinterpret_cast<View *>(tView);
((SceneManager *)sceneManager)->queueRelativePositionUpdateFromViewportVector(view, entity, viewportX, viewportY);
}
EMSCRIPTEN_KEEPALIVE void ios_dummy()
{
Log("Dummy called");
}
EMSCRIPTEN_KEEPALIVE void thermion_filament_free(void *ptr)
{
free(ptr);
}
EMSCRIPTEN_KEEPALIVE void add_collision_component(TSceneManager *sceneManager, EntityId entityId, void (*onCollisionCallback)(const EntityId entityId1, const EntityId entityId2), bool affectsCollidingTransform)
{
((SceneManager *)sceneManager)->addCollisionComponent(entityId, onCollisionCallback, affectsCollidingTransform);
}
EMSCRIPTEN_KEEPALIVE void remove_collision_component(TSceneManager *sceneManager, EntityId entityId)
{
((SceneManager *)sceneManager)->removeCollisionComponent(entityId);
}
EMSCRIPTEN_KEEPALIVE void test_collisions(TSceneManager *sceneManager, EntityId entity)
{
((SceneManager *)sceneManager)->testCollisions(entity);
}
EMSCRIPTEN_KEEPALIVE Aabb2 get_bounding_box(TSceneManager *sceneManager, TView *tView, EntityId entity)
{
auto view = reinterpret_cast<View *>(tView);
return ((SceneManager *)sceneManager)->getScreenSpaceBoundingBox(view, entity);
}
EMSCRIPTEN_KEEPALIVE void get_bounding_box_to_out(TSceneManager *sceneManager, TView *tView, EntityId entity, float *minX, float *minY, float *maxX, float *maxY)
{
auto view = reinterpret_cast<View *>(tView);
auto box = ((SceneManager *)sceneManager)->getScreenSpaceBoundingBox(view, entity);
*minX = box.minX;
*minY = box.minY;
*maxX = box.maxX;
*maxY = box.maxY;
}
EMSCRIPTEN_KEEPALIVE void thermion_flutter_free(void *ptr)
{
free(ptr);
}
}

View File

@@ -6,7 +6,6 @@
#include <filament/LightManager.h>
#include "c_api/APIBoundaryTypes.h"
#include "c_api/TAnimationManager.h"
#include "c_api/TEngine.h"
#include "c_api/TGltfAssetLoader.h"
@@ -19,7 +18,8 @@
#include "c_api/TView.h"
#include "c_api/ThermionDartRenderThreadApi.h"
#include "FilamentViewer.hpp"
#include "RenderTicker.hpp"
#include "rendering/RenderLoop.hpp"
#include "Log.hpp"
#include "ThreadPool.hpp"
@@ -28,165 +28,6 @@ using namespace thermion;
using namespace std::chrono_literals;
#include <time.h>
class RenderLoop
{
public:
explicit RenderLoop()
{
srand(time(NULL));
t = new std::thread([this]()
{ start(); });
}
~RenderLoop()
{
TRACE("Destroying RenderLoop");
_stop = true;
_cv.notify_one();
TRACE("Joining RenderLoop thread..");
t->join();
TRACE("RenderLoop destructor complete");
}
void start()
{
while (!_stop)
{
iter();
}
}
void destroyViewer() {
std::packaged_task<void()> lambda([=]() mutable
{
if(viewer) {
Viewer_destroy(viewer);
}
viewer = nullptr;
_renderCallback = nullptr;
_renderCallbackOwner = nullptr;
});
auto fut = add_task(lambda);
fut.wait();
}
void createViewer(
void *const context,
void *const platform,
const char *uberArchivePath,
const void *const loader,
void (*renderCallback)(void *),
void *const owner,
void (*callback)(TViewer *))
{
_renderCallback = renderCallback;
_renderCallbackOwner = owner;
std::packaged_task<void()> lambda([=]() mutable
{
if(viewer) {
Viewer_destroy(viewer);
}
viewer = Viewer_create(context, loader, platform, uberArchivePath);
callback(viewer); });
add_task(lambda);
}
void requestFrame(void (*callback)())
{
std::unique_lock<std::mutex> lock(_mutex);
this->_requestFrameRenderCallback = callback;
_cv.notify_one();
}
void iter()
{
{
std::unique_lock<std::mutex> lock(_mutex);
if (_requestFrameRenderCallback)
{
doRender();
lock.unlock();
this->_requestFrameRenderCallback();
this->_requestFrameRenderCallback = nullptr;
// Calculate and print FPS
auto currentTime = std::chrono::high_resolution_clock::now();
float deltaTime = std::chrono::duration<float, std::chrono::seconds::period>(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;
}
}
}
std::unique_lock<std::mutex> taskLock(_taskMutex);
if (!_tasks.empty())
{
auto task = std::move(_tasks.front());
_tasks.pop_front();
taskLock.unlock();
task();
taskLock.lock();
}
_cv.wait_for(taskLock, std::chrono::microseconds(2000), [this]
{ return !_tasks.empty() || _stop; });
}
void doRender()
{
Viewer_render(viewer);
if (_renderCallback)
{
_renderCallback(_renderCallbackOwner);
}
}
void setFrameIntervalInMilliseconds(float frameIntervalInMilliseconds)
{
_frameIntervalInMicroseconds = static_cast<int>(1000.0f * frameIntervalInMilliseconds);
}
template <class Rt>
auto add_task(std::packaged_task<Rt()> &pt) -> std::future<Rt>
{
std::unique_lock<std::mutex> lock(_taskMutex);
auto ret = pt.get_future();
_tasks.push_back([pt = std::make_shared<std::packaged_task<Rt()>>(
std::move(pt))]
{ (*pt)(); });
_cv.notify_one();
return ret;
}
TViewer *viewer = std::nullptr_t();
private:
void (*_requestFrameRenderCallback)() = nullptr;
bool _stop = false;
int _frameIntervalInMicroseconds = 1000000 / 60;
std::mutex _mutex;
std::mutex _taskMutex;
std::condition_variable _cv;
void (*_renderCallback)(void *const) = nullptr;
void *_renderCallbackOwner = nullptr;
std::deque<std::function<void()>> _tasks;
std::chrono::high_resolution_clock::time_point _lastFrameTime;
int _frameCount = 0;
float _accumulatedTime = 0.0f;
float _fps = 0.0f;
std::thread *t = nullptr;
};
extern "C"
{
@@ -205,145 +46,21 @@ extern "C"
TRACE("RenderLoop_destroy");
if (_rl)
{
_rl->destroyViewer();
_rl = nullptr;
}
}
EMSCRIPTEN_KEEPALIVE void Viewer_createOnRenderThread(
void *const context, void *const platform, const char *uberArchivePath,
const void *const loader,
void (*renderCallback)(void *const renderCallbackOwner),
void *const renderCallbackOwner,
void (*callback)(TViewer *))
{
TRACE("Viewer_createOnRenderThread");
_rl->createViewer(
context,
platform,
uberArchivePath,
loader,
renderCallback,
renderCallbackOwner,
callback
);
}
EMSCRIPTEN_KEEPALIVE void Viewer_destroyOnRenderThread(TViewer *viewer)
{
TRACE("Viewer_destroyOnRenderThread");
if (!_rl)
{
Log("Warning - cannot destroy viewer, no RenderLoop has been created");
} else {
_rl->destroyViewer();
}
}
EMSCRIPTEN_KEEPALIVE void Viewer_createViewRenderThread(TViewer *viewer, void (*onComplete)(TView *tView)) {
EMSCRIPTEN_KEEPALIVE void RenderTicker_renderRenderThread(TRenderTicker *tRenderTicker, , uint64_t frameTimeInNanos, void (*onComplete)()) {
std::packaged_task<void()> lambda(
[=]() mutable
{
auto *view = Viewer_createView(viewer);
onComplete(view);
RenderTicker_render(tRenderTicker, frameTimeInNanos);
onComplete();
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Viewer_createHeadlessSwapChainRenderThread(TViewer *viewer,
uint32_t width,
uint32_t height,
void (*onComplete)(TSwapChain *))
{
std::packaged_task<void()> lambda(
[=]() mutable
{
auto *swapChain = Viewer_createHeadlessSwapChain(viewer, width, height);
onComplete(swapChain);
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Viewer_createSwapChainRenderThread(TViewer *viewer,
void *const surface,
void (*onComplete)(TSwapChain *))
{
std::packaged_task<void()> lambda(
[=]() mutable
{
auto *swapChain = Viewer_createSwapChain(viewer, surface);
onComplete(swapChain);
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Viewer_destroySwapChainRenderThread(TViewer *viewer, TSwapChain *swapChain, void (*onComplete)())
{
std::packaged_task<void()> lambda(
[=]() mutable
{
Viewer_destroySwapChain(viewer, swapChain);
onComplete();
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Viewer_requestFrameRenderThread(TViewer *viewer, void (*onComplete)())
{
if (!_rl)
{
Log("No render loop!"); // PANIC?
}
else
{
_rl->requestFrame(onComplete);
}
}
EMSCRIPTEN_KEEPALIVE void Viewer_loadIblRenderThread(TViewer *viewer, const char *iblPath, float intensity, void (*onComplete)())
{
std::packaged_task<void()> lambda(
[=]() mutable
{
Viewer_loadIbl(viewer, iblPath, intensity);
onComplete();
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Viewer_removeIblRenderThread(TViewer *viewer, void (*onComplete)())
{
std::packaged_task<void()> lambda(
[=]() mutable
{
Viewer_removeIbl(viewer);
onComplete();
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Viewer_createRenderTargetRenderThread(TViewer *viewer, intptr_t colorTexture, intptr_t depthTexture, uint32_t width, uint32_t height, void (*onComplete)(TRenderTarget *))
{
std::packaged_task<void()> lambda(
[=]() mutable
{
auto renderTarget = Viewer_createRenderTarget(viewer, colorTexture, depthTexture, width, height);
onComplete(renderTarget);
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Viewer_destroyRenderTargetRenderThread(TViewer *tViewer, TRenderTarget *tRenderTarget, void (*onComplete)())
{
std::packaged_task<void()> lambda(
[=]() mutable
{
Viewer_destroyRenderTarget(tViewer, tRenderTarget);
onComplete();
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Engine_createRenderThread(TBackend backend, void (*onComplete)(TEngine *)) {
std::packaged_task<void()> lambda(
[=]() mutable
@@ -383,6 +100,16 @@ extern "C"
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Engine_destroySwapChainRenderThread(TEngine *tEngine, TSwapChain *tSwapChain, void (*onComplete)()) {
std::packaged_task<void()> lambda(
[=]() mutable
{
Engine_destroySwapChain(tEngine);
onComplete();
});
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Engine_createCameraRenderThread(TEngine* tEngine, void (*onComplete)(TCamera *)) {
std::packaged_task<void()> lambda(
@@ -446,6 +173,7 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Engine_destroyMaterialRenderThread(TEngine *tEngine, TMaterial *tMaterial, void (*onComplete)())
{
std::packaged_task<void()> lambda(
@@ -592,15 +320,6 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void
set_frame_interval_render_thread(TViewer *viewer, float frameIntervalInMilliseconds)
{
_rl->setFrameIntervalInMilliseconds(frameIntervalInMilliseconds);
std::packaged_task<void()> lambda([=]() mutable
{ ((FilamentViewer *)viewer)->setFrameInterval(frameIntervalInMilliseconds); });
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Viewer_renderRenderThread(TViewer *viewer, TView *tView, TSwapChain *tSwapChain)
{
std::packaged_task<void()> lambda([=]() mutable
@@ -608,30 +327,6 @@ extern "C"
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderThread(TViewer *viewer, TView *view, TSwapChain *tSwapChain, uint8_t *pixelBuffer, bool useFence, void (*onComplete)())
{
std::packaged_task<void()> lambda([=]() mutable
{ Viewer_capture(viewer, view, tSwapChain, pixelBuffer, useFence, onComplete); });
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void Viewer_captureRenderTargetRenderThread(TViewer *viewer, TView *view, TSwapChain *tSwapChain, TRenderTarget *tRenderTarget, uint8_t *pixelBuffer, bool useFence, void (*onComplete)())
{
std::packaged_task<void()> lambda([=]() mutable
{ Viewer_captureRenderTarget(viewer, view, tSwapChain, tRenderTarget, pixelBuffer, useFence, onComplete); });
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void
set_background_color_render_thread(TViewer *viewer, const float r, const float g,
const float b, const float a)
{
std::packaged_task<void()> lambda(
[=]() mutable
{ set_background_color(viewer, r, g, b, a); });
auto fut = _rl->add_task(lambda);
}
EMSCRIPTEN_KEEPALIVE void SceneManager_createGridRenderThread(TSceneManager *tSceneManager, TMaterial *tMaterial, void (*callback)(TSceneAsset *))
{
std::packaged_task<void()> lambda([=]() mutable

View File

@@ -0,0 +1,83 @@
#include "RenderLoop.hpp"
#include <functional>
#include <stdlib.h>
#include <time.h>
#include "Log.hpp"
namespace thermion {
RenderLoop::RenderLoop()
{
srand(time(NULL));
t = new std::thread([this]() {
while (!_stop) {
iter();
}
});
}
RenderLoop::~RenderLoop()
{
TRACE("Destroying RenderLoop");
_stop = true;
_cv.notify_one();
TRACE("Joining RenderLoop thread..");
t->join();
delete t;
TRACE("RenderLoop destructor complete");
}
void RenderLoop::requestFrame(void (*callback)())
{
std::unique_lock<std::mutex> lock(_mutex);
this->_requestFrameRenderCallback = callback;
_cv.notify_one();
}
void RenderLoop::iter()
{
{
std::unique_lock<std::mutex> lock(_mutex);
if (_requestFrameRenderCallback)
{
mRenderTicker->render();
lock.unlock();
this->_requestFrameRenderCallback();
this->_requestFrameRenderCallback = nullptr;
// Calculate and print FPS
auto currentTime = std::chrono::high_resolution_clock::now();
float deltaTime = std::chrono::duration<float, std::chrono::seconds::period>(currentTime - _lastFrameTime).count();
_lastFrameTime = currentTime;
_frameCount++;
_accumulatedTime += deltaTime;
if (_accumulatedTime >= 1.0f) // Update FPS every second
{
_fps = _frameCount / _accumulatedTime;
_frameCount = 0;
_accumulatedTime = 0.0f;
}
}
}
std::unique_lock<std::mutex> taskLock(_taskMutex);
if (!_tasks.empty())
{
auto task = std::move(_tasks.front());
_tasks.pop_front();
taskLock.unlock();
task();
taskLock.lock();
}
_cv.wait_for(taskLock, std::chrono::microseconds(2000), [this]
{ return !_tasks.empty() || _stop; });
}
} // namespace thermion