diff --git a/thermion_dart/lib/src/filament/src/implementation/ffi_scene.dart b/thermion_dart/lib/src/filament/src/implementation/ffi_scene.dart index 59999769..a8609942 100644 --- a/thermion_dart/lib/src/filament/src/implementation/ffi_scene.dart +++ b/thermion_dart/lib/src/filament/src/implementation/ffi_scene.dart @@ -46,113 +46,6 @@ class FFIScene extends Scene> { Scene_removeEntity(scene, entity); } - /// - /// - /// - @override - Future removeStencilHighlight(ThermionAsset asset) async { - if (!_highlightInstances.containsKey(asset)) { - _logger - .warning("No stencil highlight for asset (entity ${asset.entity})"); - return; - } - _logger - .info("Removing stencil highlight for asset (entity ${asset.entity})"); - - final highlight = _highlightInstances[asset]!; - _highlightInstances.remove(asset); - - await remove(highlight); - final materialInstance = await highlight.getMaterialInstanceAt(); - await FilamentApp.instance!.destroyAsset(highlight); - await materialInstance.destroy(); - - _logger - .info("Removed stencil highlight for asset (entity ${asset.entity})"); - } - - final _highlightInstances = {}; - - Future getAssetForHighlight(ThermionEntity entity) async { - for (final asset in _highlightInstances.keys) { - var highlightAsset = _highlightInstances[asset]!; - if (highlightAsset.entity == entity) { - return asset; - } - for (final child in await highlightAsset.getChildEntities()) { - if (child == entity) { - return asset; - } - } - } - return null; - } - - /// - /// - /// - @override - Future setStencilHighlight(ThermionAsset asset, - {double r = 1.0, - double g = 0.0, - double b = 0.0, - int? entity, - int primitiveIndex = 0}) async { - entity ??= asset.entity; - - if (_highlightInstances.containsKey(asset)) { - _logger - .info("Stencil highlight exists for asset (entity ${asset.entity})"); - var instance = _highlightInstances[asset]; - var highlightMaterialInstance = await instance!.getMaterialInstanceAt(); - await highlightMaterialInstance.setParameterFloat4( - "baseColorFactor", r, g, b, 1.0); - } else { - var highlightMaterialInstance = - await FilamentApp.instance!.createUnlitMaterialInstance(); - var highlightInstance = await asset - .createInstance(materialInstances: [highlightMaterialInstance]); - _highlightInstances[asset] = highlightInstance as FFIAsset; - await highlightInstance.setCastShadows(false); - await highlightInstance.setReceiveShadows(false); - - var sourceMaterialInstance = - await asset.getMaterialInstanceAt(entity: entity); - await sourceMaterialInstance.setStencilWriteEnabled(true); - await sourceMaterialInstance.setDepthWriteEnabled(true); - await sourceMaterialInstance - .setStencilOpDepthStencilPass(StencilOperation.REPLACE); - await sourceMaterialInstance - .setStencilCompareFunction(SamplerCompareFunction.A); - - await sourceMaterialInstance - .setStencilReferenceValue(View.STENCIL_HIGHLIGHT_REFERENCE_VALUE); - - await highlightMaterialInstance - .setStencilCompareFunction(SamplerCompareFunction.NE); - await highlightMaterialInstance - .setStencilReferenceValue(View.STENCIL_HIGHLIGHT_REFERENCE_VALUE); - await highlightMaterialInstance.setDepthCullingEnabled(true); - await highlightMaterialInstance.setParameterFloat4( - "baseColorFactor", r, g, b, 1.0); - - await add(highlightInstance); - - var transform = await FilamentApp.instance! - .getWorldTransform(highlightInstance.entity); - - await FilamentApp.instance!.setTransform(highlightInstance.entity, - Matrix4.diagonal3(Vector3(1.1, 1.1, 1.1)) * transform); - - await FilamentApp.instance!.setPriority(highlightInstance.entity, 7); - - await FilamentApp.instance!.setParent(highlightInstance.entity, entity); - - _logger - .info("Added stencil highlight for asset (entity ${asset.entity})"); - } - } - IndirectLight? _indirectLight; /// diff --git a/thermion_dart/lib/src/filament/src/implementation/ffi_view.dart b/thermion_dart/lib/src/filament/src/implementation/ffi_view.dart index e131e975..387f68f4 100644 --- a/thermion_dart/lib/src/filament/src/implementation/ffi_view.dart +++ b/thermion_dart/lib/src/filament/src/implementation/ffi_view.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'package:logging/logging.dart'; import 'package:thermion_dart/src/filament/src/implementation/ffi_texture.dart'; import 'package:thermion_dart/src/filament/src/interface/scene.dart'; @@ -9,18 +10,21 @@ import 'package:thermion_dart/thermion_dart.dart'; import 'ffi_camera.dart'; -class FFIView extends View { +class FFIView extends View> { late final _logger = Logger(this.runtimeType.toString()); int _renderOrder = 0; int get renderOrder => _renderOrder; final Pointer view; + + Pointer getNativeHandle() => view; + final FFIFilamentApp app; bool _renderable = false; bool get renderable => _renderable; - FFIRenderTarget? renderTarget; + RenderTarget? renderTarget; late CallbackHolder _onPickResultHolder; @@ -59,6 +63,7 @@ class FFIView extends View { @override Future setViewport(int width, int height) async { View_setViewport(view, width, height); + // await overlayView?.setViewport(width, height); } Future getRenderTarget() async { @@ -66,18 +71,20 @@ class FFIView extends View { } @override - Future setRenderTarget(covariant FFIRenderTarget? renderTarget) async { + Future setRenderTarget(RenderTarget? renderTarget) async { if (renderTarget != null) { - View_setRenderTarget(view, renderTarget.renderTarget); + View_setRenderTarget(view, renderTarget.getNativeHandle()); this.renderTarget = renderTarget; } else { View_setRenderTarget(view, nullptr); } + // await overlayView?.setRenderTarget(renderTarget); } @override - Future setCamera(FFICamera camera) async { - View_setCamera(view, camera.camera); + Future setCamera(Camera camera) async { + View_setCamera(view, camera.getNativeHandle()); + // await overlayView?.setCamera(camera.getNativeHandle()); } @override @@ -250,4 +257,102 @@ class FFIView extends View { Future setShadowsEnabled(bool enabled) async { View_setShadowsEnabled(this.view, enabled); } + + Pointer? overlayManager; + View? overlayView; + Scene? overlayScene; + RenderTarget? overlayRenderTarget; + Material? highlightMaterial; + + final _highlighted = {}; + + /// + /// + /// + @override + Future setStencilHighlight(ThermionAsset asset, + {double r = 1.0, + double g = 0.0, + double b = 0.0, + int? entity, + int primitiveIndex = 0}) async { + entity ??= asset.entity; + + + if (overlayScene == null) { + // overlayView = await FilamentApp.instance!.createView(); + overlayScene = await FilamentApp.instance!.createScene(); + // await overlayView!.setScene(overlayScene!); + // await overlayView!.setRenderTarget(await this.getRenderTarget()); + + final vp = await getViewport(); + overlayRenderTarget = + await FilamentApp.instance!.createRenderTarget(vp.width, vp.height); + overlayManager = OverlayManager_create( + app.engine, + app.renderer, + getNativeHandle(), + overlayScene!.getNativeHandle(), + overlayRenderTarget!.getNativeHandle()); + // await setBlendMode(BlendMode.transparent); + // await overlayView!.setBlendMode(BlendMode.transparent); + // await overlayView!.setCamera(await getCamera()); + // await overlayView!.setViewport(vp.width, vp.height); + // await setStencilBufferEnabled(true); + // await overlayView!.setStencilBufferEnabled(true); + RenderTicker_setOverlayManager(app.renderTicker, overlayManager!); + highlightMaterial ??= await FilamentApp.instance!.createMaterial( + File("/Users/nickfisher/Documents/thermion/materials/outline.filamat") + .readAsBytesSync()); + } + + // await sourceMaterialInstance.setStencilWriteEnabled(true); + // await sourceMaterialInstance + // .setStencilOpDepthStencilPass(StencilOperation.REPLACE); + // await sourceMaterialInstance + // .setStencilReferenceValue(View.STENCIL_HIGHLIGHT_REFERENCE_VALUE); + // await sourceMaterialInstance.setDepthCullingEnabled(false); + // await sourceMaterialInstance.setDepthFunc(SamplerCompareFunction.A); + // await sourceMaterialInstance + // .setStencilCompareFunction(SamplerCompareFunction.A); + + var highlightMaterialInstance = await highlightMaterial!.createInstance(); + + await highlightMaterialInstance.setDepthCullingEnabled(true); + await highlightMaterialInstance.setDepthWriteEnabled(true); + + OverlayManager_addComponent( + overlayManager!, entity, highlightMaterialInstance.getNativeHandle()); + + _highlighted[asset] = highlightMaterialInstance; + + _logger.info("Added stencil highlight for asset (entity ${asset.entity})"); + } + + /// + /// + /// + @override + Future removeStencilHighlight(ThermionAsset asset) async { + if (!_highlighted.containsKey(asset)) { + _logger + .warning("No stencil highlight for asset (entity ${asset.entity})"); + return; + } + final materialInstance = _highlighted[asset]!; + _highlighted.remove(asset); + _logger + .info("Removing stencil highlight for asset (entity ${asset.entity})"); + + OverlayManager_removeComponent(overlayManager!, asset.entity); + + await materialInstance.destroy(); + + _logger + .info("Removed stencil highlight for asset (entity ${asset.entity})"); + } + + void setName(String name) { + View_setName(getNativeHandle(), name.toNativeUtf8().cast()); + } } diff --git a/thermion_dart/lib/src/filament/src/interface/scene.dart b/thermion_dart/lib/src/filament/src/interface/scene.dart index 760c2faf..efa31516 100644 --- a/thermion_dart/lib/src/filament/src/interface/scene.dart +++ b/thermion_dart/lib/src/filament/src/interface/scene.dart @@ -24,26 +24,6 @@ abstract class Scene extends NativeHandle { /// Future removeEntity(ThermionEntity entity); - /// - /// - /// - Future getAssetForHighlight(ThermionEntity entity); - - /// Renders an outline around [entity] with the given color. - /// - /// - Future setStencilHighlight(ThermionAsset asset, - {double r = 1.0, - double g = 0.0, - double b = 0.0, - int? entity, - int primitiveIndex = 0}); - - /// Removes the outline around [entity]. Noop if there was no highlight. - /// - /// - Future removeStencilHighlight(ThermionAsset asset); - /// /// /// diff --git a/thermion_dart/lib/src/filament/src/interface/view.dart b/thermion_dart/lib/src/filament/src/interface/view.dart index a512e836..920f9d70 100644 --- a/thermion_dart/lib/src/filament/src/interface/view.dart +++ b/thermion_dart/lib/src/filament/src/interface/view.dart @@ -1,4 +1,5 @@ import 'package:thermion_dart/src/filament/src/interface/layers.dart'; +import 'package:thermion_dart/src/filament/src/interface/native_handle.dart'; import 'package:thermion_dart/src/filament/src/interface/scene.dart'; import 'package:thermion_dart/thermion_dart.dart'; @@ -51,20 +52,24 @@ class Viewport { enum QualityLevel { LOW, MEDIUM, HIGH, ULTRA } -abstract class View { - +abstract class View extends NativeHandle { static int STENCIL_HIGHLIGHT_REFERENCE_VALUE = 1; /// Gets the scene currently associated with this View. /// /// Future getScene(); - + /// Sets the scene currently associated with this View. /// /// Future setScene(Scene scene); + /// Sets the (debug) name for this View. + /// + /// + void setName(String name); + Future getViewport(); Future setViewport(int width, int height); Future getRenderTarget(); @@ -87,6 +92,17 @@ abstract class View { Future setShadowsEnabled(bool enabled); Future setLayerVisibility(VisibilityLayers layer, bool visible); + /// Renders an outline around [entity] with the given color. + Future setStencilHighlight(ThermionAsset asset, + {double r = 1.0, + double g = 0.0, + double b = 0.0, + int? entity, + int primitiveIndex = 0}); + + /// Removes the outline around [entity]. Noop if there was no highlight. + Future removeStencilHighlight(ThermionAsset asset); + /// Sets the fog options for this view. /// Fog is disabled by default /// diff --git a/thermion_dart/native/include/RenderTicker.hpp b/thermion_dart/native/include/RenderTicker.hpp index 9381ffd8..0f30df1c 100644 --- a/thermion_dart/native/include/RenderTicker.hpp +++ b/thermion_dart/native/include/RenderTicker.hpp @@ -3,7 +3,7 @@ #include #include #include -#include // for std::pair +#include #include #include @@ -21,13 +21,18 @@ #include #include "scene/AnimationManager.hpp" +#include "components/OverlayComponentManager.hpp" namespace thermion { + + class RenderTicker { + using ViewAttachment = std::pair>; + public: RenderTicker( filament::Engine *engine, @@ -54,14 +59,21 @@ namespace thermion /// @param animationManager void removeAnimationManager(AnimationManager* animationManager); + /// @brief + /// @param overlayComponentManager + void addOverlayManager(OverlayComponentManager *overlayComponentManager) { + mOverlayComponentManager = overlayComponentManager; + } private: std::mutex mMutex; - filament::Engine *mEngine = nullptr; - filament::Renderer *mRenderer = nullptr; + filament::Engine *mEngine = std::nullptr_t(); + filament::Renderer *mRenderer = std::nullptr_t(); std::vector mAnimationManagers; - std::vector>> mRenderable; + OverlayComponentManager *mOverlayComponentManager = std::nullptr_t(); + std::vector mRenderable; std::chrono::high_resolution_clock::time_point mLastRender; + }; } \ No newline at end of file diff --git a/thermion_dart/native/include/c_api/TOverlayManager.h b/thermion_dart/native/include/c_api/TOverlayManager.h new file mode 100644 index 00000000..c602a70b --- /dev/null +++ b/thermion_dart/native/include/c_api/TOverlayManager.h @@ -0,0 +1,34 @@ + +#pragma once + +#include "APIExport.h" +#include "APIBoundaryTypes.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +EMSCRIPTEN_KEEPALIVE TOverlayManager *OverlayManager_create( + TEngine *tEngine, + TRenderer *tRenderer, + TView *tView, + TScene *tScene, + TRenderTarget *tRenderTarget +); + +EMSCRIPTEN_KEEPALIVE void OverlayManager_addComponent( + TOverlayManager *tOverlayManager, + EntityId entityId, + TMaterialInstance *tMaterialInstance +); + +EMSCRIPTEN_KEEPALIVE void OverlayManager_removeComponent( + TOverlayManager *tOverlayManager, + EntityId entityId +); + + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/thermion_dart/native/include/c_api/TView.h b/thermion_dart/native/include/c_api/TView.h index 5eb10091..b633220a 100644 --- a/thermion_dart/native/include/c_api/TView.h +++ b/thermion_dart/native/include/c_api/TView.h @@ -92,6 +92,7 @@ EMSCRIPTEN_KEEPALIVE void View_setFogOptions(TView *tView, TFogOptions tFogOptio typedef void (*PickCallback)(uint32_t requestId, EntityId entityId, float depth, float fragX, float fragY, float fragZ); EMSCRIPTEN_KEEPALIVE void View_pick(TView* tView, uint32_t requestId, uint32_t x, uint32_t y, PickCallback callback); +EMSCRIPTEN_KEEPALIVE void View_setName(TView* tView, const char *name); #ifdef __cplusplus } diff --git a/thermion_dart/native/include/components/OverlayComponentManager.hpp b/thermion_dart/native/include/components/OverlayComponentManager.hpp new file mode 100644 index 00000000..60fdaaa2 --- /dev/null +++ b/thermion_dart/native/include/components/OverlayComponentManager.hpp @@ -0,0 +1,145 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "c_api/APIBoundaryTypes.h" +#include "material/linear_depth.h" + +namespace thermion +{ + + /// @brief + class OverlayComponentManager : public utils::SingleInstanceComponentManager + { + public: + OverlayComponentManager( + filament::Engine *engine, + filament::View *view, + filament::Scene *scene, + filament::RenderTarget *renderTarget, + filament::Renderer *renderer) : mEngine(engine), mView(view), mScene(scene), mRenderTarget(renderTarget), mRenderer(renderer) + { + mDepthMaterial = filament::Material::Builder() + .package(LINEAR_DEPTH_LINEAR_DEPTH_DATA, LINEAR_DEPTH_LINEAR_DEPTH_SIZE) + .build(*engine); + mDepthMaterialInstance = mDepthMaterial->createInstance(); + } + + void addOverlayComponent(utils::Entity target, filament::MaterialInstance *materialInstance) + { + auto *color = mRenderTarget->getTexture(filament::RenderTarget::AttachmentPoint::COLOR); + materialInstance->setParameter("depth", color, mDepthSampler); + if (!hasComponent(target)) + { + utils::EntityInstanceBase::Type componentInstance = addComponent(target); + this->elementAt<0>(componentInstance) = materialInstance; + } + mScene->addEntity(target); + } + + void removeOverlayComponent(utils::Entity target) + { + if (hasComponent(target)) + { + removeComponent(target); + } + mScene->remove(target); + } + + void update() + { + if (!mView || !mScene || getComponentCount() == 0) + { + return; + } + auto &rm = mEngine->getRenderableManager(); + std::map> materials; + auto *scene = mView->getScene(); + auto *renderTarget = mView->getRenderTarget(); + mView->setRenderTarget(mRenderTarget); + mView->setScene(mScene); + + for (auto it = begin(); it < end(); it++) + { + const auto &entity = getEntity(it); + + auto ri = rm.getInstance(entity); + if (ri.isValid()) + { + + for (int i = 0; i < rm.getPrimitiveCount(ri); i++) + { + auto *existing = rm.getMaterialInstanceAt(ri, i); + materials[entity].push_back(existing); + rm.setMaterialInstanceAt(ri, i, mDepthMaterialInstance); + } + } + else + { + Log("WARNING: INVALID RENDERABLE"); + } + } + + mRenderer->render(mView); + + mView->setRenderTarget(renderTarget); + + for (auto it = begin(); it < end(); it++) + { + const auto &entity = getEntity(it); + auto componentInstance = getInstance(entity); + auto &materialInstance = elementAt<0>(componentInstance); + + auto ri = rm.getInstance(entity); + if (ri.isValid()) + { + for (int i = 0; i < rm.getPrimitiveCount(ri); i++) + { + rm.setMaterialInstanceAt(ri, i, materialInstance); + } + } + else + { + Log("WARNING: INVALID RENDERABLE"); + } + } + + mRenderer->render(mView); + + for (auto it = begin(); it < end(); it++) + { + const auto &entity = getEntity(it); + auto ri = rm.getInstance(entity); + for (int i = 0; i < rm.getPrimitiveCount(ri); i++) + { + rm.setMaterialInstanceAt(ri, i, materials[entity][i]); + } + } + mView->setScene(scene); + } + + private: + filament::Engine *mEngine = std::nullptr_t(); + filament::View *mView = std::nullptr_t(); + filament::Scene *mScene = std::nullptr_t(); + filament::RenderTarget *mRenderTarget = std::nullptr_t(); + filament::Renderer *mRenderer = std::nullptr_t(); + filament::Material *mDepthMaterial = std::nullptr_t(); + filament::MaterialInstance *mDepthMaterialInstance = std::nullptr_t(); + filament::TextureSampler mDepthSampler; + }; +} diff --git a/thermion_dart/native/src/c_api/TOverlayManager.cpp b/thermion_dart/native/src/c_api/TOverlayManager.cpp new file mode 100644 index 00000000..64cc31e9 --- /dev/null +++ b/thermion_dart/native/src/c_api/TOverlayManager.cpp @@ -0,0 +1,44 @@ +#ifdef __EMSCRIPTEN__ +#include +#endif + +#include "Log.hpp" + +#include +#include +#include +#include +#include + +#include "c_api/TOverlayManager.h" +#include "components/OverlayComponentManager.hpp" + +using namespace thermion; + +extern "C" +{ + + +EMSCRIPTEN_KEEPALIVE TOverlayManager *OverlayManager_create(TEngine *tEngine, TRenderer *tRenderer, TView *tView, TScene *tScene, TRenderTarget *tRenderTarget) { + auto *engine = reinterpret_cast(tEngine); + auto *view = reinterpret_cast(tView); + auto *scene = reinterpret_cast(tScene); + auto *renderer = reinterpret_cast(tRenderer); + auto *renderTarget = reinterpret_cast(tRenderTarget); + auto *overlayManager = new OverlayComponentManager(engine, view, scene, renderTarget, renderer); + return reinterpret_cast(overlayManager); +} + +EMSCRIPTEN_KEEPALIVE void OverlayManager_addComponent(TOverlayManager *tOverlayManager, EntityId entityId, TMaterialInstance *tMaterialInstance) { + auto *overlayManager = reinterpret_cast(tOverlayManager); + auto *materialInstance = reinterpret_cast(tMaterialInstance); + overlayManager->addOverlayComponent(utils::Entity::import(entityId), materialInstance); +} + + +EMSCRIPTEN_KEEPALIVE void OverlayManager_removeComponent(TOverlayManager *tOverlayManager, EntityId entityId) { + auto *overlayManager = reinterpret_cast(tOverlayManager); + overlayManager->removeOverlayComponent(utils::Entity::import(entityId)); +} + +} \ No newline at end of file diff --git a/thermion_dart/native/src/c_api/TRenderTicker.cpp b/thermion_dart/native/src/c_api/TRenderTicker.cpp index 13d0e93a..4cfa496e 100644 --- a/thermion_dart/native/src/c_api/TRenderTicker.cpp +++ b/thermion_dart/native/src/c_api/TRenderTicker.cpp @@ -55,4 +55,11 @@ EMSCRIPTEN_KEEPALIVE void RenderTicker_setRenderable(TRenderTicker *tRenderTicke renderTicker->setRenderable(swapChain, views, numViews); } +EMSCRIPTEN_KEEPALIVE void RenderTicker_setOverlayManager(TRenderTicker *tRenderTicker, TOverlayManager *tOverlayManager) { + auto *renderTicker = reinterpret_cast(tRenderTicker); + auto *overlayManager = reinterpret_cast(tOverlayManager); + renderTicker->addOverlayManager(overlayManager); +} + + } diff --git a/thermion_dart/native/src/c_api/TView.cpp b/thermion_dart/native/src/c_api/TView.cpp index 13021f83..fb244b04 100644 --- a/thermion_dart/native/src/c_api/TView.cpp +++ b/thermion_dart/native/src/c_api/TView.cpp @@ -279,6 +279,11 @@ namespace thermion view->setFogOptions(fogOptions); } + EMSCRIPTEN_KEEPALIVE void View_setName(TView* tView, const char *name) { + auto view = reinterpret_cast(tView); + view->setName(name); + } + #ifdef __cplusplus } }