fix: allow destroying instances independently of owner

This commit is contained in:
Nick Fisher
2025-01-02 16:46:44 +08:00
parent 8e0ba8ac4e
commit 3e181b6aff
13 changed files with 141 additions and 91 deletions

View File

@@ -16,18 +16,20 @@ namespace thermion
class GeometrySceneAsset : public SceneAsset class GeometrySceneAsset : public SceneAsset
{ {
public: public:
GeometrySceneAsset(bool isInstance, GeometrySceneAsset(Engine *engine,
Engine *engine,
VertexBuffer *vertexBuffer, VertexBuffer *vertexBuffer,
IndexBuffer *indexBuffer, IndexBuffer *indexBuffer,
MaterialInstance **materialInstances, MaterialInstance **materialInstances,
size_t materialInstanceCount, size_t materialInstanceCount,
RenderableManager::PrimitiveType primitiveType, RenderableManager::PrimitiveType primitiveType,
Box boundingBox); Box boundingBox,
GeometrySceneAsset *instanceParent = std::nullptr_t());
~GeometrySceneAsset(); ~GeometrySceneAsset();
SceneAsset *createInstance(MaterialInstance **materialInstances = nullptr, size_t materialInstanceCount = 0) override; SceneAsset *createInstance(MaterialInstance **materialInstances = nullptr, size_t materialInstanceCount = 0) override;
void destroyInstance(SceneAsset *sceneAsset) override;
SceneAssetType getType() override SceneAssetType getType() override
{ {
return SceneAsset::SceneAssetType::Geometry; return SceneAsset::SceneAssetType::Geometry;
@@ -35,7 +37,11 @@ namespace thermion
bool isInstance() override bool isInstance() override
{ {
return _isInstance; return _instanceOwner;
}
SceneAsset *getInstanceOwner() override {
return _instanceOwner;
} }
utils::Entity getEntity() override utils::Entity getEntity() override
@@ -132,7 +138,7 @@ namespace thermion
MaterialInstance **_materialInstances = nullptr; MaterialInstance **_materialInstances = nullptr;
size_t _materialInstanceCount = 0; size_t _materialInstanceCount = 0;
Box _boundingBox; Box _boundingBox;
bool _isInstance = false; GeometrySceneAsset *_instanceOwner = std::nullptr_t();
utils::Entity _entity; utils::Entity _entity;
RenderableManager::PrimitiveType _primitiveType; RenderableManager::PrimitiveType _primitiveType;
std::vector<std::unique_ptr<GeometrySceneAsset>> _instances; std::vector<std::unique_ptr<GeometrySceneAsset>> _instances;

View File

@@ -66,7 +66,9 @@ namespace thermion
SceneAssetType getType() override { return SceneAssetType::Gizmo; } SceneAssetType getType() override { return SceneAssetType::Gizmo; }
utils::Entity getEntity() override { return _parent; } utils::Entity getEntity() override { return _parent; }
bool isInstance() override { return false; } bool isInstance() override { return false; }
SceneAsset *getInstanceOwner() override { return std::nullptr_t(); }
SceneAsset *createInstance(MaterialInstance **materialInstances, size_t materialInstanceCount) override { return nullptr; } SceneAsset *createInstance(MaterialInstance **materialInstances, size_t materialInstanceCount) override { return nullptr; }
void destroyInstance(SceneAsset *instance) override { return; }
MaterialInstance **getMaterialInstances() override { return _materialInstances.data(); } MaterialInstance **getMaterialInstances() override { return _materialInstances.data(); }
size_t getMaterialInstanceCount() override { return _materialInstances.size(); } size_t getMaterialInstanceCount() override { return _materialInstances.size(); }

View File

@@ -13,7 +13,6 @@
#include <utils/NameComponentManager.h> #include <utils/NameComponentManager.h>
#include "scene/GltfSceneAssetInstance.hpp" #include "scene/GltfSceneAssetInstance.hpp"
#include "components/AnimationComponentManager.hpp" #include "components/AnimationComponentManager.hpp"
#include "components/CollisionComponentManager.hpp" #include "components/CollisionComponentManager.hpp"
@@ -49,6 +48,12 @@ namespace thermion
SceneAsset *createInstance(MaterialInstance **materialInstances = nullptr, size_t materialInstanceCount = 0) override; SceneAsset *createInstance(MaterialInstance **materialInstances = nullptr, size_t materialInstanceCount = 0) override;
void destroyInstance(SceneAsset *asset) override {
auto it = std::remove_if(_instances.begin(), _instances.end(), [=](auto &sceneAsset)
{ return sceneAsset.get() == asset; });
_instances.erase(it, _instances.end());
};
SceneAssetType getType() override SceneAssetType getType() override
{ {
return SceneAsset::SceneAssetType::Gltf; return SceneAsset::SceneAssetType::Gltf;
@@ -59,6 +64,10 @@ namespace thermion
return false; return false;
} }
SceneAsset *getInstanceOwner() override {
return std::nullptr_t();
}
utils::Entity getEntity() override utils::Entity getEntity() override
{ {
return _asset->getRoot(); return _asset->getRoot();

View File

@@ -12,7 +12,6 @@
#include <gltfio/MaterialProvider.h> #include <gltfio/MaterialProvider.h>
#include <utils/NameComponentManager.h> #include <utils/NameComponentManager.h>
#include "scene/SceneAsset.hpp" #include "scene/SceneAsset.hpp"
namespace thermion namespace thermion
@@ -20,16 +19,20 @@ namespace thermion
using namespace filament; using namespace filament;
class GltfSceneAsset;
class GltfSceneAssetInstance : public SceneAsset class GltfSceneAssetInstance : public SceneAsset
{ {
public: public:
GltfSceneAssetInstance( GltfSceneAssetInstance(
GltfSceneAsset *instanceOwner,
gltfio::FilamentInstance *instance, gltfio::FilamentInstance *instance,
Engine *engine, Engine *engine,
utils::NameComponentManager* ncm, utils::NameComponentManager* ncm,
MaterialInstance **materialInstances = nullptr, MaterialInstance **materialInstances = nullptr,
size_t materialInstanceCount = 0, size_t materialInstanceCount = 0,
int instanceIndex = -1) : _ncm(ncm), int instanceIndex = -1) : _instanceOwner(instanceOwner),
_ncm(ncm),
_instance(instance), _instance(instance),
_materialInstances(materialInstances), _materialInstances(materialInstances),
_materialInstanceCount(materialInstanceCount) _materialInstanceCount(materialInstanceCount)
@@ -43,6 +46,10 @@ namespace thermion
return std::nullptr_t(); return std::nullptr_t();
}; };
void destroyInstance(SceneAsset *instance) override {
}
SceneAssetType getType() override SceneAssetType getType() override
{ {
return SceneAsset::SceneAssetType::Gltf; return SceneAsset::SceneAssetType::Gltf;
@@ -53,6 +60,8 @@ namespace thermion
return true; return true;
} }
SceneAsset *getInstanceOwner() override;
utils::Entity getEntity() override utils::Entity getEntity() override
{ {
return _instance->getRoot(); return _instance->getRoot();
@@ -156,8 +165,9 @@ namespace thermion
filament::Engine *_engine; filament::Engine *_engine;
utils::NameComponentManager *_ncm; utils::NameComponentManager *_ncm;
gltfio::FilamentInstance *_instance; gltfio::FilamentInstance *_instance;
MaterialInstance **_materialInstances = nullptr; MaterialInstance **_materialInstances = std::nullptr_t();
size_t _materialInstanceCount = 0; size_t _materialInstanceCount = 0;
GltfSceneAsset *_instanceOwner = std::nullptr_t();
}; };
} // namespace thermion } // namespace thermion

View File

@@ -22,9 +22,14 @@ public:
SceneAssetType getType() override { return SceneAsset::SceneAssetType::Gizmo; } SceneAssetType getType() override { return SceneAsset::SceneAssetType::Gizmo; }
bool isInstance() override { return false; } bool isInstance() override { return false; }
SceneAsset *getInstanceOwner() override { return std::nullptr_t(); }
SceneAsset* createInstance(MaterialInstance** materialInstances = nullptr, SceneAsset* createInstance(MaterialInstance** materialInstances = nullptr,
size_t materialInstanceCount = 0) override; size_t materialInstanceCount = 0) override;
void destroyInstance(SceneAsset *instance) override {
}
MaterialInstance** getMaterialInstances() override { return &_materialInstance; } MaterialInstance** getMaterialInstances() override { return &_materialInstance; }
size_t getMaterialInstanceCount() override { return 1; } size_t getMaterialInstanceCount() override { return 1; }

View File

@@ -30,6 +30,9 @@ class SceneAsset {
} }
virtual bool isInstance() = 0; virtual bool isInstance() = 0;
virtual SceneAsset* getInstanceOwner() = 0;
virtual void destroyInstance(SceneAsset *instance) = 0;
virtual SceneAsset* createInstance(MaterialInstance **materialInstances, size_t materialInstanceCount) = 0; virtual SceneAsset* createInstance(MaterialInstance **materialInstances, size_t materialInstanceCount) = 0;

View File

@@ -19,16 +19,15 @@ namespace thermion
using namespace filament; using namespace filament;
GeometrySceneAsset::GeometrySceneAsset( GeometrySceneAsset::GeometrySceneAsset(
bool isInstance,
Engine *engine, Engine *engine,
VertexBuffer *vertexBuffer, VertexBuffer *vertexBuffer,
IndexBuffer *indexBuffer, IndexBuffer *indexBuffer,
MaterialInstance **materialInstances, MaterialInstance **materialInstances,
size_t materialInstanceCount, size_t materialInstanceCount,
RenderableManager::PrimitiveType primitiveType, RenderableManager::PrimitiveType primitiveType,
Box boundingBox) Box boundingBox,
: _isInstance(isInstance), GeometrySceneAsset *instanceOwner)
_engine(engine), _vertexBuffer(vertexBuffer), _indexBuffer(indexBuffer), _materialInstances(materialInstances), _materialInstanceCount(materialInstanceCount), _primitiveType(primitiveType), _boundingBox(boundingBox) : _engine(engine), _vertexBuffer(vertexBuffer), _indexBuffer(indexBuffer), _materialInstances(materialInstances), _materialInstanceCount(materialInstanceCount), _primitiveType(primitiveType), _boundingBox(boundingBox), _instanceOwner(instanceOwner)
{ {
_entity = utils::EntityManager::get().create(); _entity = utils::EntityManager::get().create();
@@ -49,75 +48,40 @@ namespace thermion
{ {
if (_engine) if (_engine)
{ {
if (_vertexBuffer && !_isInstance) if (_vertexBuffer && !isInstance())
_engine->destroy(_vertexBuffer); _engine->destroy(_vertexBuffer);
if (_indexBuffer && !_isInstance) if (_indexBuffer && !isInstance())
_engine->destroy(_indexBuffer); _engine->destroy(_indexBuffer);
} }
} }
SceneAsset *GeometrySceneAsset::createInstance(MaterialInstance **materialInstances, size_t materialInstanceCount) SceneAsset *GeometrySceneAsset::createInstance(MaterialInstance **materialInstances, size_t materialInstanceCount)
{ {
if (_isInstance) if (isInstance())
{ {
Log("Cannot create an instance from another instance. Ensure you are calling createInstance with the original asset."); Log("Cannot create an instance from another instance. Ensure you are calling createInstance with the original asset.");
return nullptr; return nullptr;
} }
std::unique_ptr<GeometrySceneAsset> instance = std::make_unique<GeometrySceneAsset>( std::unique_ptr<GeometrySceneAsset> instance = std::make_unique<GeometrySceneAsset>(
true,
_engine, _engine,
_vertexBuffer, _vertexBuffer,
_indexBuffer, _indexBuffer,
materialInstances, materialInstances,
materialInstanceCount, materialInstanceCount,
_primitiveType, _primitiveType,
_boundingBox); _boundingBox,
this);
auto *raw = instance.get(); auto *raw = instance.get();
_instances.push_back(std::move(instance)); _instances.push_back(std::move(instance));
return raw; return raw;
} }
// std::unique_ptr<GeometrySceneAsset> GeometrySceneAsset::create( void GeometrySceneAsset::destroyInstance(SceneAsset *asset)
// float *vertices, uint32_t numVertices, {
// float *normals, uint32_t numNormals, auto it = std::remove_if(_instances.begin(), _instances.end(), [=](auto &sceneAsset)
// float *uvs, uint32_t numUvs, { return sceneAsset.get() == asset; });
// uint16_t *indices, uint32_t numIndices, _instances.erase(it, _instances.end());
// MaterialInstance **materialInstances, }
// size_t materialInstanceCount,
// RenderableManager::PrimitiveType primitiveType,
// Engine *engine)
// {
// // Setup texture if needed
// if (asset && uvs && numUvs > 0 &&
// asset->getMaterialInstance() &&
// asset->getMaterialInstance()->getMaterial()->hasParameter("baseColorMap"))
// {
// static constexpr uint32_t textureSize = 1;
// static constexpr uint32_t white = 0x00ffffff;
// auto texture = Texture::Builder()
// .width(textureSize)
// .height(textureSize)
// .levels(1)
// .format(Texture::InternalFormat::RGBA8)
// .build(*engine);
// filament::backend::PixelBufferDescriptor pbd(
// &white, 4, Texture::Format::RGBA, Texture::Type::UBYTE);
// texture->setImage(*engine, 0, std::move(pbd));
// TextureSampler sampler(
// TextureSampler::MinFilter::NEAREST,
// TextureSampler::MagFilter::NEAREST);
// sampler.setWrapModeS(TextureSampler::WrapMode::REPEAT);
// sampler.setWrapModeT(TextureSampler::WrapMode::REPEAT);
// asset->getMaterialInstance()->setParameter("baseColorMap", texture, sampler);
// }
// return asset;
// }
} // namespace thermion } // namespace thermion

View File

@@ -107,7 +107,6 @@ namespace thermion
boundingBox.getMax().x, boundingBox.getMax().y, boundingBox.getMax().z); boundingBox.getMax().x, boundingBox.getMax().y, boundingBox.getMax().z);
auto asset = std::make_unique<GeometrySceneAsset>( auto asset = std::make_unique<GeometrySceneAsset>(
false,
mEngine, mEngine,
vertexBuffer, vertexBuffer,
indexBuffer, indexBuffer,

View File

@@ -49,6 +49,7 @@ namespace thermion
} }
std::unique_ptr<GltfSceneAssetInstance> sceneAssetInstance = std::make_unique<GltfSceneAssetInstance>( std::unique_ptr<GltfSceneAssetInstance> sceneAssetInstance = std::make_unique<GltfSceneAssetInstance>(
this,
instance, instance,
_engine, _engine,
_ncm, _ncm,

View File

@@ -1,7 +1,7 @@
#include "scene/GltfSceneAssetInstance.hpp" #include "scene/GltfSceneAssetInstance.hpp"
#include "gltfio/FilamentInstance.h" #include "scene/GltfSceneAsset.hpp"
#include "Log.hpp"
namespace thermion namespace thermion
{ {
@@ -10,5 +10,9 @@ namespace thermion
} }
SceneAsset *GltfSceneAssetInstance::getInstanceOwner() {
return static_cast<SceneAsset *>(_instanceOwner);
}
} }

View File

@@ -157,12 +157,13 @@ namespace thermion
{ {
if (!_grid) if (!_grid)
{ {
if(!material) { if (!material)
{
material = Material::Builder() material = Material::Builder()
.package(GRID_PACKAGE, GRID_GRID_SIZE) .package(GRID_PACKAGE, GRID_GRID_SIZE)
.build(*_engine); .build(*_engine);
} }
_grid = std::make_unique<GridOverlay>(*_engine, material); _grid = std::make_unique<GridOverlay>(*_engine, material);
} }
return _grid.get(); return _grid.get();
@@ -344,7 +345,7 @@ namespace thermion
_sceneAssets.push_back(std::move(sceneAsset)); _sceneAssets.push_back(std::move(sceneAsset));
Log("Finished loading glTF from %s", uri); Log("Loaded glTF asset from uri: %s", uri);
return raw; return raw;
} }
@@ -492,22 +493,25 @@ namespace thermion
std::lock_guard lock(_mutex); std::lock_guard lock(_mutex);
auto it = std::remove_if(_sceneAssets.begin(), _sceneAssets.end(), [=](auto &sceneAsset) auto entity = asset->getEntity();
{ return sceneAsset.get() == asset; }); _collisionComponentManager->removeComponent(entity);
if (it != _sceneAssets.end()) _animationManager->removeAnimationComponent(utils::Entity::smuggle(entity));
for (int i = 0; i < asset->getChildEntityCount(); i++)
{ {
auto entity = (*it)->getEntity(); auto childEntity = asset->getChildEntities()[i];
_collisionComponentManager->removeComponent(entity); _collisionComponentManager->removeComponent(childEntity);
_animationManager->removeAnimationComponent(utils::Entity::smuggle(entity)); _animationManager->removeAnimationComponent(utils::Entity::smuggle(childEntity));
for (int i = 0; i < (*it)->getChildEntityCount(); i++) }
{ asset->removeAllEntities(_scene);
auto childEntity = (*it)->getChildEntities()[i]; if (asset->isInstance())
_collisionComponentManager->removeComponent(childEntity); {
_animationManager->removeAnimationComponent(utils::Entity::smuggle(childEntity)); asset->destroyInstance(asset);
} }
(*it)->removeAllEntities(_scene); else
{
auto it = std::remove_if(_sceneAssets.begin(), _sceneAssets.end(), [=](auto &sceneAsset)
{ return sceneAsset.get() == asset; });
_sceneAssets.erase(it, _sceneAssets.end()); _sceneAssets.erase(it, _sceneAssets.end());
return;
} }
} }

View File

@@ -22,7 +22,7 @@ void main() async {
final cube = await viewer final cube = await viewer
.createGeometry(GeometryHelper.cube(normals: false, uvs: false)); .createGeometry(GeometryHelper.cube(normals: false, uvs: false));
await testHelper.capture(viewer, "geometry_cube_no_normals_uvs"); await testHelper.capture(viewer, "geometry_cube_no_normals_uvs");
await viewer.removeEntity(cube); await viewer.removeAsset(cube);
await testHelper.capture(viewer, "geometry_remove_cube"); await testHelper.capture(viewer, "geometry_remove_cube");
}); });
}); });
@@ -86,6 +86,8 @@ void main() async {
instance.entity, Matrix4.translation(Vector3.all(1))); instance.entity, Matrix4.translation(Vector3.all(1)));
await testHelper.capture(viewer, "geometry_instanced"); await testHelper.capture(viewer, "geometry_instanced");
await viewer.removeAsset(instance);
await testHelper.capture(viewer, "geometry_instance_removed");
}); });
}); });
@@ -167,7 +169,7 @@ void main() async {
"baseColorFactor", 0.0, 1.0, 0.0, 0.0); "baseColorFactor", 0.0, 1.0, 0.0, 0.0);
await testHelper.capture( await testHelper.capture(
viewer, "geometry_cube_with_custom_material_ubershader"); viewer, "geometry_cube_with_custom_material_ubershader");
await viewer.removeEntity(cube); await viewer.removeAsset(cube);
}); });
}); });
@@ -191,7 +193,7 @@ void main() async {
await viewer.applyTexture(texture as ThermionFFITexture, cube.entity); await viewer.applyTexture(texture as ThermionFFITexture, cube.entity);
await testHelper.capture( await testHelper.capture(
viewer, "geometry_cube_with_custom_material_ubershader_texture"); viewer, "geometry_cube_with_custom_material_ubershader_texture");
await viewer.removeEntity(cube); await viewer.removeAsset(cube);
await viewer.destroyTexture(texture); await viewer.destroyTexture(texture);
}); });
@@ -221,7 +223,7 @@ void main() async {
var texture = await viewer.createTexture(textureData); var texture = await viewer.createTexture(textureData);
await viewer.applyTexture(texture, cube.entity); await viewer.applyTexture(texture, cube.entity);
await testHelper.capture(viewer, "unlit_material_texture_only"); await testHelper.capture(viewer, "unlit_material_texture_only");
await viewer.removeEntity(cube); await viewer.removeAsset(cube);
}); });
}); });

View File

@@ -16,7 +16,7 @@ void main() async {
var model = await viewer var model = await viewer
.loadGlb("file://${testHelper.testDir}/assets/cube.glb"); .loadGlb("file://${testHelper.testDir}/assets/cube.glb");
await testHelper.capture(viewer, "load_glb_from_file"); await testHelper.capture(viewer, "load_glb_from_file");
await viewer.removeEntity(model); await viewer.removeAsset(model);
}); });
}); });
@@ -29,6 +29,28 @@ void main() async {
}); });
}); });
test('load glb from buffer with instances', () async {
await testHelper.withViewer((viewer) async {
var buffer =
File("${testHelper.testDir}/assets/cube.glb").readAsBytesSync();
var model = await viewer.loadGlbFromBuffer(buffer, numInstances: 2);
var instance = await model.createInstance();
await instance.addToScene();
await viewer.setTransform(
instance.entity, Matrix4.translation(Vector3(1, 0, 0)));
await testHelper.capture(viewer, "load_glb_from_buffer_with_instances");
await viewer.removeAsset(instance);
await testHelper.capture(viewer, "load_glb_from_buffer_instance_removed");
await viewer.removeAsset(model);
await testHelper.capture(viewer, "load_glb_from_buffer_original_removed");
}, bg: kRed);
});
test('load glb from buffer with priority', () async { test('load glb from buffer with priority', () async {
await testHelper.withViewer((viewer) async { await testHelper.withViewer((viewer) async {
viewer.addDirectLight(DirectLight.sun()); viewer.addDirectLight(DirectLight.sun());
@@ -58,7 +80,7 @@ void main() async {
await testHelper.withViewer((viewer) async { await testHelper.withViewer((viewer) async {
var model = await viewer.loadGlb( var model = await viewer.loadGlb(
"file://${testHelper.testDir}/assets/cube.glb", "file://${testHelper.testDir}/assets/cube.glb",
numInstances: 2); numInstances: 32);
await testHelper.capture(viewer, "gltf_create_instance_0"); await testHelper.capture(viewer, "gltf_create_instance_0");
var instance = await model.createInstance(); var instance = await model.createInstance();
await instance.addToScene(); await instance.addToScene();
@@ -75,17 +97,36 @@ void main() async {
var model = await viewer.loadGlb( var model = await viewer.loadGlb(
"file://${testHelper.testDir}/assets/cube.glb", "file://${testHelper.testDir}/assets/cube.glb",
numInstances: 2); numInstances: 2);
await testHelper.capture(viewer, "gltf_create_instance_with_material_0"); await testHelper.capture(
viewer, "gltf_create_instance_with_material_0");
final materialInstance = await viewer.createUnlitMaterialInstance(); final materialInstance = await viewer.createUnlitMaterialInstance();
await materialInstance.setParameterFloat4( await materialInstance.setParameterFloat4(
"baseColorFactor", 1.0, 0.0, 0.0, 1.0); "baseColorFactor", 1.0, 0.0, 0.0, 1.0);
var instance = await model.createInstance(materialInstances: [materialInstance]); var instance =
await model.createInstance(materialInstances: [materialInstance]);
await instance.addToScene(); await instance.addToScene();
await viewer.setTransform( await viewer.setTransform(
instance.entity, Matrix4.translation(Vector3.all(1))); instance.entity, Matrix4.translation(Vector3.all(1)));
await testHelper.capture(viewer, "gltf_create_instance_with_material_1"); await testHelper.capture(
viewer, "gltf_create_instance_with_material_1");
await viewer.destroyMaterialInstance(materialInstance);
});
});
test('replace material instance for gltf', () async {
await testHelper.withViewer((viewer) async {
var model = await viewer
.loadGlb("file://${testHelper.testDir}/assets/cube.glb");
await testHelper.capture(viewer, "gltf_default_material_instance");
var materialInstance = await viewer.createUnlitMaterialInstance();
await materialInstance.setParameterFloat4(
"baseColorFactor", 1.0, 1.0, 0.0, 1.0);
await model.setMaterialInstanceAt(materialInstance);
await testHelper.capture(viewer, "gltf_set_material_instance");
await viewer.removeAsset(model);
await viewer.destroyMaterialInstance(materialInstance);
}); });
}); });
}); });