feat: move HighlightOverlay to nested class, move createGeometry to SceneManager, add queueRelativePositionUpdateFromViewportVector

This commit is contained in:
Nick Fisher
2024-09-07 17:57:38 +08:00
parent 8044adcc50
commit 53ff6c72f0
5 changed files with 523 additions and 20 deletions

View File

@@ -0,0 +1,46 @@
#pragma once
#include <stddef.h>
#include <filament/Engine.h>
#include <filament/VertexBuffer.h>
#include <filament/IndexBuffer.h>
#include <filament/TransformManager.h>
#include <filament/Texture.h>
#include <filament/RenderableManager.h>
#include <filament/Viewport.h>
#include <filament/Frustum.h>
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;
};
}

View File

@@ -16,10 +16,13 @@
#include <filament/IndexBuffer.h>
#include <filament/InstanceBuffer.h>
#include <utils/NameComponentManager.h>
#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<EntityId, gltfio::FilamentAsset *> _assets;
tsl::robin_map<EntityId, CustomGeometry*> _geometry;
tsl::robin_map<EntityId, HighlightOverlay *> _highlighted;
tsl::robin_map<EntityId, std::tuple<math::float3, bool, math::quatf, bool, float>> _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;
};
}

View File

@@ -0,0 +1,92 @@
#include <filament/Engine.h>
#include <filament/TransformManager.h>
#include <filament/Texture.h>
#include <filament/RenderableManager.h>
#include <filament/Viewport.h>
#include <filament/Frustum.h>
#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;
}
}

View File

@@ -0,0 +1,183 @@
#include <filament/Material.h>
#include <filament/MaterialInstance.h>
#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);
}
}

View File

@@ -20,7 +20,7 @@
#include <gltfio/ResourceLoader.h>
#include <gltfio/TextureProvider.h>
#include <gltfio/math.h>
#include <gltfio/materials/uberarchive.h>
#include <imageio/ImageDecoder.h>
#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