feature!:

This is a breaking change needed to fully implement instancing and stencil highlighting.

Previously, users would work directly with entities (on the Dart side, ThermionEntity), e.g.

final entity = await viewer.loadGlb("some.glb");

However, Filament "entities" are a lower-level abstraction.

Loading a glTF file, for example, inserts multiple entities into the scene.

For example, each mesh, light, and camera within a glTF asset will be assigned an entity. A top-level (non-renderable) entity will also be created for the glTF asset, which can be used to transform the entire hierarchy.

"Asset" is a better representation for loading/inserting objects into the scene; think of this as a bundle of entities.

Unless you need to work directly with transforms, instancing, materials and renderables, you can work directly with ThermionAsset.
This commit is contained in:
Nick Fisher
2024-11-21 15:04:10 +08:00
parent 9ada6aae64
commit ed444b0615
195 changed files with 18061 additions and 12628 deletions

View File

@@ -0,0 +1,172 @@
#pragma once
#include <mutex>
#include <vector>
#include <filament/Engine.h>
#include <filament/Scene.h>
#include "c_api/APIBoundaryTypes.h"
#include "components/CollisionComponentManager.hpp"
#include "components/AnimationComponentManager.hpp"
#include "GltfSceneAssetInstance.hpp"
#include "GltfSceneAsset.hpp"
#include "SceneAsset.hpp"
namespace thermion
{
using namespace filament;
using namespace filament::gltfio;
using namespace utils;
using std::string;
using std::unique_ptr;
using std::vector;
/// @brief
class AnimationManager
{
public:
AnimationManager(
Engine *engine,
Scene *scene);
~AnimationManager();
void update();
/// @brief
/// @param asset
/// @param childEntity
/// @return
vector<string> getMorphTargetNames(GltfSceneAsset *asset, EntityId childEntity);
/// @brief
/// @param instance
/// @param skinIndex
/// @return
vector<Entity> getBoneEntities(GltfSceneAssetInstance *instance, int skinIndex);
/// @brief
/// @param sceneAsset
/// @param morphData
/// @param morphIndices
/// @param numMorphTargets
/// @param numFrames
/// @param frameLengthInMs
/// @return
bool setMorphAnimationBuffer(
utils::Entity entity,
const float *const morphData,
const uint32_t *const morphIndices,
int numMorphTargets,
int numFrames,
float frameLengthInMs);
/// @brief
/// @param entityId
void clearMorphAnimationBuffer(
utils::Entity entity);
/// @brief
/// @param instance
/// @param skinIndex
/// @param boneIndex
/// @return
math::mat4f getInverseBindMatrix(GltfSceneAssetInstance *instance, int skinIndex, int boneIndex);
/// @brief Set the local transform for the bone at boneIndex/skinIndex in the given entity.
/// @param entityId the parent entity
/// @param entityName the name of the mesh under entityId for which the bone will be set.
/// @param skinIndex the index of the joint skin. Currently only 0 is supported.
/// @param boneName the name of the bone
/// @param transform the 4x4 matrix representing the local transform for the bone
/// @return true if the transform was successfully set, false otherwise
bool setBoneTransform(GltfSceneAssetInstance *instance, int skinIndex, int boneIndex, math::mat4f transform);
/// @brief Immediately start animating the bone at [boneIndex] under the parent instance [entity] at skin [skinIndex].
/// @param entity the mesh entity to animate
/// @param frameData frame data as quaternions
/// @param numFrames the number of frames
/// @param boneName the name of the bone to animate
/// @param frameLengthInMs the length of each frame in ms
/// @return true if the bone animation was successfully enqueued
bool addBoneAnimation(
GltfSceneAssetInstance *instance,
int skinIndex,
int boneIndex,
const float *const frameData,
int numFrames,
float frameLengthInMs,
float fadeOutInSecs,
float fadeInInSecs,
float maxDelta);
/// @brief
/// @param instance
/// @param skinIndex
/// @return
std::vector<math::mat4f> getBoneRestTranforms(GltfSceneAssetInstance *instance, int skinIndex);
/// @brief
/// @param instance
void resetToRestPose(GltfSceneAssetInstance *instance);
/// @brief
/// @param instance
void updateBoneMatrices(GltfSceneAssetInstance *instance);
/// @brief
/// @param instance
/// @param animationIndex
/// @param loop
/// @param reverse
/// @param replaceActive
/// @param crossfade
/// @param startOffset
void playGltfAnimation(GltfSceneAssetInstance *instance, int animationIndex, bool loop, bool reverse, bool replaceActive, float crossfade = 0.3f, float startOffset = 0.0f);
/// @brief
/// @param instance
/// @param animationIndex
void stopGltfAnimation(GltfSceneAssetInstance *instance, int animationIndex);
/// @brief
/// @param instance
/// @param weights
/// @param count
void setMorphTargetWeights(utils::Entity entity, const float *const weights, int count);
/// @brief
/// @param instance
/// @param animationIndex
/// @param animationFrame
void setGltfAnimationFrame(GltfSceneAssetInstance *instance, int animationIndex, int animationFrame);
/// @brief
/// @param instance
/// @return
vector<string> getGltfAnimationNames(GltfSceneAssetInstance *instance);
/// @brief
/// @param instance
/// @param animationIndex
/// @return
float getGltfAnimationDuration(GltfSceneAssetInstance *instance, int animationIndex);
/// @brief
/// @param entity
/// @return
bool addAnimationComponent(EntityId entity);
/// @brief
/// @param entity
void removeAnimationComponent(EntityId entity);
private:
Engine *_engine = nullptr;
Scene *_scene = nullptr;
std::mutex _mutex;
std::unique_ptr<AnimationComponentManager> _animationComponentManager = std::nullptr_t();
};
}

View File

@@ -0,0 +1,63 @@
#pragma once
#include <stddef.h>
#include <filament/Engine.h>
#include <filament/Frustum.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 <utils/Entity.h>
#include <utils/EntityManager.h>
namespace thermion
{
using namespace filament;
class CustomGeometry {
public:
CustomGeometry(
float* vertices,
uint32_t numVertices,
float* normals,
uint32_t numNormals,
float *uvs,
uint32_t numUvs,
uint16_t* indices,
uint32_t numIndices,
MaterialInstance* materialInstance,
RenderableManager::PrimitiveType primitiveType,
Engine* engine);
~CustomGeometry();
utils::Entity createInstance(MaterialInstance *materialInstance);
private:
Engine* _engine;
MaterialInstance* _materialInstance = nullptr;
VertexBuffer* vertexBuffer;
IndexBuffer* indexBuffer;
float* vertices = nullptr;
float* normals = nullptr;
float *uvs = nullptr;
uint32_t numVertices = 0;
uint16_t* indices = 0;
uint32_t numIndices = 0;
Box boundingBox;
RenderableManager::PrimitiveType primitiveType;
void computeBoundingBox();
};
}

View File

@@ -0,0 +1,143 @@
#pragma once
#include <memory>
#include <filament/Engine.h>
#include <filament/RenderableManager.h>
#include <filament/VertexBuffer.h>
#include <filament/IndexBuffer.h>
#include <gltfio/MaterialProvider.h>
#include "scene/SceneAsset.hpp"
namespace thermion
{
using namespace filament;
class GeometrySceneAsset : public SceneAsset
{
public:
GeometrySceneAsset(bool isInstance,
Engine *engine,
VertexBuffer *vertexBuffer,
IndexBuffer *indexBuffer,
MaterialInstance **materialInstances,
size_t materialInstanceCount,
RenderableManager::PrimitiveType primitiveType,
Box boundingBox);
~GeometrySceneAsset();
SceneAsset *createInstance(MaterialInstance **materialInstances = nullptr, size_t materialInstanceCount = 0) override;
SceneAssetType getType() override
{
return SceneAsset::SceneAssetType::Geometry;
}
bool isInstance() override
{
return _isInstance;
}
utils::Entity getEntity() override
{
return _entity;
}
MaterialInstance **getMaterialInstances() override
{
return _materialInstances;
}
size_t getMaterialInstanceCount() override
{
return _materialInstanceCount;
}
const Box &getBoundingBox() const { return _boundingBox; }
VertexBuffer *getVertexBuffer() const { return _vertexBuffer; }
IndexBuffer *getIndexBuffer() const { return _indexBuffer; }
void addAllEntities(Scene *scene) override
{
scene->addEntity(_entity);
}
void removeAllEntities(Scene *scene) override
{
scene->remove(_entity);
}
void setPriority(RenderableManager &rm, int priority) override
{
auto renderableInstance = rm.getInstance(_entity);
rm.setPriority(renderableInstance, priority);
}
void setLayer(RenderableManager &rm, int layer) override
{
auto renderableInstance = rm.getInstance(_entity);
rm.setLayerMask(renderableInstance, 0xFF, 1u << (uint8_t)layer);
}
SceneAsset *getInstanceByEntity(utils::Entity entity) override
{
for (auto &instance : _instances)
{
if (instance->getEntity() == entity)
{
return instance.get();
}
}
return std::nullptr_t();
}
SceneAsset *getInstanceAt(size_t index) override
{
auto &asset = _instances[index];
return asset.get();
}
size_t getInstanceCount() override
{
return _instances.size();
}
size_t getChildEntityCount() override
{
return 0;
}
const Entity *getChildEntities() override
{
return nullptr;
}
Entity findEntityByName(const char *name) override
{
return Entity(); // not currently implemented
}
static std::unique_ptr<GeometrySceneAsset> create(
float *vertices, uint32_t numVertices,
float *normals, uint32_t numNormals,
float *uvs, uint32_t numUvs,
uint16_t *indices, uint32_t numIndices,
MaterialInstance *materialInstance,
RenderableManager::PrimitiveType primitiveType,
Engine *engine);
private:
Engine *_engine = nullptr;
VertexBuffer *_vertexBuffer = nullptr;
IndexBuffer *_indexBuffer = nullptr;
MaterialInstance **_materialInstances = nullptr;
size_t _materialInstanceCount = 0;
Box _boundingBox;
bool _isInstance = false;
utils::Entity _entity;
RenderableManager::PrimitiveType _primitiveType;
std::vector<std::unique_ptr<GeometrySceneAsset>> _instances;
};
} // namespace thermion

View File

@@ -0,0 +1,308 @@
#pragma once
#include <memory>
#include <vector>
#include <filament/Engine.h>
#include <filament/RenderableManager.h>
#include <filament/VertexBuffer.h>
#include <filament/IndexBuffer.h>
#include <filament/geometry/SurfaceOrientation.h>
#include <filament/Box.h>
#include <gltfio/MaterialProvider.h>
#include "GeometrySceneAsset.hpp"
#include "Log.hpp"
namespace thermion
{
class GeometrySceneAssetBuilder
{
public:
GeometrySceneAssetBuilder(filament::Engine *engine) : mEngine(engine) {}
GeometrySceneAssetBuilder &vertices(const float *vertices, uint32_t count)
{
mVertices->resize(count);
std::copy(vertices, vertices + count, mVertices->data());
mNumVertices = count;
return *this;
}
GeometrySceneAssetBuilder &normals(const float *normals, uint32_t count)
{
if (normals)
{
mNormals->resize(count);
std::copy(normals, normals + count, mNormals->data());
}
else
{
mNormals->clear();
}
mNumNormals = count;
return *this;
}
GeometrySceneAssetBuilder &uvs(const float *uvs, uint32_t count)
{
if (uvs)
{
mUVs->resize(count);
std::copy(uvs, uvs + count, mUVs->data());
}
else
{
mUVs->clear();
}
mNumUVs = count;
return *this;
}
GeometrySceneAssetBuilder &indices(const uint16_t *indices, uint32_t count)
{
mIndices->resize(count);
std::copy(indices, indices + count, mIndices->data());
mNumIndices = count;
return *this;
}
GeometrySceneAssetBuilder &materials(filament::MaterialInstance **materials, size_t materialInstanceCount)
{
mMaterialInstances = materials;
mMaterialInstanceCount = materialInstanceCount;
return *this;
}
GeometrySceneAssetBuilder &primitiveType(filament::RenderableManager::PrimitiveType type)
{
mPrimitiveType = type;
return *this;
}
std::unique_ptr<GeometrySceneAsset> build()
{
Log("Starting build. Validating inputs...");
if (!validate())
{
Log("Validation failed!");
return nullptr;
}
Log("Creating buffers...");
auto [vertexBuffer, indexBuffer] = createBuffers();
if (!vertexBuffer || !indexBuffer)
{
Log("Failed to create buffers: VB=%p, IB=%p", vertexBuffer, indexBuffer);
return nullptr;
}
Log("Buffers created successfully: VB=%p, IB=%p", vertexBuffer, indexBuffer);
Log("Creating entity...");
auto entity = utils::EntityManager::get().create();
Log("Entity created: %d", entity.getId());
Box boundingBox = computeBoundingBox();
Log("Computed bounding box: min={%f,%f,%f}, max={%f,%f,%f}",
boundingBox.getMin().x, boundingBox.getMin().y, boundingBox.getMin().z,
boundingBox.getMax().x, boundingBox.getMax().y, boundingBox.getMax().z);
auto asset = std::make_unique<GeometrySceneAsset>(
false,
mEngine,
vertexBuffer,
indexBuffer,
mMaterialInstances,
mMaterialInstanceCount,
mPrimitiveType,
boundingBox);
Log("Asset created: %p", asset.get());
return asset;
}
private:
Box computeBoundingBox()
{
float minX = FLT_MAX, minY = FLT_MAX, minZ = FLT_MAX;
float maxX = -FLT_MAX, maxY = -FLT_MAX, maxZ = -FLT_MAX;
Box box;
for (uint32_t i = 0; i < mNumVertices; i += 3)
{
minX = std::min(mVertices->at(i), minX);
minY = std::min(mVertices->at(i + 1), minY);
minZ = std::min(mVertices->at(i + 2), minZ);
maxX = std::max(mVertices->at(i), maxX);
maxY = std::max(mVertices->at(i + 1), maxY);
maxZ = std::max(mVertices->at(i + 2), maxZ);
}
const filament::math::float3 min {minX, minY, minZ};
const filament::math::float3 max {maxX, maxY, maxZ};
box.set(min, max);
return box;
}
std::pair<filament::VertexBuffer *, filament::IndexBuffer *> createBuffers()
{
auto indexBuffer = IndexBuffer::Builder()
.indexCount(mNumIndices)
.bufferType(IndexBuffer::IndexType::USHORT)
.build(*mEngine);
indexBuffer->setBuffer(*mEngine,
IndexBuffer::BufferDescriptor(
mIndices->data(),
mNumIndices * sizeof(uint16_t),
[](void *, size_t, void *data)
{
delete static_cast<std::vector<float> *>(data);
},
mIndices));
if (mUVs->empty())
{
mUVs->resize(mNumVertices);
std::fill(mUVs->begin(), mUVs->end(), 0.0f);
}
auto dummyColors = new std::vector<filament::math::float4>(
mNumVertices, filament::math::float4{1.0f, 1.0f, 1.0f, 1.0f});
auto vertexBufferBuilder =
VertexBuffer::Builder()
.vertexCount(mNumVertices)
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3)
.attribute(VertexAttribute::UV0, 1, VertexBuffer::AttributeType::FLOAT2)
.attribute(VertexAttribute::UV1, 2, VertexBuffer::AttributeType::FLOAT2)
.attribute(VertexAttribute::COLOR, 3, VertexBuffer::AttributeType::FLOAT4);
if (!mNormals->empty())
{
vertexBufferBuilder.bufferCount(5)
.attribute(VertexAttribute::TANGENTS, 4, VertexBuffer::AttributeType::FLOAT4);
}
else
{
vertexBufferBuilder = vertexBufferBuilder.bufferCount(4);
}
auto vertexBuffer = vertexBufferBuilder.build(*mEngine);
vertexBuffer->setBufferAt(*mEngine, 0,
VertexBuffer::BufferDescriptor(
mVertices->data(), mNumVertices * sizeof(float),
[](void *, size_t, void *) {}));
vertexBuffer->setBufferAt(*mEngine, 1,
VertexBuffer::BufferDescriptor(
mUVs->data(), mUVs->size() * sizeof(float),
[](void *, size_t, void *data)
{
},
mUVs));
vertexBuffer->setBufferAt(*mEngine, 2,
VertexBuffer::BufferDescriptor(
mUVs->data(), mUVs->size() * sizeof(float),
[](void *, size_t, void *data) {
delete static_cast<std::vector<float> *>(data);
},
mUVs));
vertexBuffer->setBufferAt(*mEngine, 3,
VertexBuffer::BufferDescriptor(
dummyColors->data(), dummyColors->size() * sizeof(math::float4),
[](void *, size_t, void *data)
{
delete static_cast<std::vector<math::float4> *>(data);
},
dummyColors));
if (!mNormals->empty())
{
assert(mPrimitiveType == RenderableManager::PrimitiveType::TRIANGLES);
std::vector<filament::math::ushort3> triangles;
for (uint32_t i = 0; i < mNumIndices; i += 3)
{
triangles.push_back({mIndices->at(i),
mIndices->at(i + 1),
mIndices->at(i + 2)});
}
auto &builder = geometry::SurfaceOrientation::Builder()
.vertexCount(mNumVertices)
.normals((filament::math::float3 *)mNormals->data())
.positions((filament::math::float3 *)mVertices->data())
.triangleCount(triangles.size())
.triangles(triangles.data());
auto orientation = builder.build();
auto quats = new std::vector<filament::math::quatf>(mNumVertices);
orientation->getQuats(quats->data(), mNumVertices);
vertexBuffer->setBufferAt(*mEngine, 4,
VertexBuffer::BufferDescriptor(
quats->data(), quats->size() * sizeof(math::quatf),
[](void *, size_t, void *data)
{
delete static_cast<std::vector<math::quatf> *>(data);
},
quats));
}
return {vertexBuffer, indexBuffer};
}
bool validate() const
{
if (!mEngine)
{
Log("Validation failed: No engine");
return false;
}
if (mVertices->empty() || mNumVertices == 0)
{
Log("Validation failed: No vertices (empty=%d, count=%d)", mVertices->empty(), mNumVertices);
return false;
}
if (mNumNormals > 0 && !mNormals->empty() && mNumNormals != mNumVertices)
{
Log("Validation failed: Normal count mismatch (normals=%d, vertices=%d)", mNumNormals, mNumVertices);
return false;
}
if (mNumUVs > 0 && !mUVs->empty() && mNumUVs != mNumVertices)
{
Log("Validation failed: UV count mismatch (uvs=%d, vertices=%d)", mNumUVs, mNumVertices);
return false;
}
if (mIndices->empty() || mNumIndices == 0)
{
Log("Validation failed: No indices (empty=%d, count=%d)", mIndices->empty(), mNumIndices);
return false;
}
Log("Validation passed: vertices=%d, normals=%s, uvs=%d, indices=%d",
mNumVertices,
(!mNormals->empty() ? "yes" : "no"),
mNumUVs,
mNumIndices);
return true;
}
filament::Engine *mEngine = nullptr;
std::vector<float> *mVertices = new std::vector<float>();
std::vector<float> *mNormals = new std::vector<float>();
std::vector<float> *mUVs = new std::vector<float>();
std::vector<uint16_t> *mIndices = new std::vector<uint16_t>;
uint32_t mNumVertices = 0;
uint32_t mNumNormals = 0;
uint32_t mNumUVs = 0;
uint32_t mNumIndices = 0;
filament::MaterialInstance **mMaterialInstances = nullptr;
size_t mMaterialInstanceCount = 0;
filament::gltfio::MaterialProvider *mMaterialProvider = nullptr;
filament::RenderableManager::PrimitiveType mPrimitiveType =
filament::RenderableManager::PrimitiveType::TRIANGLES;
};
} // namespace thermion

View File

@@ -0,0 +1,201 @@
#pragma once
#include <utils/Entity.h>
#include <filament/Engine.h>
#include <filament/Material.h>
#include <filament/MaterialInstance.h>
#include <filament/Scene.h>
#include <filament/Camera.h>
#include <filament/View.h>
#include <filament/Viewport.h>
#include <filament/RenderableManager.h>
#include <gltfio/AssetLoader.h>
#include <gltfio/FilamentAsset.h>
#include <gltfio/FilamentInstance.h>
#include <gltfio/ResourceLoader.h>
#include <filament/IndexBuffer.h>
#include <filament/InstanceBuffer.h>
#include "c_api/ThermionDartApi.h"
#include "scene/SceneAsset.hpp"
namespace thermion
{
using namespace filament;
using namespace utils;
class Gizmo : public SceneAsset
{
public:
Gizmo(Engine *engine, View *view, Scene *scene, Material *material);
~Gizmo() override;
enum Axis
{
X,
Y,
Z
};
enum GizmoPickResultType {
AxisX,
AxisY,
AxisZ,
Parent,
None
};
typedef void (*GizmoPickCallback)(Gizmo::GizmoPickResultType result, float x, float y, float z);
void pick(uint32_t x, uint32_t y, GizmoPickCallback callback);
bool isGizmoEntity(Entity entity);
SceneAssetType getType() override { return SceneAssetType::Gizmo; }
utils::Entity getEntity() override { return _entities[0]; }
bool isInstance() override { return false; }
SceneAsset *createInstance(MaterialInstance **materialInstances, size_t materialInstanceCount) override { return nullptr; }
MaterialInstance **getMaterialInstances() override { return _materialInstances.data(); }
size_t getMaterialInstanceCount() override { return _materialInstances.size(); }
void addAllEntities(Scene *scene) override
{
for (const auto &entity : _entities)
{
if (entity.isNull())
{
continue;
}
scene->addEntity(entity);
}
}
void removeAllEntities(Scene *scene) override
{
for (const auto &entity : _entities)
{
scene->remove(entity);
}
}
size_t getInstanceCount() override { return 0; }
SceneAsset *getInstanceByEntity(utils::Entity entity) override { return nullptr; }
SceneAsset *getInstanceAt(size_t index) override { return nullptr; }
size_t getChildEntityCount() override { return _entities.size() - 1; }
const Entity *getChildEntities() override { return _entities.data() + 1; }
Entity findEntityByName(const char *name) override
{
return utils::Entity::import(0);
}
void setPriority(RenderableManager &rm, int mask) override
{
}
void setLayer(RenderableManager &rm, int layer) override
{
}
void highlight(Gizmo::Axis axis);
void unhighlight(Gizmo::Axis axis);
private:
class PickCallbackHandler
{
public:
PickCallbackHandler(Gizmo *gizmo, GizmoPickCallback callback)
: _gizmo(gizmo), _callback(callback) {}
void handle(filament::View::PickingQueryResult const &result)
{
_gizmo->unhighlight(Gizmo::Axis::X);
_gizmo->unhighlight(Gizmo::Axis::Y);
_gizmo->unhighlight(Gizmo::Axis::Z);
Gizmo::GizmoPickResultType resultType;
if (result.renderable == _gizmo->_parent)
{
resultType = Gizmo::GizmoPickResultType::Parent;
}
else if (result.renderable == _gizmo->_x || result.renderable == _gizmo->_xHitTest)
{
resultType = Gizmo::GizmoPickResultType::AxisX;
_gizmo->highlight(Gizmo::Axis::X);
}
else if (result.renderable == _gizmo->_y || result.renderable == _gizmo->_yHitTest)
{
_gizmo->highlight(Gizmo::Axis::Y);
resultType = Gizmo::GizmoPickResultType::AxisY;
}
else if (result.renderable == _gizmo->_z || result.renderable == _gizmo->_zHitTest)
{
_gizmo->highlight(Gizmo::Axis::Z);
resultType = Gizmo::GizmoPickResultType::AxisZ;
} else {
resultType = Gizmo::GizmoPickResultType::None;
}
_callback(resultType, result.fragCoords.x, result.fragCoords.y, result.fragCoords.z);
}
private:
Gizmo *_gizmo;
GizmoPickCallback _callback;
};
Entity createParentEntity();
Entity createHitTestEntity(Gizmo::Axis axis, Entity parent);
Entity createAxisEntity(Gizmo::Axis axis, Entity parent);
math::mat4f getRotationForAxis(Gizmo::Axis axis);
Entity getEntityForAxis(Gizmo::Axis axis)
{
switch (axis)
{
case Gizmo::Axis::X:
return _x;
case Gizmo::Axis::Y:
return _y;
case Gizmo::Axis::Z:
return _z;
}
}
Engine *_engine;
Scene *_scene;
View *_view;
Material *_material;
utils::Entity _parent;
utils::Entity _x;
utils::Entity _y;
utils::Entity _z;
utils::Entity _xHitTest;
utils::Entity _yHitTest;
utils::Entity _zHitTest;
std::vector<utils::Entity> _entities;
std::vector<MaterialInstance *> _materialInstances;
math::float4 activeColors[3]{
math::float4{1.0f, 1.0f, 0.0f, 0.5f},
math::float4{1.0f, 1.0f, 0.0f, 0.5f},
math::float4{1.0f, 1.0f, 0.0f, 0.5f},
};
math::float4 inactiveColors[3]{
math::float4{1.0f, 0.0f, 0.0f, 1.0f},
math::float4{0.0f, 1.0f, 0.0f, 1.0f},
math::float4{0.0f, 0.0f, 1.0f, 1.0f},
};
};
}

View File

@@ -0,0 +1,163 @@
#pragma once
#include <memory>
#include <vector>
#include <filament/Engine.h>
#include <filament/RenderableManager.h>
#include <filament/VertexBuffer.h>
#include <filament/IndexBuffer.h>
#include <gltfio/AssetLoader.h>
#include <gltfio/FilamentAsset.h>
#include <gltfio/MaterialProvider.h>
#include "scene/GltfSceneAssetInstance.hpp"
#include "components/AnimationComponentManager.hpp"
#include "components/CollisionComponentManager.hpp"
#include "scene/SceneAsset.hpp"
namespace thermion
{
using namespace filament;
class GltfSceneAsset : public SceneAsset
{
public:
GltfSceneAsset(
gltfio::FilamentAsset *asset,
gltfio::AssetLoader *assetLoader,
Engine *engine,
MaterialInstance **materialInstances = nullptr,
size_t materialInstanceCount = 0,
int instanceIndex = -1) : _asset(asset),
_assetLoader(assetLoader),
_engine(engine),
_materialInstances(materialInstances),
_materialInstanceCount(materialInstanceCount)
{
}
~GltfSceneAsset();
SceneAsset *createInstance(MaterialInstance **materialInstances = nullptr, size_t materialInstanceCount = 0) override;
SceneAssetType getType() override
{
return SceneAsset::SceneAssetType::Gltf;
}
bool isInstance() override
{
return false;
}
utils::Entity getEntity() override
{
return _asset->getRoot();
}
MaterialInstance **getMaterialInstances() override
{
return _materialInstances;
}
size_t getMaterialInstanceCount() override
{
return _materialInstanceCount;
}
gltfio::FilamentAsset *getAsset()
{
return _asset;
}
void addAllEntities(Scene *scene) override
{
scene->addEntities(_asset->getEntities(), _asset->getEntityCount());
scene->addEntities(_asset->getLightEntities(), _asset->getLightEntityCount());
scene->addEntities(_asset->getCameraEntities(), _asset->getCameraEntityCount());
}
void removeAllEntities(Scene *scene) override
{
scene->removeEntities(_asset->getEntities(), _asset->getEntityCount());
scene->removeEntities(_asset->getLightEntities(), _asset->getLightEntityCount());
scene->removeEntities(_asset->getCameraEntities(), _asset->getCameraEntityCount());
}
void setPriority(RenderableManager &rm, int priority) override
{
const Entity *entities = _asset->getEntities();
for (int i = 0; i < _asset->getEntityCount(); i++)
{
if (rm.hasComponent(entities[i]))
{
auto renderableInstance = rm.getInstance(entities[i]);
rm.setPriority(renderableInstance, priority);
}
}
}
void setLayer(RenderableManager &rm, int layer) override
{
const Entity *entities = _asset->getEntities();
for (int i = 0; i < _asset->getEntityCount(); i++)
{
if (rm.hasComponent(entities[i]))
{
auto renderableInstance = rm.getInstance(entities[i]);
rm.setLayerMask(renderableInstance, 0xFF, 1u << (uint8_t)layer);
}
}
}
SceneAsset *getInstanceByEntity(utils::Entity entity) override
{
for (auto &instance : _instances)
{
if (instance->getEntity() == entity)
{
return instance.get();
}
}
return std::nullptr_t();
}
SceneAsset *getInstanceAt(size_t index) override
{
auto &asset = _instances[index];
return asset.get();
}
size_t getInstanceCount() override
{
return _instances.size();
}
size_t getChildEntityCount() override
{
return _asset->getEntityCount();
}
const Entity* getChildEntities() override {
return _asset->getEntities();
}
Entity findEntityByName(const char* name) override {
Entity entities[1];
auto found = _asset->getEntitiesByName(name, entities, 1);
return entities[0];
}
private:
gltfio::FilamentAsset *_asset;
gltfio::AssetLoader *_assetLoader;
Engine *_engine;
MaterialInstance **_materialInstances = nullptr;
size_t _materialInstanceCount = 0;
std::vector<std::unique_ptr<GltfSceneAssetInstance>> _instances;
};
} // namespace thermion

View File

@@ -0,0 +1,143 @@
#pragma once
#include <memory>
#include <vector>
#include <filament/Engine.h>
#include <filament/RenderableManager.h>
#include <filament/VertexBuffer.h>
#include <filament/IndexBuffer.h>
#include <gltfio/FilamentAsset.h>
#include <gltfio/FilamentInstance.h>
#include <gltfio/MaterialProvider.h>
#include "scene/SceneAsset.hpp"
namespace thermion
{
using namespace filament;
class GltfSceneAssetInstance : public SceneAsset
{
public:
GltfSceneAssetInstance(
gltfio::FilamentInstance *instance,
Engine *engine,
MaterialInstance **materialInstances = nullptr,
size_t materialInstanceCount = 0,
int instanceIndex = -1) : _instance(instance),
_materialInstances(materialInstances),
_materialInstanceCount(materialInstanceCount)
{
}
~GltfSceneAssetInstance();
SceneAsset *createInstance(MaterialInstance **materialInstances = nullptr, size_t materialInstanceCount = 0) override
{
return std::nullptr_t();
};
SceneAssetType getType() override
{
return SceneAsset::SceneAssetType::Gltf;
}
bool isInstance() override
{
return true;
}
utils::Entity getEntity() override
{
return _instance->getRoot();
}
MaterialInstance **getMaterialInstances() override
{
return _materialInstances;
}
size_t getMaterialInstanceCount() override
{
return _materialInstanceCount;
}
gltfio::FilamentInstance *getInstance()
{
return _instance;
}
void addAllEntities(Scene *scene) override
{
scene->addEntities(_instance->getEntities(), _instance->getEntityCount());
}
void removeAllEntities(Scene *scene) override {
scene->removeEntities(_instance->getEntities(), _instance->getEntityCount());
}
size_t getInstanceCount() override
{
return 0;
}
SceneAsset *getInstanceAt(size_t index) override
{
return std::nullptr_t();
}
size_t getChildEntityCount() override
{
return _instance->getEntityCount();
}
const Entity* getChildEntities() override {
return _instance->getEntities();
}
Entity findEntityByName(const char* name) override {
return Entity(); // not currently implemented
}
SceneAsset *getInstanceByEntity(utils::Entity entity) override {
return std::nullptr_t();
}
void setPriority(RenderableManager &rm, int priority) override
{
const Entity *entities = _instance->getEntities();
for (int i = 0; i < _instance->getEntityCount(); i++)
{
if (rm.hasComponent(entities[i]))
{
auto renderableInstance = rm.getInstance(entities[i]);
rm.setPriority(renderableInstance, priority);
}
}
}
void setLayer(RenderableManager &rm, int layer) override
{
const Entity *entities = _instance->getEntities();
for (int i = 0; i < _instance->getEntityCount(); i++)
{
if (rm.hasComponent(entities[i]))
{
auto renderableInstance = rm.getInstance(entities[i]);
rm.setLayerMask(renderableInstance, 0xFF, 1u << (uint8_t)layer);
}
}
}
private:
filament::Engine *_engine;
gltfio::FilamentInstance *_instance;
MaterialInstance **_materialInstances = nullptr;
size_t _materialInstanceCount = 0;
};
} // namespace thermion

View File

@@ -0,0 +1,49 @@
#pragma once
#include <vector>
#include <utils/Entity.h>
#include <filament/Engine.h>
#include <filament/Material.h>
#include <filament/MaterialInstance.h>
#include <filament/Scene.h>
#include <filament/Camera.h>
#include <filament/View.h>
#include <filament/Viewport.h>
#include <gltfio/AssetLoader.h>
#include <gltfio/FilamentAsset.h>
#include <gltfio/FilamentInstance.h>
#include <gltfio/ResourceLoader.h>
#include <filament/IndexBuffer.h>
#include <filament/InstanceBuffer.h>
namespace thermion {
using namespace filament;
using namespace utils;
class GridOverlay {
public:
GridOverlay(Engine& engine);
void destroy();
utils::Entity sphere() {
return _sphereEntity;
}
utils::Entity grid() {
return _gridEntity;
}
private:
Engine &_engine;
utils::Entity _gridEntity;
utils::Entity _sphereEntity;
Material* _material;
MaterialInstance* _materialInstance;
MaterialInstance* _sphereMaterialInstance;
};
}

View File

@@ -0,0 +1,53 @@
#pragma once
#include <memory>
#include <utils/Entity.h>
#include <gltfio/FilamentAsset.h>
#include <filament/Scene.h>
#include "CustomGeometry.hpp"
#include "Log.hpp"
namespace thermion {
using namespace filament;
using namespace utils;
class SceneAsset {
public:
enum SceneAssetType { Gltf, Geometry, Light, Skybox, Ibl, Image, Gizmo };
virtual ~SceneAsset() {
}
virtual SceneAssetType getType() = 0;
virtual utils::Entity getEntity() {
return utils::Entity::import(0);
}
virtual bool isInstance() = 0;
virtual SceneAsset* createInstance(MaterialInstance **materialInstances, size_t materialInstanceCount) = 0;
virtual MaterialInstance **getMaterialInstances() = 0;
virtual size_t getMaterialInstanceCount() = 0;
virtual void addAllEntities(Scene *scene) = 0;
virtual void removeAllEntities(Scene *scene) = 0;
virtual size_t getInstanceCount() = 0;
virtual SceneAsset *getInstanceByEntity(utils::Entity entity) = 0;
virtual SceneAsset *getInstanceAt(size_t index) = 0;
virtual size_t getChildEntityCount() = 0;
virtual const Entity* getChildEntities() = 0;
virtual Entity findEntityByName(const char* name) = 0;
virtual void setPriority(RenderableManager& rm, int mask) = 0;
virtual void setLayer(RenderableManager& rm, int layer) = 0;
};
}

View File

@@ -0,0 +1,350 @@
#pragma once
#include <mutex>
#include <vector>
#include <memory>
#include <map>
#include <set>
#include <filament/Scene.h>
#include <filament/Camera.h>
#include <filament/View.h>
#include <gltfio/AssetLoader.h>
#include <gltfio/FilamentAsset.h>
#include <gltfio/FilamentInstance.h>
#include <gltfio/ResourceLoader.h>
#include <filament/IndexBuffer.h>
#include <filament/InstanceBuffer.h>
#include <utils/NameComponentManager.h>
#include "tsl/robin_map.h"
#include "AnimationManager.hpp"
#include "CustomGeometry.hpp"
#include "Gizmo.hpp"
#include "GridOverlay.hpp"
#include "ResourceBuffer.hpp"
#include "SceneAsset.hpp"
#include "components/CollisionComponentManager.hpp"
namespace thermion
{
typedef int32_t EntityId;
using namespace filament;
using namespace filament::gltfio;
using namespace utils;
using std::string;
using std::unique_ptr;
using std::vector;
class SceneManager
{
public:
SceneManager(
const ResourceLoaderWrapperImpl *const loader,
Engine *engine,
Scene *scene,
const char *uberArchivePath,
Camera *mainCamera);
~SceneManager();
enum LAYERS
{
DEFAULT_ASSETS = 0,
BACKGROUND = 6,
OVERLAY = 7,
};
////
/// @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.
///
SceneAsset* loadGltf(const char *uri, const char *relativeResourcePath, int numInstances = 1, bool keepData = false);
////
/// @brief Load the GLB from the specified path, optionally creating multiple instances.
/// @param uri the path to the asset. Should be either asset:// (representing a Flutter asset), or file:// (representing a filesystem file).
/// @param numInstances the number of instances to create. Must be at least 1.
/// @return an Entity representing the FilamentAsset associated with the loaded FilamentAsset.
///
SceneAsset* loadGlb(const char *uri, int numInstances, bool keepData);
/// @brief
/// @param data
/// @param length
/// @param numInstances
/// @param keepData
/// @param priority
/// @param layer
/// @param loadResourcesAsync
/// @return
SceneAsset* loadGlbFromBuffer(const uint8_t *data, size_t length, int numInstances = 1, bool keepData = false, int priority = 4, int layer = 0, bool loadResourcesAsync = false);
///
/// Creates an instance of the given entity.
/// This may return an instance from a pool of inactive instances; see [remove] for more information.
/// If [materialInstances] is provided, these wil
///
SceneAsset* createInstance(SceneAsset* asset, MaterialInstance **materialInstances = nullptr, size_t materialInstanceCount = 0);
/// @brief Removes the asset (and all its child entities) from the scene and "destroys" all resources.
/// If the asset is not an instance, the asset will be deleted.
/// If the asset is an instance, [remove] is not guaranted to delete the asset. It may be returned to a pool of inactive instances.
/// From the user's perspective, this can be considered as destroyed.
/// @param entity
void destroy(SceneAsset* entity);
/// @brief Destroys all assets, scenes, materials, etc.
///
void destroyAll();
/// @brief
/// @param entityId
void transformToUnitCube(EntityId entityId);
/// @brief
/// @param entities
/// @param transforms
/// @param numEntities
void queueTransformUpdates(EntityId *entities, math::mat4 *transforms, int numEntities);
/// @brief
/// @param entity
/// @param viewportCoordX
/// @param viewportCoordY
/// @param x
/// @param y
/// @param z
void queueRelativePositionUpdateWorldAxis(EntityId entity, float viewportCoordX, float viewportCoordY, float x, float y, float z);
/// @brief
/// @param view
/// @param entityId
/// @param viewportCoordX
/// @param viewportCoordY
void queueRelativePositionUpdateFromViewportVector(View *view, EntityId entityId, float viewportCoordX, float viewportCoordY);
const utils::Entity *getCameraEntities(EntityId e);
size_t getCameraEntityCount(EntityId e);
const utils::Entity *getLightEntities(EntityId e) noexcept;
size_t getLightEntityCount(EntityId e) noexcept;
/// @brief
void update();
/// @brief
/// @param data
/// @param length
/// @param name
/// @return
Texture *createTexture(const uint8_t *data, size_t length, const char *name);
/// @brief
/// @param entityId
/// @param texture
/// @param slotName
/// @param materialIndex
/// @return
bool applyTexture(EntityId entityId, Texture *texture, const char *slotName, int materialIndex);
/// @brief
/// @param texture
void destroyTexture(Texture *texture);
/// @brief
/// @param entity
/// @return
bool removeFromScene(EntityId entity);
/// @brief
/// @param entity
/// @return
bool addToScene(EntityId entity);
/// @brief
/// @param entity
/// @param onCollisionCallback
/// @param affectsCollidingTransform
void addCollisionComponent(EntityId entity, void (*onCollisionCallback)(const EntityId entityId1, const EntityId entityId2), bool affectsCollidingTransform);
/// @brief
/// @param entityId
///
///
void removeCollisionComponent(EntityId entityId);
/// @brief
/// @param entity
void testCollisions(EntityId entity);
/// @brief returns the number of instances of the FilamentAsset represented by the given entity.
/// @param entityId
/// @return the number of instances
int getInstanceCount(EntityId entityId);
/// @brief returns an array containing all instances of the FilamentAsset represented by the given entity.
/// @param entityId
void getInstances(EntityId entityId, EntityId *out);
///
/// Sets the draw priority for the given entity. See RenderableManager.h for more details.
///
void setPriority(EntityId entity, int priority);
/// @brief returns the 2D min/max viewport coordinates of the bounding box for the specified enitty;
/// @param out a pointer large enough to store four floats (the min/max coordinates of the bounding box)
/// @return
///
Aabb2 getScreenSpaceBoundingBox(View *view, EntityId entity);
/// @brief returns the 3D bounding box of the renderable instance for the given entity.
/// @return the bounding box
///
Aabb3 getRenderableBoundingBox(EntityId entity);
///
/// Creates an entity with the specified geometry/material/normals and adds to the scene.
/// If [keepData] is true, stores
///
SceneAsset *createGeometry(
float *vertices,
uint32_t numVertices,
float *normals,
uint32_t numNormals,
float *uvs,
uint32_t numUvs,
uint16_t *indices,
uint32_t numIndices,
filament::RenderableManager::PrimitiveType primitiveType = RenderableManager::PrimitiveType::TRIANGLES,
MaterialInstance **materialInstances = nullptr,
size_t materialInstanceCount = 0,
bool keepData = false);
gltfio::MaterialProvider *const getUnlitMaterialProvider()
{
return _unlitMaterialProvider;
}
gltfio::MaterialProvider *const getUbershaderMaterialProvider()
{
return _ubershaderProvider;
}
/// @brief
/// @param materialInstance
void destroy(MaterialInstance *materialInstance);
/// @brief
/// @return
MaterialInstance *createUnlitFixedSizeMaterialInstance();
/// @brief
/// @return
MaterialInstance *createUnlitMaterialInstance();
/// @brief
/// @param entityId
/// @param layer
void setVisibilityLayer(EntityId entityId, int layer);
/// @brief
/// @return
Camera *createCamera();
/// @brief
/// @param camera
void destroyCamera(Camera *camera);
/// @brief
/// @return
size_t getCameraCount();
/// @brief
/// @param index
/// @return
Camera *getCameraAt(size_t index);
/// @brief
/// @param view
/// @param scene
/// @return
Gizmo *createGizmo(View *view, Scene *scene);
/// @brief
/// @return
Scene *getScene()
{
return _scene;
}
/// @brief
/// @return
AnimationManager *getAnimationManager() {
return _animationManager.get();
}
/// @brief
/// @return
NameComponentManager *getNameComponentManager() {
return _ncm;
}
Entity getOverlayEntity(size_t index) {
if(index == 0) {
return _gridOverlay->grid();
} else if(index == 1) {
return _gridOverlay->sphere();
}
}
size_t getOverlayEntityCount() {
return 2;
}
private:
gltfio::AssetLoader *_assetLoader = nullptr;
const ResourceLoaderWrapperImpl *const _resourceLoaderWrapper;
Engine *_engine = nullptr;
Scene *_scene = nullptr;
Camera *_mainCamera;
gltfio::MaterialKey _defaultUnlitConfig;
gltfio::MaterialProvider *_ubershaderProvider = nullptr;
gltfio::MaterialProvider *_unlitMaterialProvider = nullptr;
gltfio::ResourceLoader *_gltfResourceLoader = nullptr;
gltfio::TextureProvider *_stbDecoder = nullptr;
gltfio::TextureProvider *_ktxDecoder = nullptr;
std::mutex _mutex;
std::vector<MaterialInstance *> _materialInstances;
Material *_unlitFixedSizeMaterial = nullptr;
utils::NameComponentManager *_ncm;
tsl::robin_map<EntityId, math::mat4> _transformUpdates;
std::set<Texture *> _textures;
std::vector<Camera *> _cameras;
std::vector<std::unique_ptr<SceneAsset>> _sceneAssets;
std::vector<std::unique_ptr<Gizmo>> _gizmos;
std::unique_ptr<AnimationManager> _animationManager = std::nullptr_t();
std::unique_ptr<CollisionComponentManager> _collisionComponentManager = std::nullptr_t();
GridOverlay *_gridOverlay = std::nullptr_t();
void _updateTransforms();
};
}