Files
cup_edit/thermion_dart/native/src/scene/Gizmo.cpp
2025-04-02 22:22:36 +08:00

249 lines
8.5 KiB
C++

#include <filament/Engine.h>
#include <filament/RenderableManager.h>
#include <filament/TransformManager.h>
#include <utils/Entity.h>
#include <utils/EntityManager.h>
#include <gltfio/math.h>
#include "Globals.hpp"
#include "Log.hpp"
#include "scene/Gizmo.hpp"
namespace thermion
{
using namespace filament::gltfio;
Gizmo::Gizmo(
SceneAsset *sceneAsset,
Engine *engine,
View *view,
Material *material) noexcept : _source(sceneAsset),
_engine(engine),
_view(view),
_material(material)
{
auto &entityManager = _engine->getEntityManager();
_parent = entityManager.create();
RenderableManager::Builder(1) // 1 primitive
.boundingBox({{-1, -1, -1}, {1, 1, 1}}) // Set a basic bounding box
.culling(false) // Disable culling since this is a UI element
.castShadows(false) // UI elements typically don't cast shadows
.receiveShadows(false)
.build(*_engine, _parent); // Bu
auto &tm = _engine->getTransformManager();
auto parentTransformInstance = tm.getInstance(_parent);
tm.setTransform(parentTransformInstance, math::mat4f());
TRACE("Created Gizmo parent entity %d", _parent);
_entities.push_back(_parent);
createAxisInstance(Axis::X);
createAxisInstance(Axis::Y);
createAxisInstance(Axis::Z);
}
// move constructor so we don't re-create all entities/materials when moving
Gizmo::Gizmo(Gizmo &&other) noexcept : _source(other._source),
_engine(other._engine),
_view(other._view),
_scene(other._scene),
_material(other._material),
_parent(other._parent),
_scale(other._scale),
_entities(std::move(other._entities)),
_materialInstances(std::move(other._materialInstances)),
_hitTest(std::move(other._hitTest)),
_axes(std::move(other._axes))
{
other._source = nullptr;
other._engine = nullptr;
other._view = nullptr;
other._scene = nullptr;
other._material = nullptr;
other._parent = {};
}
void Gizmo::createAxisInstance(Gizmo::Axis axis)
{
auto &rm = _engine->getRenderableManager();
auto *materialInstance = _material->createInstance();
_materialInstances.push_back(materialInstance);
auto instance = _source->createInstance(&materialInstance, 1);
if(!instance) {
Log("FATAL: failed to create asset instance");
}
TRACE("Created Gizmo axis glTF instance with head entity %d", instance->getEntity());
auto color = filament::math::float4(AXIS_COLORS[axis], 0.5f);
materialInstance->setParameter("baseColorFactor", color);
materialInstance->setParameter("scale", _scale);
auto hitTestEntity = instance->findEntityByName("HitTest");
TRACE("Created hit test entity %d for axis %d", hitTestEntity, axis);
_hitTest.push_back(hitTestEntity);
if (hitTestEntity.isNull())
{
TRACE("Hit test entity not found");
}
else
{
auto renderableInstance = rm.getInstance(hitTestEntity);
if (!renderableInstance.isValid())
{
TRACE("Failed to find renderable for hit test entity");
}
else
{
auto *hitTestMaterialInstance = _material->createInstance();
_materialInstances.push_back(hitTestMaterialInstance);
hitTestMaterialInstance->setParameter("baseColorFactor", math::float4{0.0f, 0.0f, 0.0f, 0.0f});
hitTestMaterialInstance->setTransparencyMode(MaterialInstance::TransparencyMode::TWO_PASSES_ONE_SIDE);
hitTestMaterialInstance->setCullingMode(MaterialInstance::CullingMode::BACK);
hitTestMaterialInstance->setParameter("scale", _scale);
rm.setMaterialInstanceAt(renderableInstance, 0, hitTestMaterialInstance);
}
}
auto transform = getRotationForAxis(axis);
TRACE("Created Gizmo axis instance for axis %d", axis);
auto &tm = _engine->getTransformManager();
auto transformInstance = tm.getInstance(instance->getEntity());
tm.setTransform(transformInstance, transform);
// parent this entity's transform to the Gizmo _parent entity
auto parentTransformInstance = tm.getInstance(_parent);
if (parentTransformInstance.isValid())
{
tm.setParent(transformInstance, parentTransformInstance);
}
else
{
TRACE("WARNING: parent transform instance not valid.");
}
_entities.push_back(instance->getEntity());
TRACE("Added entity %d for axis %d", instance->getEntity(), axis);
for (int i = 0; i < instance->getChildEntityCount(); i++)
{
auto entity = instance->getChildEntities()[i];
_entities.push_back(entity);
TRACE("Added entity %d for axis %d", entity, axis);
auto renderable = rm.getInstance(entity);
if (renderable.isValid())
{
rm.setPriority(renderable, 7);
}
}
_axes.push_back(instance);
}
Gizmo::~Gizmo()
{
_scene->removeEntities(_entities.data(), _entities.size());
for (auto entity : _entities)
{
_engine->destroy(entity);
}
for (auto *materialInstance : _materialInstances)
{
_engine->destroy(materialInstance);
}
}
void Gizmo::highlight(Gizmo::Axis axis)
{
auto &rm = _engine->getRenderableManager();
auto instance = _axes[axis];
for (int i = 0; i < instance->getChildEntityCount(); i++)
{
auto childEntity = instance->getChildEntities()[i];
if (childEntity == _hitTest[axis])
{
continue;
}
auto renderableInstance = rm.getInstance(childEntity);
if (renderableInstance.isValid())
{
auto *materialInstance = rm.getMaterialInstanceAt(renderableInstance, 0);
auto color = filament::math::float4(AXIS_COLORS[axis], 1.0f);
materialInstance->setParameter("baseColorFactor", color);
}
}
}
void Gizmo::unhighlight(Gizmo::Axis axis)
{
auto &rm = _engine->getRenderableManager();
auto instance = _axes[axis];
for (int i = 0; i < instance->getChildEntityCount(); i++)
{
auto childEntity = instance->getChildEntities()[i];
if (childEntity == _hitTest[axis])
{
continue;
}
auto renderableInstance = rm.getInstance(childEntity);
if (renderableInstance.isValid())
{
auto *materialInstance = rm.getMaterialInstanceAt(renderableInstance, 0);
auto color = filament::math::float4(AXIS_COLORS[axis], 0.5f);
materialInstance->setParameter("baseColorFactor", color);
}
}
}
void Gizmo::pick(uint32_t x, uint32_t y, GizmoPickCallback callback)
{
auto handler = new Gizmo::PickCallbackHandler(this, callback);
_view->pick(x, y, [=](filament::View::PickingQueryResult const &result)
{
handler->handle(result);
delete handler; });
}
bool Gizmo::isGizmoEntity(Entity e)
{
return std::find(_entities.begin(), _entities.end(), e) != _entities.end();
}
math::mat4f Gizmo::getRotationForAxis(Gizmo::Axis axis)
{
math::mat4f transform;
switch (axis)
{
case Axis::X:
transform = math::mat4f::rotation(math::F_PI_2, math::float3{0, 1, 0});
break;
case Axis::Y:
transform = math::mat4f::rotation(-math::F_PI_2, math::float3{1, 0, 0});
break;
case Axis::Z:
break;
}
return transform;
}
}