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:
@@ -0,0 +1,946 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
#include <stack>
|
||||
|
||||
#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 <utils/EntityManager.h>
|
||||
|
||||
#include <gltfio/Animator.h>
|
||||
#include <gltfio/AssetLoader.h>
|
||||
#include <gltfio/FilamentAsset.h>
|
||||
#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"
|
||||
#include "material/UnlitMaterialProvider.hpp"
|
||||
#include "material/unlit.h"
|
||||
|
||||
#include "StreamBufferAdapter.hpp"
|
||||
|
||||
#include "Log.hpp"
|
||||
|
||||
#include "scene/SceneManager.hpp"
|
||||
#include "scene/CustomGeometry.hpp"
|
||||
#include "scene/GeometrySceneAsset.hpp"
|
||||
#include "scene/GltfSceneAsset.hpp"
|
||||
#include "scene/Gizmo.hpp"
|
||||
#include "scene/SceneAsset.hpp"
|
||||
#include "scene/GeometrySceneAssetBuilder.hpp"
|
||||
#include "UnprojectTexture.hpp"
|
||||
|
||||
|
||||
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "material/image.h"
|
||||
#include "material/unlit_fixed_size.h"
|
||||
}
|
||||
|
||||
namespace thermion
|
||||
{
|
||||
|
||||
using namespace std::chrono;
|
||||
using namespace image;
|
||||
using namespace utils;
|
||||
using namespace filament;
|
||||
using namespace filament::gltfio;
|
||||
using std::unique_ptr;
|
||||
|
||||
SceneManager::SceneManager(const ResourceLoaderWrapperImpl *const resourceLoaderWrapper,
|
||||
Engine *engine,
|
||||
Scene *scene,
|
||||
const char *uberArchivePath,
|
||||
Camera *mainCamera)
|
||||
: _resourceLoaderWrapper(resourceLoaderWrapper),
|
||||
_engine(engine),
|
||||
_scene(scene),
|
||||
_mainCamera(mainCamera)
|
||||
{
|
||||
|
||||
_stbDecoder = createStbProvider(_engine);
|
||||
_ktxDecoder = createKtx2Provider(_engine);
|
||||
|
||||
_gltfResourceLoader = new ResourceLoader({.engine = _engine,
|
||||
.normalizeSkinningWeights = true});
|
||||
if (uberArchivePath)
|
||||
{
|
||||
auto uberdata = resourceLoaderWrapper->load(uberArchivePath);
|
||||
if (!uberdata.data)
|
||||
{
|
||||
Log("Failed to load ubershader material. This is fatal.");
|
||||
}
|
||||
_ubershaderProvider = gltfio::createUbershaderProvider(_engine, uberdata.data, uberdata.size);
|
||||
resourceLoaderWrapper->free(uberdata);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ubershaderProvider = gltfio::createUbershaderProvider(
|
||||
_engine, UBERARCHIVE_DEFAULT_DATA, UBERARCHIVE_DEFAULT_SIZE);
|
||||
}
|
||||
|
||||
_unlitMaterialProvider = new UnlitMaterialProvider(_engine, UNLIT_PACKAGE, UNLIT_UNLIT_SIZE);
|
||||
|
||||
utils::EntityManager &em = utils::EntityManager::get();
|
||||
|
||||
_ncm = new NameComponentManager(em);
|
||||
|
||||
_assetLoader = AssetLoader::create({_engine, _ubershaderProvider, _ncm, &em});
|
||||
|
||||
_gltfResourceLoader->addTextureProvider("image/ktx2", _ktxDecoder);
|
||||
_gltfResourceLoader->addTextureProvider("image/png", _stbDecoder);
|
||||
_gltfResourceLoader->addTextureProvider("image/jpeg", _stbDecoder);
|
||||
|
||||
auto &tm = _engine->getTransformManager();
|
||||
|
||||
_collisionComponentManager = std::make_unique<CollisionComponentManager>(tm);
|
||||
|
||||
_animationManager = std::make_unique<AnimationManager>(_engine, _scene);
|
||||
|
||||
_gridOverlay = new GridOverlay(*_engine);
|
||||
|
||||
_scene->addEntity(_gridOverlay->sphere());
|
||||
_scene->addEntity(_gridOverlay->grid());
|
||||
|
||||
_unlitFixedSizeMaterial =
|
||||
Material::Builder()
|
||||
.package(UNLIT_FIXED_SIZE_UNLIT_FIXED_SIZE_DATA, UNLIT_FIXED_SIZE_UNLIT_FIXED_SIZE_SIZE)
|
||||
.build(*_engine);
|
||||
}
|
||||
|
||||
SceneManager::~SceneManager()
|
||||
{
|
||||
for (auto camera : _cameras)
|
||||
{
|
||||
auto entity = camera->getEntity();
|
||||
_engine->destroyCameraComponent(entity);
|
||||
_engine->getEntityManager().destroy(entity);
|
||||
}
|
||||
|
||||
_engine->destroy(_unlitFixedSizeMaterial);
|
||||
_cameras.clear();
|
||||
|
||||
_gridOverlay->destroy();
|
||||
destroyAll();
|
||||
|
||||
_gltfResourceLoader->asyncCancelLoad();
|
||||
_ubershaderProvider->destroyMaterials();
|
||||
|
||||
_animationManager = std::nullptr_t();
|
||||
_collisionComponentManager = std::nullptr_t();
|
||||
delete _ncm;
|
||||
|
||||
delete _gltfResourceLoader;
|
||||
delete _stbDecoder;
|
||||
delete _ktxDecoder;
|
||||
delete _ubershaderProvider;
|
||||
AssetLoader::destroy(&_assetLoader);
|
||||
}
|
||||
|
||||
Gizmo *SceneManager::createGizmo(View *view, Scene *scene)
|
||||
{
|
||||
auto gizmo = std::make_unique<Gizmo>(_engine, view, scene, _unlitFixedSizeMaterial);
|
||||
auto *raw =gizmo.get();
|
||||
_sceneAssets.push_back(std::move(gizmo));
|
||||
return raw;
|
||||
}
|
||||
|
||||
int SceneManager::getInstanceCount(EntityId entityId)
|
||||
{
|
||||
auto entity = utils::Entity::import(entityId);
|
||||
for (auto &asset : _sceneAssets)
|
||||
{
|
||||
if (asset->getEntity() == entity)
|
||||
{
|
||||
return asset->getInstanceCount();
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void SceneManager::getInstances(EntityId entityId, EntityId *out)
|
||||
{
|
||||
auto entity = utils::Entity::import(entityId);
|
||||
for (auto &asset : _sceneAssets)
|
||||
{
|
||||
if (asset->getEntity() == entity)
|
||||
{
|
||||
for (int i = 0; i < asset->getInstanceCount(); i++)
|
||||
{
|
||||
out[i] = Entity::smuggle(asset->getInstanceAt(i)->getEntity());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SceneAsset *SceneManager::loadGltf(const char *uri,
|
||||
const char *relativeResourcePath,
|
||||
int numInstances,
|
||||
bool keepData)
|
||||
{
|
||||
if (numInstances < 1)
|
||||
{
|
||||
return std::nullptr_t();
|
||||
}
|
||||
|
||||
ResourceBuffer rbuf = _resourceLoaderWrapper->load(uri);
|
||||
|
||||
std::vector<FilamentInstance *> instances(numInstances);
|
||||
|
||||
FilamentAsset *asset = _assetLoader->createInstancedAsset((uint8_t *)rbuf.data, rbuf.size, instances.data(), numInstances);
|
||||
|
||||
if (!asset)
|
||||
{
|
||||
Log("Unable to load glTF asset at %d", uri);
|
||||
return std::nullptr_t();
|
||||
}
|
||||
|
||||
const char *const *const resourceUris = asset->getResourceUris();
|
||||
const size_t resourceUriCount = asset->getResourceUriCount();
|
||||
|
||||
std::vector<ResourceBuffer> resourceBuffers;
|
||||
|
||||
for (size_t i = 0; i < resourceUriCount; i++)
|
||||
{
|
||||
std::string uri = std::string(relativeResourcePath) + std::string("/") + std::string(resourceUris[i]);
|
||||
ResourceBuffer buf = _resourceLoaderWrapper->load(uri.c_str());
|
||||
|
||||
resourceBuffers.push_back(buf);
|
||||
|
||||
ResourceLoader::BufferDescriptor b(buf.data, buf.size);
|
||||
_gltfResourceLoader->addResourceData(resourceUris[i], std::move(b));
|
||||
}
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
if (!_gltfResourceLoader->asyncBeginLoad(asset))
|
||||
{
|
||||
Log("Unknown error loading glTF asset");
|
||||
_resourceLoaderWrapper->free(rbuf);
|
||||
for (auto &rb : resourceBuffers)
|
||||
{
|
||||
_resourceLoaderWrapper->free(rb);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
while (_gltfResourceLoader->asyncGetLoadProgress() < 1.0f)
|
||||
{
|
||||
_gltfResourceLoader->asyncUpdateLoad();
|
||||
}
|
||||
#else
|
||||
// load resources synchronously
|
||||
if (!_gltfResourceLoader->loadResources(asset))
|
||||
{
|
||||
Log("Unknown error loading glTF asset");
|
||||
_resourceLoaderWrapper->free(rbuf);
|
||||
for (auto &rb : resourceBuffers)
|
||||
{
|
||||
_resourceLoaderWrapper->free(rb);
|
||||
}
|
||||
return std::nullptr_t();
|
||||
}
|
||||
#endif
|
||||
|
||||
auto sceneAsset = std::make_unique<GltfSceneAsset>(
|
||||
asset,
|
||||
_assetLoader,
|
||||
_engine);
|
||||
auto filamentInstance = asset->getInstance();
|
||||
size_t entityCount = filamentInstance->getEntityCount();
|
||||
|
||||
_scene->addEntities(filamentInstance->getEntities(), entityCount);
|
||||
|
||||
for (auto &rb : resourceBuffers)
|
||||
{
|
||||
_resourceLoaderWrapper->free(rb);
|
||||
}
|
||||
_resourceLoaderWrapper->free(rbuf);
|
||||
|
||||
auto lights = asset->getLightEntities();
|
||||
_scene->addEntities(lights, asset->getLightEntityCount());
|
||||
|
||||
sceneAsset->createInstance();
|
||||
|
||||
auto entityId = Entity::smuggle(sceneAsset->getEntity());
|
||||
|
||||
auto *raw = sceneAsset.get();
|
||||
|
||||
_sceneAssets.push_back(std::move(sceneAsset));
|
||||
|
||||
Log("Finished loading glTF from %s", uri);
|
||||
|
||||
return raw;
|
||||
}
|
||||
|
||||
void SceneManager::setVisibilityLayer(EntityId entityId, int layer)
|
||||
{
|
||||
utils::Entity entity = utils::Entity::import(entityId);
|
||||
for (auto &asset : _sceneAssets)
|
||||
{
|
||||
if (asset->getEntity() == entity)
|
||||
{
|
||||
asset->setLayer(_engine->getRenderableManager(), layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SceneAsset *SceneManager::loadGlbFromBuffer(const uint8_t *data, size_t length, int numInstances, bool keepData, int priority, int layer, bool loadResourcesAsync)
|
||||
{
|
||||
auto &rm = _engine->getRenderableManager();
|
||||
|
||||
std::vector<FilamentInstance *> instances(numInstances);
|
||||
|
||||
FilamentAsset *asset = _assetLoader->createInstancedAsset((const uint8_t *)data, length, instances.data(), numInstances);
|
||||
|
||||
Log("Created instanced asset.");
|
||||
|
||||
if (!asset)
|
||||
{
|
||||
Log("Unknown error loading GLB asset.");
|
||||
return std::nullptr_t();
|
||||
}
|
||||
#ifdef __EMSCRIPTEN__
|
||||
if (!_gltfResourceLoader->asyncBeginLoad(asset))
|
||||
{
|
||||
Log("Unknown error loading glb asset");
|
||||
return 0;
|
||||
}
|
||||
while (_gltfResourceLoader->asyncGetLoadProgress() < 1.0f)
|
||||
{
|
||||
_gltfResourceLoader->asyncUpdateLoad();
|
||||
}
|
||||
#else
|
||||
if (loadResourcesAsync)
|
||||
{
|
||||
if (!_gltfResourceLoader->asyncBeginLoad(asset))
|
||||
{
|
||||
Log("Unknown error loading glb asset");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_gltfResourceLoader->loadResources(asset))
|
||||
{
|
||||
Log("Unknown error loading glb asset");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
auto sceneAsset = std::make_unique<GltfSceneAsset>(
|
||||
asset,
|
||||
_assetLoader,
|
||||
_engine);
|
||||
|
||||
auto sceneAssetInstance = sceneAsset->createInstance();
|
||||
sceneAssetInstance->addAllEntities(_scene);
|
||||
sceneAssetInstance->setPriority(_engine->getRenderableManager(), priority);
|
||||
sceneAssetInstance->setLayer(_engine->getRenderableManager(), layer);
|
||||
|
||||
auto *raw = sceneAsset.get();
|
||||
|
||||
_sceneAssets.push_back(std::move(sceneAsset));
|
||||
|
||||
return raw;
|
||||
}
|
||||
|
||||
SceneAsset *SceneManager::createInstance(SceneAsset *asset, MaterialInstance **materialInstances, size_t materialInstanceCount)
|
||||
{
|
||||
std::lock_guard lock(_mutex);
|
||||
|
||||
auto instance = asset->createInstance(materialInstances, materialInstanceCount);
|
||||
if (instance)
|
||||
{
|
||||
instance->addAllEntities(_scene);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("Failed to create instance");
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
SceneAsset *SceneManager::loadGlb(const char *uri, int numInstances, bool keepData)
|
||||
{
|
||||
ResourceBuffer rbuf = _resourceLoaderWrapper->load(uri);
|
||||
auto entity = loadGlbFromBuffer((const uint8_t *)rbuf.data, rbuf.size, numInstances, keepData);
|
||||
_resourceLoaderWrapper->free(rbuf);
|
||||
return entity;
|
||||
}
|
||||
|
||||
bool SceneManager::removeFromScene(EntityId entityId)
|
||||
{
|
||||
_scene->remove(Entity::import(entityId));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SceneManager::addToScene(EntityId entityId)
|
||||
{
|
||||
_scene->addEntity(Entity::import(entityId));
|
||||
return true;
|
||||
}
|
||||
|
||||
void SceneManager::destroyAll()
|
||||
{
|
||||
std::lock_guard lock(_mutex);
|
||||
|
||||
for (auto &asset : _sceneAssets)
|
||||
{
|
||||
asset->removeAllEntities(_scene);
|
||||
}
|
||||
|
||||
_sceneAssets.clear();
|
||||
|
||||
for (auto *texture : _textures)
|
||||
{
|
||||
_engine->destroy(texture);
|
||||
}
|
||||
|
||||
for (auto *materialInstance : _materialInstances)
|
||||
{
|
||||
_engine->destroy(materialInstance);
|
||||
}
|
||||
|
||||
_textures.clear();
|
||||
_materialInstances.clear();
|
||||
}
|
||||
|
||||
void SceneManager::destroy(SceneAsset *asset)
|
||||
{
|
||||
|
||||
std::lock_guard lock(_mutex);
|
||||
|
||||
auto it = std::remove_if(_sceneAssets.begin(), _sceneAssets.end(), [=](auto &sceneAsset)
|
||||
{ return sceneAsset.get() == asset; });
|
||||
if (it != _sceneAssets.end())
|
||||
{
|
||||
auto entity = (*it)->getEntity();
|
||||
_collisionComponentManager->removeComponent(entity);
|
||||
_animationManager->removeAnimationComponent(utils::Entity::smuggle(entity));
|
||||
for (int i = 0; i < (*it)->getChildEntityCount(); i++)
|
||||
{
|
||||
auto childEntity = (*it)->getChildEntities()[i];
|
||||
_collisionComponentManager->removeComponent(childEntity);
|
||||
_animationManager->removeAnimationComponent(utils::Entity::smuggle(childEntity));
|
||||
}
|
||||
(*it)->removeAllEntities(_scene);
|
||||
_sceneAssets.erase(it, _sceneAssets.end());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Texture *SceneManager::createTexture(const uint8_t *data, size_t length, const char *name)
|
||||
{
|
||||
|
||||
// Create an input stream from the data
|
||||
std::istringstream stream(std::string(reinterpret_cast<const char *>(data), length));
|
||||
|
||||
// Decode the image
|
||||
image::LinearImage linearImage = image::ImageDecoder::decode(stream, name, image::ImageDecoder::ColorSpace::SRGB);
|
||||
|
||||
if (!linearImage.isValid())
|
||||
{
|
||||
Log("Failed to decode image.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t w = linearImage.getWidth();
|
||||
uint32_t h = linearImage.getHeight();
|
||||
uint32_t channels = linearImage.getChannels();
|
||||
|
||||
Texture::InternalFormat textureFormat = channels == 3 ? Texture::InternalFormat::RGB16F
|
||||
: Texture::InternalFormat::RGBA16F;
|
||||
Texture::Format bufferFormat = channels == 3 ? Texture::Format::RGB
|
||||
: Texture::Format::RGBA;
|
||||
|
||||
Texture *texture = Texture::Builder()
|
||||
.width(w)
|
||||
.height(h)
|
||||
.levels(1)
|
||||
.format(textureFormat)
|
||||
.sampler(Texture::Sampler::SAMPLER_2D)
|
||||
.build(*_engine);
|
||||
|
||||
if (!texture)
|
||||
{
|
||||
Log("Failed to create texture: ");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Texture::PixelBufferDescriptor buffer(
|
||||
linearImage.getPixelRef(),
|
||||
size_t(w * h * channels * sizeof(float)),
|
||||
bufferFormat,
|
||||
Texture::Type::FLOAT);
|
||||
|
||||
texture->setImage(*_engine, 0, std::move(buffer));
|
||||
|
||||
Log("Created texture: %s (%d x %d, %d channels)", name, w, h, channels);
|
||||
|
||||
_textures.insert(texture);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
bool SceneManager::applyTexture(EntityId entityId, Texture *texture, const char *parameterName, int materialIndex)
|
||||
{
|
||||
auto entity = Entity::import(entityId);
|
||||
|
||||
if (entity.isNull())
|
||||
{
|
||||
Log("Entity %d is null?", entityId);
|
||||
return false;
|
||||
}
|
||||
|
||||
RenderableManager &rm = _engine->getRenderableManager();
|
||||
|
||||
auto renderable = rm.getInstance(entity);
|
||||
|
||||
if (!renderable.isValid())
|
||||
{
|
||||
Log("Renderable not valid, was the entity id correct (%d)?", entityId);
|
||||
return false;
|
||||
}
|
||||
|
||||
MaterialInstance *mi = rm.getMaterialInstanceAt(renderable, materialIndex);
|
||||
|
||||
if (!mi)
|
||||
{
|
||||
Log("ERROR: material index must be less than number of material instances");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto sampler = TextureSampler();
|
||||
mi->setParameter(parameterName, texture, sampler);
|
||||
Log("Applied texture to entity %d", entityId);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SceneManager::destroyTexture(Texture *texture)
|
||||
{
|
||||
if (_textures.find(texture) == _textures.end())
|
||||
{
|
||||
Log("Warning: couldn't find texture");
|
||||
}
|
||||
_textures.erase(texture);
|
||||
_engine->destroy(texture);
|
||||
}
|
||||
|
||||
void SceneManager::addCollisionComponent(EntityId entityId, void (*onCollisionCallback)(const EntityId entityId1, const EntityId entityId2), bool affectsTransform)
|
||||
{
|
||||
std::lock_guard lock(_mutex);
|
||||
utils::Entity entity = utils::Entity::import(entityId);
|
||||
for (auto &asset : _sceneAssets)
|
||||
{
|
||||
auto *instance = reinterpret_cast<GltfSceneAssetInstance *>(asset->getInstanceByEntity(entity));
|
||||
if (instance)
|
||||
{
|
||||
auto collisionInstance = _collisionComponentManager->addComponent(instance->getInstance()->getRoot());
|
||||
_collisionComponentManager->elementAt<0>(collisionInstance) = instance->getInstance()->getBoundingBox();
|
||||
_collisionComponentManager->elementAt<1>(collisionInstance) = onCollisionCallback;
|
||||
_collisionComponentManager->elementAt<2>(collisionInstance) = affectsTransform;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SceneManager::removeCollisionComponent(EntityId entityId)
|
||||
{
|
||||
std::lock_guard lock(_mutex);
|
||||
utils::Entity entity = utils::Entity::import(entityId);
|
||||
_collisionComponentManager->removeComponent(entity);
|
||||
}
|
||||
|
||||
void SceneManager::testCollisions(EntityId entityId)
|
||||
{
|
||||
utils::Entity entity = utils::Entity::import(entityId);
|
||||
for (auto &asset : _sceneAssets)
|
||||
{
|
||||
auto *instance = reinterpret_cast<GltfSceneAssetInstance *>(asset->getInstanceByEntity(entity));
|
||||
if (instance)
|
||||
{
|
||||
const auto &tm = _engine->getTransformManager();
|
||||
auto transformInstance = tm.getInstance(entity);
|
||||
auto worldTransform = tm.getWorldTransform(transformInstance);
|
||||
auto aabb = instance->getInstance()->getBoundingBox();
|
||||
aabb = aabb.transform(worldTransform);
|
||||
_collisionComponentManager->collides(entity, aabb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SceneManager::update()
|
||||
{
|
||||
_animationManager->update();
|
||||
_updateTransforms();
|
||||
}
|
||||
|
||||
void SceneManager::_updateTransforms()
|
||||
{
|
||||
std::lock_guard lock(_mutex);
|
||||
|
||||
// auto &tm = _engine->getTransformManager();
|
||||
// tm.openLocalTransformTransaction();
|
||||
|
||||
// for (const auto &[entityId, transformUpdate] : _transformUpdates)
|
||||
// {
|
||||
// const auto &pos = _instances.find(entityId);
|
||||
|
||||
// bool isCollidable = true;
|
||||
// Entity entity;
|
||||
// filament::TransformManager::Instance transformInstance;
|
||||
// filament::math::mat4f transform;
|
||||
// Aabb boundingBox;
|
||||
// if (pos == _instances.end())
|
||||
// {
|
||||
// isCollidable = false;
|
||||
// entity = Entity::import(entityId);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// const auto *instance = pos->second;
|
||||
// entity = instance->getRoot();
|
||||
// boundingBox = instance->getBoundingBox();
|
||||
// }
|
||||
|
||||
// transformInstance = tm.getInstance(entity);
|
||||
// transform = tm.getTransform(transformInstance);
|
||||
|
||||
// if (isCollidable)
|
||||
// {
|
||||
// auto transformedBB = boundingBox.transform(transform);
|
||||
|
||||
// auto collisionAxes = _collisionComponentManager->collides(entity, transformedBB);
|
||||
|
||||
// if (collisionAxes.size() == 1)
|
||||
// {
|
||||
// // auto globalAxis = collisionAxes[0];
|
||||
// // globalAxis *= norm(relativeTranslation);
|
||||
// // auto newRelativeTranslation = relativeTranslation + globalAxis;
|
||||
// // translation -= relativeTranslation;
|
||||
// // translation += newRelativeTranslation;
|
||||
// // transform = composeMatrix(translation, rotation, scale);
|
||||
// }
|
||||
// else if (collisionAxes.size() > 1)
|
||||
// {
|
||||
// // translation -= relativeTranslation;
|
||||
// // transform = composeMatrix(translation, rotation, scale);
|
||||
// }
|
||||
// }
|
||||
// tm.setTransform(transformInstance, transformUpdate);
|
||||
// }
|
||||
// tm.commitLocalTransformTransaction();
|
||||
// _transformUpdates.clear();
|
||||
}
|
||||
|
||||
void SceneManager::queueRelativePositionUpdateFromViewportVector(View *view, 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
}
|
||||
|
||||
void SceneManager::queueTransformUpdates(EntityId *entities, math::mat4 *transforms, int numEntities)
|
||||
{
|
||||
std::lock_guard lock(_mutex);
|
||||
|
||||
for (int i = 0; i < numEntities; i++)
|
||||
{
|
||||
auto entity = entities[i];
|
||||
const auto &pos = _transformUpdates.find(entity);
|
||||
if (pos == _transformUpdates.end())
|
||||
{
|
||||
_transformUpdates.emplace(entity, transforms[i]);
|
||||
}
|
||||
auto curr = _transformUpdates[entity];
|
||||
_transformUpdates[entity] = curr;
|
||||
}
|
||||
}
|
||||
|
||||
Aabb3 SceneManager::getRenderableBoundingBox(EntityId entityId)
|
||||
{
|
||||
auto &rm = _engine->getRenderableManager();
|
||||
auto instance = rm.getInstance(Entity::import(entityId));
|
||||
if (!instance.isValid())
|
||||
{
|
||||
return Aabb3{};
|
||||
}
|
||||
auto box = rm.getAxisAlignedBoundingBox(instance);
|
||||
return Aabb3{box.center.x, box.center.y, box.center.z, box.halfExtent.x, box.halfExtent.y, box.halfExtent.z};
|
||||
}
|
||||
|
||||
Aabb2 SceneManager::getScreenSpaceBoundingBox(View *view, EntityId entityId)
|
||||
{
|
||||
const auto &camera = view->getCamera();
|
||||
const auto &viewport = view->getViewport();
|
||||
|
||||
auto &tcm = _engine->getTransformManager();
|
||||
auto &rcm = _engine->getRenderableManager();
|
||||
|
||||
// Get the projection and view matrices
|
||||
math::mat4 projMatrix = camera.getProjectionMatrix();
|
||||
math::mat4 viewMatrix = camera.getViewMatrix();
|
||||
math::mat4 vpMatrix = projMatrix * viewMatrix;
|
||||
|
||||
auto entity = Entity::import(entityId);
|
||||
|
||||
auto renderable = rcm.getInstance(entity);
|
||||
auto worldTransform = tcm.getWorldTransform(tcm.getInstance(entity));
|
||||
|
||||
// Get the axis-aligned bounding box in model space
|
||||
Box aabb = rcm.getAxisAlignedBoundingBox(renderable);
|
||||
|
||||
auto min = aabb.getMin();
|
||||
auto max = aabb.getMax();
|
||||
|
||||
// Transform the 8 corners of the AABB to clip space
|
||||
std::array<math::float4, 8> corners = {
|
||||
worldTransform * math::float4(min.x, min.y, min.z, 1.0f),
|
||||
worldTransform * math::float4(max.x, min.y, min.z, 1.0f),
|
||||
worldTransform * math::float4(min.x, max.y, min.z, 1.0f),
|
||||
worldTransform * math::float4(max.x, max.y, min.z, 1.0f),
|
||||
worldTransform * math::float4(min.x, min.y, max.z, 1.0f),
|
||||
worldTransform * math::float4(max.x, min.y, max.z, 1.0f),
|
||||
worldTransform * math::float4(min.x, max.y, max.z, 1.0f),
|
||||
worldTransform * math::float4(max.x, max.y, max.z, 1.0f)};
|
||||
|
||||
// Project corners to clip space and convert to viewport space
|
||||
float minX = std::numeric_limits<float>::max();
|
||||
float minY = std::numeric_limits<float>::max();
|
||||
float maxX = std::numeric_limits<float>::lowest();
|
||||
float maxY = std::numeric_limits<float>::lowest();
|
||||
|
||||
for (const auto &corner : corners)
|
||||
{
|
||||
|
||||
math::float4 clipSpace = vpMatrix * corner;
|
||||
|
||||
// Check if the point is behind the camera
|
||||
if (clipSpace.w <= 0)
|
||||
{
|
||||
continue; // Skip this point
|
||||
}
|
||||
|
||||
// Perform perspective division
|
||||
math::float3 ndcSpace = clipSpace.xyz / clipSpace.w;
|
||||
|
||||
// Clamp NDC coordinates to [-1, 1] range
|
||||
ndcSpace.x = std::max(-1.0f, std::min(1.0f, ndcSpace.x));
|
||||
ndcSpace.y = std::max(-1.0f, std::min(1.0f, ndcSpace.y));
|
||||
|
||||
// Convert NDC to viewport space
|
||||
float viewportX = (ndcSpace.x * 0.5f + 0.5f) * viewport.width;
|
||||
float viewportY = (1.0f - (ndcSpace.y * 0.5f + 0.5f)) * viewport.height; // Flip Y-axis
|
||||
|
||||
minX = std::min(minX, viewportX);
|
||||
minY = std::min(minY, viewportY);
|
||||
maxX = std::max(maxX, viewportX);
|
||||
maxY = std::max(maxY, viewportY);
|
||||
}
|
||||
|
||||
return Aabb2{minX, minY, maxX, maxY};
|
||||
}
|
||||
|
||||
static filament::gltfio::MaterialKey getDefaultUnlitMaterialConfig(int numUvs)
|
||||
{
|
||||
filament::gltfio::MaterialKey config;
|
||||
memset(&config, 0, sizeof(config));
|
||||
|
||||
config.unlit = false;
|
||||
config.doubleSided = false;
|
||||
config.useSpecularGlossiness = false;
|
||||
config.alphaMode = filament::gltfio::AlphaMode::OPAQUE;
|
||||
config.hasBaseColorTexture = numUvs > 0;
|
||||
config.baseColorUV = 0;
|
||||
config.hasVertexColors = false;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
SceneAsset *SceneManager::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,
|
||||
filament::MaterialInstance **materialInstances,
|
||||
size_t materialInstanceCount,
|
||||
bool keepData)
|
||||
{
|
||||
utils::Entity entity;
|
||||
|
||||
auto builder = GeometrySceneAssetBuilder(_engine)
|
||||
.vertices(vertices, numVertices)
|
||||
.indices(indices, numIndices)
|
||||
.primitiveType(primitiveType);
|
||||
|
||||
if (normals)
|
||||
{
|
||||
builder.normals(normals, numNormals);
|
||||
}
|
||||
|
||||
if (uvs)
|
||||
{
|
||||
builder.uvs(uvs, numUvs);
|
||||
}
|
||||
|
||||
builder.materials(materialInstances, materialInstanceCount);
|
||||
|
||||
auto sceneAsset = builder.build();
|
||||
|
||||
if (!sceneAsset)
|
||||
{
|
||||
Log("Failed to create geometry");
|
||||
return std::nullptr_t();
|
||||
}
|
||||
|
||||
entity = sceneAsset->getEntity();
|
||||
_scene->addEntity(entity);
|
||||
auto *raw = sceneAsset.get();
|
||||
_sceneAssets.push_back(std::move(sceneAsset));
|
||||
return raw;
|
||||
}
|
||||
|
||||
void SceneManager::destroy(filament::MaterialInstance *instance)
|
||||
{
|
||||
auto it = std::find(_materialInstances.begin(), _materialInstances.end(), instance);
|
||||
if (it != _materialInstances.end())
|
||||
{
|
||||
_materialInstances.erase(it);
|
||||
}
|
||||
_engine->destroy(instance);
|
||||
}
|
||||
|
||||
MaterialInstance *SceneManager::createUnlitFixedSizeMaterialInstance()
|
||||
{
|
||||
auto instance = _unlitFixedSizeMaterial->createInstance();
|
||||
instance->setParameter("scale", 1.0f);
|
||||
return instance;
|
||||
}
|
||||
|
||||
MaterialInstance *SceneManager::createUnlitMaterialInstance()
|
||||
{
|
||||
UvMap uvmap;
|
||||
auto instance = _unlitMaterialProvider->createMaterialInstance(nullptr, &uvmap);
|
||||
instance->setParameter("baseColorFactor", filament::math::float4{1.0f, 1.0f, 1.0f, 1.0f});
|
||||
instance->setParameter("baseColorIndex", -1);
|
||||
_materialInstances.push_back(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
Camera *SceneManager::createCamera()
|
||||
{
|
||||
auto entity = EntityManager::get().create();
|
||||
auto camera = _engine->createCamera(entity);
|
||||
_cameras.push_back(camera);
|
||||
return camera;
|
||||
}
|
||||
|
||||
void SceneManager::destroyCamera(Camera *camera)
|
||||
{
|
||||
auto entity = camera->getEntity();
|
||||
_engine->destroyCameraComponent(entity);
|
||||
_engine->getEntityManager().destroy(entity);
|
||||
auto it = std::find(_cameras.begin(), _cameras.end(), camera);
|
||||
if (it != _cameras.end())
|
||||
{
|
||||
_cameras.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
size_t SceneManager::getCameraCount()
|
||||
{
|
||||
return _cameras.size() + 1;
|
||||
}
|
||||
|
||||
Camera *SceneManager::getCameraAt(size_t index)
|
||||
{
|
||||
if (index == 0)
|
||||
{
|
||||
return _mainCamera;
|
||||
}
|
||||
if (index - 1 > _cameras.size() - 1)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return _cameras[index - 1];
|
||||
}
|
||||
|
||||
void SceneManager::transformToUnitCube(EntityId entityId)
|
||||
{
|
||||
auto entity = utils::Entity::import(entityId);
|
||||
for (auto &asset : _sceneAssets)
|
||||
{
|
||||
auto *instance = reinterpret_cast<GltfSceneAssetInstance *>(asset->getInstanceByEntity(entity));
|
||||
if (instance)
|
||||
{
|
||||
auto &transformManager = _engine->getTransformManager();
|
||||
const auto &entity = utils::Entity::import(entityId);
|
||||
auto transformInstance = transformManager.getInstance(entity);
|
||||
if (!transformInstance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto aabb = instance->getInstance()->getBoundingBox();
|
||||
auto center = aabb.center();
|
||||
auto halfExtent = aabb.extent();
|
||||
auto maxExtent = max(halfExtent) * 2;
|
||||
auto scaleFactor = 2.0f / maxExtent;
|
||||
auto transform = math::mat4f::scaling(scaleFactor) * math::mat4f::translation(-center);
|
||||
transformManager.setTransform(transformManager.getInstance(entity), transform);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace thermion
|
||||
Reference in New Issue
Block a user