From 53ff6c72f0961c3ff8d55c1687c4b9548296c8d5 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Sat, 7 Sep 2024 17:57:38 +0800 Subject: [PATCH] feat: move HighlightOverlay to nested class, move createGeometry to SceneManager, add queueRelativePositionUpdateFromViewportVector --- .../native/include/CustomGeometry.hpp | 46 +++++ thermion_dart/native/include/SceneManager.hpp | 92 ++++++++- thermion_dart/native/src/CustomGeometry.cpp | 92 +++++++++ thermion_dart/native/src/HighlightOverlay.cpp | 183 ++++++++++++++++++ thermion_dart/native/src/SceneManager.cpp | 130 +++++++++++-- 5 files changed, 523 insertions(+), 20 deletions(-) create mode 100644 thermion_dart/native/include/CustomGeometry.hpp create mode 100644 thermion_dart/native/src/CustomGeometry.cpp create mode 100644 thermion_dart/native/src/HighlightOverlay.cpp diff --git a/thermion_dart/native/include/CustomGeometry.hpp b/thermion_dart/native/include/CustomGeometry.hpp new file mode 100644 index 00000000..4fac5264 --- /dev/null +++ b/thermion_dart/native/include/CustomGeometry.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace thermion_filament +{ + + using namespace filament; + +// CustomGeometry.h +class CustomGeometry { +public: + CustomGeometry(float* vertices, uint32_t numVertices, uint16_t* indices, uint32_t numIndices, RenderableManager::PrimitiveType primitiveType, Engine* engine); + ~CustomGeometry(); + + void computeBoundingBox(); + VertexBuffer* vertexBuffer(); + IndexBuffer* indexBuffer(); + Box getBoundingBox() const; + + float* vertices; + uint32_t numVertices; + uint16_t* indices; + uint32_t numIndices; + Box boundingBox; + RenderableManager::PrimitiveType primitiveType; + +private: + Engine* _engine; + bool _vertexBufferFreed = false; + bool _indexBufferFreed = false; + +}; + + + +} \ No newline at end of file diff --git a/thermion_dart/native/include/SceneManager.hpp b/thermion_dart/native/include/SceneManager.hpp index 14742178..9626f543 100644 --- a/thermion_dart/native/include/SceneManager.hpp +++ b/thermion_dart/native/include/SceneManager.hpp @@ -16,10 +16,13 @@ #include #include +#include #include "material/gizmo.h" -#include "utils/NameComponentManager.h" + +#include "CustomGeometry.hpp" #include "Gizmo.hpp" + #include "GridOverlay.hpp" #include "ResourceBuffer.hpp" #include "components/CollisionComponentManager.hpp" @@ -46,10 +49,33 @@ namespace thermion_filament const ResourceLoaderWrapperImpl *const loader, Engine *engine, Scene *scene, - Scene *highlightScene, const char *uberArchivePath); ~SceneManager(); + class HighlightOverlay { + public: + HighlightOverlay(EntityId id, SceneManager* const sceneManager, Engine* const engine, float r, float g, float b); + ~HighlightOverlay(); + + bool isValid() { + return !_entity.isNull(); + } + + private: + MaterialInstance* _highlightMaterialInstance; + CustomGeometry* _newGeometry; + FilamentInstance* _newInstance; + Entity _entity; + Engine* const _engine; + SceneManager* const _sceneManager; + }; + + //// + /// @brief Load the glTF file from the specified path and adds all entities to the scene. + /// @param uri the path to the asset. Should be either asset:// (representing a Flutter asset), or file:// (representing a filesystem file). + /// @param relativeResourcePath the (relative) path to the asset's resources. + /// @return the glTF entity. + /// EntityId loadGltf(const char *uri, const char *relativeResourcePath, bool keepData = false); //// @@ -77,6 +103,7 @@ namespace thermion_filament void queuePositionUpdate(EntityId e, float x, float y, float z, bool relative); void queueRotationUpdate(EntityId e, float rads, float x, float y, float z, float w, bool relative); void queueRelativePositionUpdateWorldAxis(EntityId entity, float viewportCoordX, float viewportCoordY, float x, float y, float z); + void queueRelativePositionUpdateFromViewportVector(EntityId entityId, float viewportCoordX, float viewportCoordY); const utils::Entity *getCameraEntities(EntityId e); size_t getCameraEntityCount(EntityId e); const utils::Entity *getLightEntities(EntityId e) noexcept; @@ -158,7 +185,15 @@ namespace thermion_filament bool addAnimationComponent(EntityId entity); void removeAnimationComponent(EntityId entity); - void setStencilHighlight(EntityId entity); + /// @brief renders an outline around the specified entity. + /// + /// + void setStencilHighlight(EntityId entity, float r, float g, float b); + + /// @brief removes the outline around the specified entity. + /// + /// + void removeStencilHighlight(EntityId entity); /// @brief returns the number of instances of the FilamentAsset represented by the given entity. /// @param entityId @@ -188,16 +223,56 @@ namespace thermion_filament /// void setLayerEnabled(int layer, bool enabled); + /// + /// Creates an entity with the specified geometry/material and adds to the scene. + /// If [keepData] is true, stores + /// + EntityId createGeometry( + float *vertices, + uint32_t numVertices, + uint16_t *indices, + uint32_t numIndices, + filament::RenderableManager::PrimitiveType primitiveType = RenderableManager::PrimitiveType::TRIANGLES, + const char *materialPath = nullptr, + bool keepData = false + ); + friend class FilamentViewer; Gizmo* gizmo = nullptr; + gltfio::MaterialProvider * const unlitMaterialProvider() { + return _unlitMaterialProvider; + } + + bool isGeometryEntity(EntityId entity) { + return _geometry.find(entity) != _geometry.end(); + } + + CustomGeometry* const getGeometry(EntityId entityId) { + return _geometry[entityId]; + } + + Scene* const getScene() { + return _scene; + } + + bool isGltfAsset(EntityId entity) { + return getAssetByEntityId(entity) != nullptr; + } + + gltfio::FilamentInstance *getInstanceByEntityId(EntityId entityId); + gltfio::FilamentAsset *getAssetByEntityId(EntityId entityId); + + gltfio::FilamentInstance *createGltfAssetInstance(FilamentAsset* asset) { + return _assetLoader->createInstance(asset); + } + private: gltfio::AssetLoader *_assetLoader = nullptr; const ResourceLoaderWrapperImpl *const _resourceLoaderWrapper; Engine *_engine = nullptr; Scene *_scene = nullptr; - Scene *_highlightScene = nullptr; View* _view = nullptr; gltfio::MaterialProvider *_ubershaderProvider = nullptr; @@ -214,19 +289,20 @@ namespace thermion_filament gltfio::FilamentInstance *> _instances; tsl::robin_map _assets; + tsl::robin_map _geometry; + tsl::robin_map _highlighted; + tsl::robin_map> _transformUpdates; AnimationComponentManager *_animationComponentManager = nullptr; CollisionComponentManager *_collisionComponentManager = nullptr; - gltfio::FilamentInstance *getInstanceByEntityId(EntityId entityId); - gltfio::FilamentAsset *getAssetByEntityId(EntityId entityId); - utils::Entity findEntityByName( const gltfio::FilamentInstance *instance, const char *entityName); - GridOverlay* _gridOverlay = nullptr; + GridOverlay* _gridOverlay = nullptr; + }; } diff --git a/thermion_dart/native/src/CustomGeometry.cpp b/thermion_dart/native/src/CustomGeometry.cpp new file mode 100644 index 00000000..a8799de5 --- /dev/null +++ b/thermion_dart/native/src/CustomGeometry.cpp @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include + +#include "CustomGeometry.hpp" + +namespace thermion_filament { + +using namespace filament; + +CustomGeometry::CustomGeometry( + float* vertices, + uint32_t numVertices, + uint16_t* indices, + uint32_t numIndices, + RenderableManager::PrimitiveType primitiveType, + Engine* engine) + : numVertices(numVertices), numIndices(numIndices), _engine(engine) { + this->primitiveType = primitiveType; + this->vertices = new float[numVertices]; + std::memcpy(this->vertices, vertices, numVertices * sizeof(float)); + + this->indices = new uint16_t[numIndices]; + std::memcpy(this->indices, indices, numIndices * sizeof(uint16_t)); + + computeBoundingBox(); +} + +IndexBuffer* CustomGeometry::indexBuffer() { + IndexBuffer::BufferDescriptor::Callback indexCallback = [](void *buf, size_t, + void *data) + { + // free((void *)buf); + }; + + auto indexBuffer = IndexBuffer::Builder() + .indexCount(numIndices) + .bufferType(IndexBuffer::IndexType::USHORT) + .build(*_engine); + + indexBuffer->setBuffer(*_engine, IndexBuffer::BufferDescriptor( + this->indices, indexBuffer->getIndexCount() * sizeof(uint16_t), indexCallback)); + return indexBuffer; +} + +VertexBuffer* CustomGeometry::vertexBuffer() { + VertexBuffer::BufferDescriptor::Callback vertexCallback = [](void *buf, size_t, + void *data) + { + // free((void *)buf); + }; + + auto vertexBuffer = VertexBuffer::Builder() + .vertexCount(numVertices) + .bufferCount(1) + .attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3) + .build(*_engine); + + vertexBuffer->setBufferAt(*_engine, 0, VertexBuffer::BufferDescriptor( + this->vertices, vertexBuffer->getVertexCount() * sizeof(math::float3), vertexCallback)); + return vertexBuffer; +} + +CustomGeometry::~CustomGeometry() { + delete[] vertices; + delete[] indices; +} + +void CustomGeometry::computeBoundingBox() { + float minX = FLT_MAX, minY = FLT_MAX, minZ = FLT_MAX; + float maxX = -FLT_MAX, maxY = -FLT_MAX, maxZ = -FLT_MAX; + + for (uint32_t i = 0; i < numVertices; i += 3) { + minX = std::min(vertices[i], minX); + minY = std::min(vertices[i + 1], minY); + minZ = std::min(vertices[i + 2], minZ); + maxX = std::max(vertices[i], maxX); + maxY = std::max(vertices[i + 1], maxY); + maxZ = std::max(vertices[i + 2], maxZ); + } + + boundingBox = Box{{minX, minY, minZ}, {maxX, maxY, maxZ}}; +} + +Box CustomGeometry::getBoundingBox() const { + return boundingBox; +} + +} \ No newline at end of file diff --git a/thermion_dart/native/src/HighlightOverlay.cpp b/thermion_dart/native/src/HighlightOverlay.cpp new file mode 100644 index 00000000..2d6ffe6b --- /dev/null +++ b/thermion_dart/native/src/HighlightOverlay.cpp @@ -0,0 +1,183 @@ +#include +#include + +#include "SceneManager.hpp" + +namespace thermion_filament { + + SceneManager::HighlightOverlay::HighlightOverlay( + EntityId entityId, + SceneManager* const sceneManager, + Engine* engine, + float r, + float g, + float b) : _sceneManager(sceneManager), _engine(engine) { + + auto& rm = engine->getRenderableManager(); + + auto& tm = engine->getTransformManager(); + + // Create the outline/highlight material instance + filament::gltfio::MaterialKey dummyKey; // We're not using the key for this simple material + filament::gltfio::UvMap dummyUvMap; // We're not using UV mapping for this simple material + + auto materialProvider = sceneManager->unlitMaterialProvider(); + + _highlightMaterialInstance = materialProvider->createMaterialInstance(&dummyKey, &dummyUvMap); + + _highlightMaterialInstance->setStencilOpStencilFail(filament::backend::StencilOperation::KEEP); + _highlightMaterialInstance->setStencilOpDepthFail(filament::backend::StencilOperation::KEEP); + _highlightMaterialInstance->setStencilOpDepthStencilPass(filament::backend::StencilOperation::KEEP); + _highlightMaterialInstance->setStencilCompareFunction(filament::backend::SamplerCompareFunc::NE); + _highlightMaterialInstance->setStencilReferenceValue(1); + _highlightMaterialInstance->setParameter("color", filament::math::float3 { r, g, b }); + _highlightMaterialInstance->setParameter("scale", 1.05f); + + auto scene = sceneManager->getScene(); + + if(sceneManager->isGeometryEntity(entityId)) { + + auto geometryEntity = Entity::import(entityId); + auto renderable = rm.getInstance(geometryEntity); + + auto materialInstance = rm.getMaterialInstanceAt(renderable, 0); + + // set stencil write on the existing material + materialInstance->setStencilWrite(true); + materialInstance->setDepthWrite(true); + materialInstance->setStencilReferenceValue(1); + materialInstance->setStencilOpStencilFail(filament::backend::StencilOperation::KEEP); + materialInstance->setStencilOpDepthFail(filament::backend::StencilOperation::REPLACE); + materialInstance->setStencilOpDepthStencilPass(filament::backend::StencilOperation::REPLACE); + materialInstance->setStencilCompareFunction(filament::backend::SamplerCompareFunc::A); + + auto geometry = sceneManager->getGeometry(entityId); + + _entity = utils::EntityManager::get().create(); + RenderableManager::Builder builder(1); + builder.boundingBox(geometry->getBoundingBox()) + .geometry(0, geometry->primitiveType, geometry->vertexBuffer(), geometry->indexBuffer(), 0, geometry->numIndices) + .culling(true) + .material(0, _highlightMaterialInstance) + .receiveShadows(false) + .castShadows(false); + + builder.build(*engine, _entity); + + scene->addEntity(_entity); + auto outlineTransformInstance = tm.getInstance(_entity); + auto entityTransformInstance = tm.getInstance(geometryEntity); + tm.setParent(outlineTransformInstance, entityTransformInstance); + return; + } + + Log("Not geometry"); + + if(sceneManager->isGltfAsset(entityId)) { + + auto *asset = sceneManager->getAssetByEntityId(entityId); + + if (asset) + { + + Log("Found glTF FilamentAsset with %d material instances", asset->getInstance()->getMaterialInstanceCount()); + + + auto materialInstance = asset->getInstance()->getMaterialInstances()[0]; + + // set stencil write on the existing material + materialInstance->setStencilWrite(true); + materialInstance->setDepthWrite(true); + materialInstance->setStencilReferenceValue(1); + materialInstance->setStencilOpStencilFail(filament::backend::StencilOperation::KEEP); + materialInstance->setStencilOpDepthFail(filament::backend::StencilOperation::REPLACE); + materialInstance->setStencilOpDepthStencilPass(filament::backend::StencilOperation::REPLACE); + materialInstance->setStencilCompareFunction(filament::backend::SamplerCompareFunc::A); + + auto newInstance = sceneManager->createGltfAssetInstance(asset); + + _entity = newInstance->getRoot(); + + if(!newInstance) { + Log("Couldn't create new instance"); + } else { + auto& tm = engine->getTransformManager(); + for(int i = 0; i < newInstance->getEntityCount(); i++) { + auto entity = newInstance->getEntities()[i]; + auto renderableInstance = rm.getInstance(entity); + rm.setPriority(renderableInstance, 7); + if(renderableInstance.isValid()) { + for(int primitiveIndex = 0; primitiveIndex < rm.getPrimitiveCount(renderableInstance); primitiveIndex++) { + rm.setMaterialInstanceAt(renderableInstance, primitiveIndex, _highlightMaterialInstance); + } + } else { + Log("Not renderable, ignoring"); + } + } + scene->addEntities(newInstance->getEntities(), newInstance->getEntityCount()); + } + return; + } else { + Log("Not FilamentAsset"); + } + } + + Log("Looking for parent"); + + auto renderable = rm.getInstance(Entity::import(entityId)); + auto transformInstance = tm.getInstance(Entity::import(entityId)); + if(!transformInstance.isValid()) { + Log("Unknown entity type"); + return; + } + + Entity parent; + while(true) { + auto newParent = tm.getParent(transformInstance); + if(newParent.isNull()) { + break; + } + parent = newParent; + transformInstance = tm.getInstance(parent); + } + if(parent.isNull()) { + Log("Unknown entity type"); + return; + } + + sceneManager->setStencilHighlight(Entity::smuggle(parent), r, g, b); +} + +SceneManager::HighlightOverlay::~HighlightOverlay() { + Log("DEsturctor called!"); + if (_entity.isNull()) { + return; + } + + auto& rm = _engine->getRenderableManager(); + auto& tm = _engine->getTransformManager(); + + _sceneManager->getScene()->remove(_entity); + + + // If this was a glTF asset instance, we need to destroy it + if (_newInstance) { + for(int i =0 ; i < _newInstance->getEntityCount(); i++) { + auto entity =_newInstance->getEntities()[i]; + _sceneManager->getScene()->remove(entity); + rm.destroy(entity); + } + } + + tm.destroy(_entity); + + _engine->destroy(_highlightMaterialInstance); + + // Destroy the entity + utils::EntityManager::get().destroy(_entity); + + + } + + +} \ No newline at end of file diff --git a/thermion_dart/native/src/SceneManager.cpp b/thermion_dart/native/src/SceneManager.cpp index 16b05fa5..ec9a7e1b 100644 --- a/thermion_dart/native/src/SceneManager.cpp +++ b/thermion_dart/native/src/SceneManager.cpp @@ -20,7 +20,7 @@ #include #include #include - +#include #include #include "material/FileMaterialProvider.hpp" @@ -30,8 +30,7 @@ #include "StreamBufferAdapter.hpp" #include "Log.hpp" #include "SceneManager.hpp" - -#include "gltfio/materials/uberarchive.h" +#include "CustomGeometry.hpp" extern "C" { @@ -52,13 +51,11 @@ namespace thermion_filament const ResourceLoaderWrapperImpl *const resourceLoaderWrapper, Engine *engine, Scene *scene, - Scene *highlightScene, const char *uberArchivePath) : _view(view), _resourceLoaderWrapper(resourceLoaderWrapper), _engine(engine), - _scene(scene), - _highlightScene(highlightScene) + _scene(scene) { _stbDecoder = createStbProvider(_engine); @@ -112,11 +109,9 @@ namespace thermion_filament SceneManager::~SceneManager() { - - destroyAll(); - - gizmo->destroy(); + delete gizmo; _gridOverlay->destroy(); + destroyAll(); _gltfResourceLoader->asyncCancelLoad(); _ubershaderProvider->destroyMaterials(); @@ -1830,6 +1825,55 @@ namespace thermion_filament tm.setTransform(transformInstance, newTransform); } +void SceneManager::queueRelativePositionUpdateFromViewportVector(EntityId entityId, float viewportCoordX, float viewportCoordY) +{ + // Get the camera and viewport + const auto &camera = _view->getCamera(); + const auto &vp = _view->getViewport(); + + // Convert viewport coordinates to NDC space + float ndcX = (2.0f * viewportCoordX) / vp.width - 1.0f; + float ndcY = 1.0f - (2.0f * viewportCoordY) / vp.height; + + Log("ndc X ndcY %f %f", ndcX, ndcY ); + // Get the current position of the entity + auto &tm = _engine->getTransformManager(); + auto entity = Entity::import(entityId); + auto transformInstance = tm.getInstance(entity); + auto currentTransform = tm.getTransform(transformInstance); + + // get entity model origin in camera space + auto entityPositionInCameraSpace = camera.getViewMatrix() * currentTransform * filament::math::float4 { 0.0f, 0.0f, 0.0f, 1.0f }; + // get entity model origin in clip space + auto entityPositionInClipSpace = camera.getProjectionMatrix() * entityPositionInCameraSpace; + auto entityPositionInNdcSpace = entityPositionInClipSpace / entityPositionInClipSpace.w; + + Log("entityPositionInCameraSpace %f %f %f %f", entityPositionInCameraSpace.x, entityPositionInCameraSpace.y, entityPositionInCameraSpace.z, entityPositionInCameraSpace.w); + Log("entityPositionInClipSpace %f %f %f %f", entityPositionInClipSpace.x, entityPositionInClipSpace.y, entityPositionInClipSpace.z, entityPositionInClipSpace.w); + Log("entityPositionInNdcSpace %f %f %f %f", entityPositionInNdcSpace.x, entityPositionInNdcSpace.y, entityPositionInNdcSpace.z, entityPositionInNdcSpace.w); + + // Viewport coords in NDC space (use entity position in camera space Z to project onto near plane) + math::float4 ndcNearPlanePos = {ndcX, ndcY, -1.0f, 1.0f}; + math::float4 ndcFarPlanePos = {ndcX, ndcY, 0.99f, 1.0f}; + math::float4 ndcEntityPlanePos = {ndcX, ndcY, entityPositionInNdcSpace.z, 1.0f}; + + // Get viewport coords in clip space + math::float4 nearPlaneInClipSpace = Camera::inverseProjection(camera.getProjectionMatrix()) * ndcNearPlanePos; + auto nearPlaneInCameraSpace = nearPlaneInClipSpace / nearPlaneInClipSpace.w; + math::float4 farPlaneInClipSpace = Camera::inverseProjection(camera.getProjectionMatrix()) * ndcFarPlanePos; + auto farPlaneInCameraSpace = farPlaneInClipSpace / farPlaneInClipSpace.w; + math::float4 entityPlaneInClipSpace = Camera::inverseProjection(camera.getProjectionMatrix()) * ndcEntityPlanePos; + auto entityPlaneInCameraSpace = entityPlaneInClipSpace / entityPlaneInClipSpace.w; + auto entityPlaneInWorldSpace = camera.getModelMatrix() * entityPlaneInCameraSpace; + + Log("nearPlaneInClipSpace %f %f %f %f",nearPlaneInClipSpace.x, nearPlaneInClipSpace.y, nearPlaneInClipSpace.z, nearPlaneInClipSpace.w); + Log("nearPlaneInCameraSpace %f %f %f %f",nearPlaneInCameraSpace.x, nearPlaneInCameraSpace.y, nearPlaneInCameraSpace.z, nearPlaneInCameraSpace.w); + Log("entityPlaneInCameraSpace %f %f %f %f",entityPlaneInCameraSpace.x, entityPlaneInCameraSpace.y, entityPlaneInCameraSpace.z, entityPlaneInCameraSpace.w); + Log("entityPlaneInWorldSpace %f %f %f %f",entityPlaneInWorldSpace.x, entityPlaneInWorldSpace.y, entityPlaneInWorldSpace.z, entityPlaneInWorldSpace.w); + + // Queue the position update (as a relative movement) + queuePositionUpdate(entityId, entityPlaneInWorldSpace.x, entityPlaneInWorldSpace.y, entityPlaneInWorldSpace.z, false); +} void SceneManager::queueRelativePositionUpdateWorldAxis(EntityId entity, float viewportCoordX, float viewportCoordY, float x, float y, float z) { auto worldAxis = math::float3{x, y, z}; @@ -2212,7 +2256,6 @@ void SceneManager::queueRelativePositionUpdateWorldAxis(EntityId entity, float v return; } rm.setPriority(renderableInstance, priority); - Log("Set instance renderable priority to %d", priority); } Aabb2 SceneManager::getBoundingBox(EntityId entityId) @@ -2292,8 +2335,71 @@ void SceneManager::queueRelativePositionUpdateWorldAxis(EntityId entity, float v _view->setLayerEnabled(layer, enabled); } - + void SceneManager::removeStencilHighlight(EntityId entityId) { + auto found = _highlighted.find(entityId); + if(found == _highlighted.end()) { + return; + } + delete found->second; + _highlighted.erase(entityId); + } + + void SceneManager::setStencilHighlight(EntityId entityId, float r, float g, float b) { + + auto highlightEntity = new HighlightOverlay(entityId, this, _engine, r, g, b); + + if(!highlightEntity->isValid()) { + delete highlightEntity; + } else { + _highlighted.emplace(entityId, highlightEntity); + } } + EntityId SceneManager::createGeometry( + float *vertices, + uint32_t numVertices, + uint16_t *indices, + uint32_t numIndices, + RenderableManager::PrimitiveType primitiveType, + const char *materialPath, + bool keepData + ) { + + auto geometry = new CustomGeometry(vertices, numVertices, indices, numIndices, primitiveType, _engine); + + filament::Material* mat = nullptr; + if (materialPath) { + auto matData = _resourceLoaderWrapper->load(materialPath); + mat = Material::Builder().package(matData.data, matData.size).build(*_engine); + _resourceLoaderWrapper->free(matData); + } + + auto renderable = utils::EntityManager::get().create(); + RenderableManager::Builder builder(1); + + builder.boundingBox(geometry->getBoundingBox()) + .geometry(0, primitiveType, geometry->vertexBuffer(), geometry->indexBuffer(), 0, numIndices) + .culling(true) + .receiveShadows(false) + .castShadows(false); + + if (mat) { + builder.material(0, mat->getDefaultInstance()); + } + + builder.build(*_engine, renderable); + + _scene->addEntity(renderable); + + auto entityId = Entity::smuggle(renderable); + + _geometry.emplace(entityId, geometry); + + return entityId; + } + } // namespace thermion_filament + + + \ No newline at end of file