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:
521
thermion_dart/native/src/scene/AnimationManager.cpp
Normal file
521
thermion_dart/native/src/scene/AnimationManager.cpp
Normal file
@@ -0,0 +1,521 @@
|
||||
#include <memory>
|
||||
#include <stack>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/TransformManager.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
|
||||
#include <gltfio/Animator.h>
|
||||
|
||||
#include "Log.hpp"
|
||||
|
||||
#include "scene/AnimationManager.hpp"
|
||||
#include "scene/SceneAsset.hpp"
|
||||
#include "scene/GltfSceneAssetInstance.hpp"
|
||||
|
||||
namespace thermion
|
||||
{
|
||||
|
||||
using namespace filament;
|
||||
using namespace utils;
|
||||
|
||||
AnimationManager::AnimationManager(Engine *engine, Scene *scene) : _engine(engine), _scene(scene)
|
||||
{
|
||||
auto &transformManager = _engine->getTransformManager();
|
||||
auto &renderableManager = _engine->getRenderableManager();
|
||||
_animationComponentManager = std::make_unique<AnimationComponentManager>(transformManager, renderableManager);
|
||||
}
|
||||
|
||||
AnimationManager::~AnimationManager()
|
||||
{
|
||||
_animationComponentManager = std::nullptr_t();
|
||||
}
|
||||
|
||||
bool AnimationManager::setMorphAnimationBuffer(
|
||||
utils::Entity entity,
|
||||
const float *const morphData,
|
||||
const uint32_t *const morphIndices,
|
||||
int numMorphTargets,
|
||||
int numFrames,
|
||||
float frameLengthInMs)
|
||||
{
|
||||
std::lock_guard lock(_mutex);
|
||||
|
||||
if (!_animationComponentManager->hasComponent(entity))
|
||||
{
|
||||
_animationComponentManager->addAnimationComponent(entity);
|
||||
}
|
||||
|
||||
MorphAnimation morphAnimation;
|
||||
|
||||
morphAnimation.meshTarget = entity;
|
||||
morphAnimation.frameData.clear();
|
||||
morphAnimation.frameData.insert(
|
||||
morphAnimation.frameData.begin(),
|
||||
morphData,
|
||||
morphData + (numFrames * numMorphTargets));
|
||||
morphAnimation.frameLengthInMs = frameLengthInMs;
|
||||
morphAnimation.morphIndices.resize(numMorphTargets);
|
||||
for (int i = 0; i < numMorphTargets; i++)
|
||||
{
|
||||
morphAnimation.morphIndices[i] = morphIndices[i];
|
||||
}
|
||||
morphAnimation.durationInSecs = (frameLengthInMs * numFrames) / 1000.0f;
|
||||
|
||||
morphAnimation.start = high_resolution_clock::now();
|
||||
morphAnimation.lengthInFrames = static_cast<int>(
|
||||
morphAnimation.durationInSecs * 1000.0f /
|
||||
frameLengthInMs);
|
||||
|
||||
auto animationComponentInstance = _animationComponentManager->getInstance(entity);
|
||||
auto &animationComponent = _animationComponentManager->elementAt<0>(animationComponentInstance);
|
||||
auto &morphAnimations = animationComponent.morphAnimations;
|
||||
|
||||
morphAnimations.emplace_back(morphAnimation);
|
||||
return true;
|
||||
}
|
||||
|
||||
void AnimationManager::clearMorphAnimationBuffer(
|
||||
utils::Entity entity)
|
||||
{
|
||||
std::lock_guard lock(_mutex);
|
||||
|
||||
auto animationComponentInstance = _animationComponentManager->getInstance(entity);
|
||||
auto &animationComponent = _animationComponentManager->elementAt<0>(animationComponentInstance);
|
||||
auto &morphAnimations = animationComponent.morphAnimations;
|
||||
morphAnimations.clear();
|
||||
}
|
||||
|
||||
void AnimationManager::resetToRestPose(GltfSceneAssetInstance *instance)
|
||||
{
|
||||
std::lock_guard lock(_mutex);
|
||||
|
||||
auto filamentInstance = instance->getInstance();
|
||||
auto skinCount = filamentInstance->getSkinCount();
|
||||
|
||||
TransformManager &transformManager = _engine->getTransformManager();
|
||||
|
||||
//
|
||||
// To reset the skeleton to its rest pose, we could just call animator->resetBoneMatrices(),
|
||||
// which sets all bone matrices to the identity matrix. However, any subsequent calls to animator->updateBoneMatrices()
|
||||
// may result in unexpected poses, because that method uses each bone's transform to calculate
|
||||
// the bone matrices (and resetBoneMatrices does not affect this transform).
|
||||
// To "fully" reset the bone, we need to set its local transform (i.e. relative to its parent)
|
||||
// to its original orientation in rest pose.
|
||||
//
|
||||
// This can be calculated as:
|
||||
//
|
||||
// auto rest = inverse(parentTransformInModelSpace) * bindMatrix
|
||||
//
|
||||
// (where bindMatrix is the inverse of the inverseBindMatrix).
|
||||
//
|
||||
// The only requirement is that parent bone transforms are reset before child bone transforms.
|
||||
// glTF/Filament does not guarantee that parent bones are listed before child bones under a FilamentInstance.
|
||||
// We ensure that parents are reset before children by:
|
||||
// - pushing all bones onto a stack
|
||||
// - iterate over the stack
|
||||
// - look at the bone at the top of the stack
|
||||
// - if the bone already been reset, pop and continue iterating over the stack
|
||||
// - otherwise
|
||||
// - if the bone has a parent that has not been reset, push the parent to the top of the stack and continue iterating
|
||||
// - otherwise
|
||||
// - pop the bone, reset its transform and mark it as completed
|
||||
for (int skinIndex = 0; skinIndex < skinCount; skinIndex++)
|
||||
{
|
||||
std::unordered_set<Entity, Entity::Hasher> joints;
|
||||
std::unordered_set<Entity, Entity::Hasher> completed;
|
||||
std::stack<Entity> stack;
|
||||
|
||||
auto transforms = getBoneRestTranforms(instance, skinIndex);
|
||||
|
||||
for (int i = 0; i < filamentInstance->getJointCountAt(skinIndex); i++)
|
||||
{
|
||||
auto restTransform = transforms[i];
|
||||
const auto &joint = filamentInstance->getJointsAt(skinIndex)[i];
|
||||
auto transformInstance = transformManager.getInstance(joint);
|
||||
transformManager.setTransform(transformInstance, restTransform);
|
||||
}
|
||||
}
|
||||
filamentInstance->getAnimator()->updateBoneMatrices();
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<math::mat4f> AnimationManager::getBoneRestTranforms(GltfSceneAssetInstance *instance, int skinIndex)
|
||||
{
|
||||
|
||||
std::vector<math::mat4f> transforms;
|
||||
|
||||
auto filamentInstance = instance->getInstance();
|
||||
auto skinCount = filamentInstance->getSkinCount();
|
||||
|
||||
TransformManager &transformManager = _engine->getTransformManager();
|
||||
|
||||
transforms.resize(filamentInstance->getJointCountAt(skinIndex));
|
||||
|
||||
//
|
||||
// To reset the skeleton to its rest pose, we could just call animator->resetBoneMatrices(),
|
||||
// which sets all bone matrices to the identity matrix. However, any subsequent calls to animator->updateBoneMatrices()
|
||||
// may result in unexpected poses, because that method uses each bone's transform to calculate
|
||||
// the bone matrices (and resetBoneMatrices does not affect this transform).
|
||||
// To "fully" reset the bone, we need to set its local transform (i.e. relative to its parent)
|
||||
// to its original orientation in rest pose.
|
||||
//
|
||||
// This can be calculated as:
|
||||
//
|
||||
// auto rest = inverse(parentTransformInModelSpace) * bindMatrix
|
||||
//
|
||||
// (where bindMatrix is the inverse of the inverseBindMatrix).
|
||||
//
|
||||
// The only requirement is that parent bone transforms are reset before child bone transforms.
|
||||
// glTF/Filament does not guarantee that parent bones are listed before child bones under a FilamentInstance.
|
||||
// We ensure that parents are reset before children by:
|
||||
// - pushing all bones onto a stack
|
||||
// - iterate over the stack
|
||||
// - look at the bone at the top of the stack
|
||||
// - if the bone already been reset, pop and continue iterating over the stack
|
||||
// - otherwise
|
||||
// - if the bone has a parent that has not been reset, push the parent to the top of the stack and continue iterating
|
||||
// - otherwise
|
||||
// - pop the bone, reset its transform and mark it as completed
|
||||
std::vector<Entity> joints;
|
||||
std::unordered_set<Entity, Entity::Hasher> completed;
|
||||
std::stack<Entity> stack;
|
||||
|
||||
for (int i = 0; i < filamentInstance->getJointCountAt(skinIndex); i++)
|
||||
{
|
||||
const auto &joint = filamentInstance->getJointsAt(skinIndex)[i];
|
||||
joints.push_back(joint);
|
||||
stack.push(joint);
|
||||
}
|
||||
|
||||
while (!stack.empty())
|
||||
{
|
||||
const auto &joint = stack.top();
|
||||
|
||||
// if we've already handled this node previously (e.g. when we encountered it as a parent), then skip
|
||||
if (completed.find(joint) != completed.end())
|
||||
{
|
||||
stack.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto transformInstance = transformManager.getInstance(joint);
|
||||
auto parent = transformManager.getParent(transformInstance);
|
||||
|
||||
// we need to handle parent joints before handling their children
|
||||
// therefore, if this joint has a parent that hasn't been handled yet,
|
||||
// push the parent to the top of the stack and start the loop again
|
||||
const auto &jointIter = std::find(joints.begin(), joints.end(), joint);
|
||||
auto parentIter = std::find(joints.begin(), joints.end(), parent);
|
||||
|
||||
if (parentIter != joints.end() && completed.find(parent) == completed.end())
|
||||
{
|
||||
stack.push(parent);
|
||||
continue;
|
||||
}
|
||||
|
||||
// otherwise let's get the inverse bind matrix for the joint
|
||||
math::mat4f inverseBindMatrix;
|
||||
bool found = false;
|
||||
for (int i = 0; i < filamentInstance->getJointCountAt(skinIndex); i++)
|
||||
{
|
||||
if (filamentInstance->getJointsAt(skinIndex)[i] == joint)
|
||||
{
|
||||
inverseBindMatrix = filamentInstance->getInverseBindMatricesAt(skinIndex)[i];
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT_PRECONDITION(found, "Failed to find inverse bind matrix for joint %d", joint);
|
||||
|
||||
// now we need to ascend back up the hierarchy to calculate the modelSpaceTransform
|
||||
math::mat4f modelSpaceTransform;
|
||||
while (parentIter != joints.end())
|
||||
{
|
||||
const auto transformInstance = transformManager.getInstance(parent);
|
||||
const auto parentIndex = distance(joints.begin(), parentIter);
|
||||
const auto transform = transforms[parentIndex];
|
||||
modelSpaceTransform = transform * modelSpaceTransform;
|
||||
parent = transformManager.getParent(transformInstance);
|
||||
parentIter = std::find(joints.begin(), joints.end(), parent);
|
||||
}
|
||||
|
||||
const auto bindMatrix = inverse(inverseBindMatrix);
|
||||
|
||||
const auto inverseModelSpaceTransform = inverse(modelSpaceTransform);
|
||||
|
||||
const auto jointIndex = distance(joints.begin(), jointIter);
|
||||
transforms[jointIndex] = inverseModelSpaceTransform * bindMatrix;
|
||||
completed.insert(joint);
|
||||
stack.pop();
|
||||
}
|
||||
return transforms;
|
||||
}
|
||||
|
||||
void AnimationManager::updateBoneMatrices(GltfSceneAssetInstance *instance)
|
||||
{
|
||||
instance->getInstance()->getAnimator()->updateBoneMatrices();
|
||||
}
|
||||
|
||||
bool AnimationManager::addBoneAnimation(GltfSceneAssetInstance *instance,
|
||||
int skinIndex,
|
||||
int boneIndex,
|
||||
const float *const frameData,
|
||||
int numFrames,
|
||||
float frameLengthInMs,
|
||||
float fadeOutInSecs,
|
||||
float fadeInInSecs,
|
||||
float maxDelta)
|
||||
{
|
||||
std::lock_guard lock(_mutex);
|
||||
|
||||
BoneAnimation animation;
|
||||
animation.boneIndex = boneIndex;
|
||||
animation.frameData.clear();
|
||||
|
||||
const auto &inverseBindMatrix = instance->getInstance()->getInverseBindMatricesAt(skinIndex)[boneIndex];
|
||||
for (int i = 0; i < numFrames; i++)
|
||||
{
|
||||
math::mat4f frame(
|
||||
frameData[i * 16],
|
||||
frameData[(i * 16) + 1],
|
||||
frameData[(i * 16) + 2],
|
||||
frameData[(i * 16) + 3],
|
||||
frameData[(i * 16) + 4],
|
||||
frameData[(i * 16) + 5],
|
||||
frameData[(i * 16) + 6],
|
||||
frameData[(i * 16) + 7],
|
||||
frameData[(i * 16) + 8],
|
||||
frameData[(i * 16) + 9],
|
||||
frameData[(i * 16) + 10],
|
||||
frameData[(i * 16) + 11],
|
||||
frameData[(i * 16) + 12],
|
||||
frameData[(i * 16) + 13],
|
||||
frameData[(i * 16) + 14],
|
||||
frameData[(i * 16) + 15]);
|
||||
|
||||
animation.frameData.push_back(frame);
|
||||
}
|
||||
|
||||
animation.frameLengthInMs = frameLengthInMs;
|
||||
animation.start = std::chrono::high_resolution_clock::now();
|
||||
animation.reverse = false;
|
||||
animation.durationInSecs = (frameLengthInMs * numFrames) / 1000.0f;
|
||||
animation.lengthInFrames = numFrames;
|
||||
animation.frameLengthInMs = frameLengthInMs;
|
||||
animation.fadeOutInSecs = fadeOutInSecs;
|
||||
animation.fadeInInSecs = fadeInInSecs;
|
||||
animation.maxDelta = maxDelta;
|
||||
animation.skinIndex = skinIndex;
|
||||
if (!_animationComponentManager->hasComponent(instance->getInstance()->getRoot()))
|
||||
{
|
||||
Log("ERROR: specified entity is not animatable (has no animation component attached).");
|
||||
return false;
|
||||
}
|
||||
auto animationComponentInstance = _animationComponentManager->getInstance(instance->getInstance()->getRoot());
|
||||
|
||||
auto &animationComponent = _animationComponentManager->elementAt<0>(animationComponentInstance);
|
||||
auto &boneAnimations = animationComponent.boneAnimations;
|
||||
|
||||
boneAnimations.emplace_back(animation);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AnimationManager::playGltfAnimation(GltfSceneAssetInstance *instance, int index, bool loop, bool reverse, bool replaceActive, float crossfade, float startOffset)
|
||||
{
|
||||
std::lock_guard lock(_mutex);
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
Log("ERROR: glTF animation index must be greater than zero.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_animationComponentManager->hasComponent(instance->getEntity()))
|
||||
{
|
||||
Log("ERROR: specified entity is not animatable (has no animation component attached).");
|
||||
return;
|
||||
}
|
||||
|
||||
auto animationComponentInstance = _animationComponentManager->getInstance(instance->getEntity());
|
||||
|
||||
auto &animationComponent = _animationComponentManager->elementAt<0>(animationComponentInstance);
|
||||
|
||||
if (replaceActive)
|
||||
{
|
||||
if (animationComponent.gltfAnimations.size() > 0)
|
||||
{
|
||||
auto &last = animationComponent.gltfAnimations.back();
|
||||
animationComponent.fadeGltfAnimationIndex = last.index;
|
||||
animationComponent.fadeDuration = crossfade;
|
||||
auto now = high_resolution_clock::now();
|
||||
auto elapsedInSecs = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - last.start).count()) / 1000.0f;
|
||||
animationComponent.fadeOutAnimationStart = elapsedInSecs;
|
||||
animationComponent.gltfAnimations.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
animationComponent.fadeGltfAnimationIndex = -1;
|
||||
animationComponent.fadeDuration = 0.0f;
|
||||
}
|
||||
}
|
||||
else if (crossfade > 0)
|
||||
{
|
||||
Log("ERROR: crossfade only supported when replaceActive is true.");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
animationComponent.fadeGltfAnimationIndex = -1;
|
||||
animationComponent.fadeDuration = 0.0f;
|
||||
}
|
||||
|
||||
GltfAnimation animation;
|
||||
animation.startOffset = startOffset;
|
||||
animation.index = index;
|
||||
animation.start = std::chrono::high_resolution_clock::now();
|
||||
animation.loop = loop;
|
||||
animation.reverse = reverse;
|
||||
animation.durationInSecs = instance->getInstance()->getAnimator()->getAnimationDuration(index);
|
||||
|
||||
bool found = false;
|
||||
|
||||
// don't play the animation if it's already running
|
||||
for (int i = 0; i < animationComponent.gltfAnimations.size(); i++)
|
||||
{
|
||||
if (animationComponent.gltfAnimations[i].index == index)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
animationComponent.gltfAnimations.push_back(animation);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationManager::stopGltfAnimation(GltfSceneAssetInstance *instance, int index)
|
||||
{
|
||||
|
||||
auto animationComponentInstance = _animationComponentManager->getInstance(instance->getEntity());
|
||||
auto &animationComponent = _animationComponentManager->elementAt<0>(animationComponentInstance);
|
||||
|
||||
auto erased = std::remove_if(animationComponent.gltfAnimations.begin(),
|
||||
animationComponent.gltfAnimations.end(),
|
||||
[=](GltfAnimation &anim)
|
||||
{ return anim.index == index; });
|
||||
animationComponent.gltfAnimations.erase(erased,
|
||||
animationComponent.gltfAnimations.end());
|
||||
return;
|
||||
}
|
||||
|
||||
void AnimationManager::setMorphTargetWeights(utils::Entity entity, const float *const weights, const int count)
|
||||
{
|
||||
RenderableManager &rm = _engine->getRenderableManager();
|
||||
auto renderableInstance = rm.getInstance(entity);
|
||||
rm.setMorphWeights(
|
||||
renderableInstance,
|
||||
weights,
|
||||
count);
|
||||
}
|
||||
|
||||
void AnimationManager::setGltfAnimationFrame(GltfSceneAssetInstance *instance, int animationIndex, int animationFrame)
|
||||
{
|
||||
auto offset = 60 * animationFrame * 1000; // TODO - don't hardcore 60fps framerate
|
||||
instance->getInstance()->getAnimator()->applyAnimation(animationIndex, offset);
|
||||
instance->getInstance()->getAnimator()->updateBoneMatrices();
|
||||
return;
|
||||
}
|
||||
|
||||
float AnimationManager::getGltfAnimationDuration(GltfSceneAssetInstance *instance, int animationIndex)
|
||||
{
|
||||
return instance->getInstance()->getAnimator()->getAnimationDuration(animationIndex);
|
||||
}
|
||||
|
||||
std::vector<std::string> AnimationManager::getGltfAnimationNames(GltfSceneAssetInstance *instance)
|
||||
{
|
||||
std::vector<std::string> names;
|
||||
|
||||
size_t count = instance->getInstance()->getAnimator()->getAnimationCount();
|
||||
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
names.push_back(instance->getInstance()->getAnimator()->getAnimationName(i));
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
std::vector<std::string> AnimationManager::getMorphTargetNames(GltfSceneAsset *asset, EntityId childEntity)
|
||||
{
|
||||
std::vector<std::string> names;
|
||||
|
||||
auto filamentAsset = asset->getAsset();
|
||||
|
||||
const utils::Entity targetEntity = utils::Entity::import(childEntity);
|
||||
|
||||
size_t count = filamentAsset->getMorphTargetCountAt(targetEntity);
|
||||
for (int j = 0; j < count; j++)
|
||||
{
|
||||
const char *morphName = filamentAsset->getMorphTargetNameAt(targetEntity, j);
|
||||
names.push_back(morphName);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
vector<Entity> AnimationManager::getBoneEntities(GltfSceneAssetInstance *instance, int skinIndex)
|
||||
{
|
||||
auto *joints = instance->getInstance()->getJointsAt(skinIndex);
|
||||
auto jointCount = instance->getInstance()->getJointCountAt(skinIndex);
|
||||
std::vector<Entity> boneEntities(joints, joints + jointCount);
|
||||
return boneEntities;
|
||||
}
|
||||
|
||||
void AnimationManager::update()
|
||||
{
|
||||
std::lock_guard lock(_mutex);
|
||||
_animationComponentManager->update();
|
||||
}
|
||||
|
||||
math::mat4f AnimationManager::getInverseBindMatrix(GltfSceneAssetInstance *instance, int skinIndex, int boneIndex)
|
||||
{
|
||||
return instance->getInstance()->getInverseBindMatricesAt(skinIndex)[boneIndex];
|
||||
}
|
||||
|
||||
bool AnimationManager::setBoneTransform(GltfSceneAssetInstance *instance, int32_t skinIndex, int boneIndex, math::mat4f transform)
|
||||
{
|
||||
std::lock_guard lock(_mutex);
|
||||
|
||||
RenderableManager &rm = _engine->getRenderableManager();
|
||||
|
||||
const auto &renderableInstance = rm.getInstance(instance->getEntity());
|
||||
|
||||
if (!renderableInstance.isValid())
|
||||
{
|
||||
Log("Specified entity is not a renderable. You probably provided the ultimate parent entity of a glTF asset, which is non-renderable. ");
|
||||
return false;
|
||||
}
|
||||
|
||||
rm.setBones(
|
||||
renderableInstance,
|
||||
&transform,
|
||||
1,
|
||||
boneIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AnimationManager::addAnimationComponent(EntityId entity)
|
||||
{
|
||||
_animationComponentManager->addAnimationComponent(utils::Entity::import(entity));
|
||||
return true;
|
||||
}
|
||||
|
||||
void AnimationManager::removeAnimationComponent(EntityId entity)
|
||||
{
|
||||
_animationComponentManager->removeComponent(utils::Entity::import(entity));
|
||||
}
|
||||
|
||||
}
|
||||
240
thermion_dart/native/src/scene/CustomGeometry.cpp
Normal file
240
thermion_dart/native/src/scene/CustomGeometry.cpp
Normal file
@@ -0,0 +1,240 @@
|
||||
#include "math.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/Frustum.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/Texture.h>
|
||||
#include <filament/TransformManager.h>
|
||||
#include <filament/Viewport.h>
|
||||
#include <filament/geometry/SurfaceOrientation.h>
|
||||
|
||||
#include "scene/CustomGeometry.hpp"
|
||||
#include "Log.hpp"
|
||||
|
||||
namespace thermion
|
||||
{
|
||||
|
||||
using namespace filament;
|
||||
|
||||
CustomGeometry::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)
|
||||
: numVertices(numVertices), numIndices(numIndices), _materialInstance(materialInstance), _engine(engine)
|
||||
{
|
||||
|
||||
this->primitiveType = primitiveType;
|
||||
this->vertices = new float[numVertices];
|
||||
|
||||
std::memcpy(this->vertices, vertices, numVertices * sizeof(float));
|
||||
|
||||
if (numNormals > 0)
|
||||
{
|
||||
this->normals = new float[numNormals];
|
||||
std::memcpy(this->normals, normals, numNormals * sizeof(float));
|
||||
}
|
||||
|
||||
if (numUvs > 0)
|
||||
{
|
||||
this->uvs = new float[numUvs];
|
||||
std::memcpy(this->uvs, uvs, numUvs * sizeof(float));
|
||||
}
|
||||
else
|
||||
{
|
||||
this->uvs = nullptr;
|
||||
}
|
||||
|
||||
this->indices = new uint16_t[numIndices];
|
||||
|
||||
std::memcpy(this->indices, indices, numIndices * sizeof(uint16_t));
|
||||
|
||||
computeBoundingBox();
|
||||
|
||||
indexBuffer = IndexBuffer::Builder()
|
||||
.indexCount(numIndices)
|
||||
.bufferType(IndexBuffer::IndexType::USHORT)
|
||||
.build(*_engine);
|
||||
|
||||
indexBuffer->setBuffer(*_engine,
|
||||
IndexBuffer::BufferDescriptor(
|
||||
this->indices,
|
||||
indexBuffer->getIndexCount() * sizeof(uint16_t),
|
||||
[](void *buf, size_t,
|
||||
void *data)
|
||||
{
|
||||
free((void *)buf);
|
||||
}));
|
||||
|
||||
std::vector<filament::math::float2> *uvData;
|
||||
if (this->uvs != nullptr)
|
||||
{
|
||||
uvData = new std::vector<filament::math::float2>(
|
||||
(filament::math::float2 *)this->uvs,
|
||||
(filament::math::float2 *)(this->uvs + numVertices * 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
uvData = new std::vector<filament::math::float2>(
|
||||
numVertices, filament::math::float2{0.0f, 0.0f});
|
||||
}
|
||||
|
||||
// Create dummy vertex color data (white color for all vertices)
|
||||
auto dummyColors = new std::vector<filament::math::float4>(
|
||||
numVertices, filament::math::float4{1.0f, 1.0f, 1.0f, 1.0f});
|
||||
|
||||
auto vertexBufferBuilder =
|
||||
VertexBuffer::Builder()
|
||||
.vertexCount(numVertices)
|
||||
.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 (this->normals)
|
||||
{
|
||||
vertexBufferBuilder.bufferCount(5).attribute(
|
||||
VertexAttribute::TANGENTS, 4,
|
||||
filament::VertexBuffer::AttributeType::FLOAT4);
|
||||
}
|
||||
else
|
||||
{
|
||||
vertexBufferBuilder = vertexBufferBuilder.bufferCount(4);
|
||||
}
|
||||
|
||||
vertexBuffer = vertexBufferBuilder.build(*_engine);
|
||||
|
||||
vertexBuffer->setBufferAt(
|
||||
*_engine, 0,
|
||||
VertexBuffer::BufferDescriptor(
|
||||
this->vertices, numVertices * sizeof(math::float3),
|
||||
[](void *buf, size_t, void *data)
|
||||
{
|
||||
free((void *)buf);
|
||||
}));
|
||||
|
||||
// Set UV0 buffer
|
||||
vertexBuffer->setBufferAt(
|
||||
*_engine, 1,
|
||||
VertexBuffer::BufferDescriptor(
|
||||
uvData->data(), uvData->size() * sizeof(math::float2),
|
||||
[](void *buf, size_t, void *data)
|
||||
{
|
||||
delete static_cast<std::vector<math::float2> *>(data);
|
||||
},
|
||||
uvData));
|
||||
|
||||
// Set UV1 buffer (reusing UV0 data)
|
||||
vertexBuffer->setBufferAt(*_engine, 2,
|
||||
VertexBuffer::BufferDescriptor(
|
||||
uvData->data(),
|
||||
uvData->size() * sizeof(math::float2),
|
||||
[](void *buf, size_t, void *data)
|
||||
{
|
||||
// Do nothing here, as we're reusing the same
|
||||
// data as UV0
|
||||
},
|
||||
nullptr));
|
||||
|
||||
// Set vertex color buffer
|
||||
vertexBuffer->setBufferAt(
|
||||
*_engine, 3,
|
||||
VertexBuffer::BufferDescriptor(
|
||||
dummyColors->data(), dummyColors->size() * sizeof(math::float4),
|
||||
[](void *buf, size_t, void *data)
|
||||
{
|
||||
delete static_cast<std::vector<math::float4> *>(data);
|
||||
},
|
||||
dummyColors));
|
||||
|
||||
if (this->normals)
|
||||
{
|
||||
|
||||
assert(this->primitiveType == RenderableManager::PrimitiveType::TRIANGLES);
|
||||
std::vector<filament::math::ushort3> triangles;
|
||||
for (int i = 0; i < numIndices; i += 3)
|
||||
{
|
||||
filament::math::ushort3 triangle;
|
||||
triangle.x = this->indices[i];
|
||||
triangle.y = this->indices[i + 1];
|
||||
triangle.z = this->indices[i + 2];
|
||||
triangles.push_back(triangle);
|
||||
}
|
||||
|
||||
// Create a SurfaceOrientation builder
|
||||
geometry::SurfaceOrientation::Builder builder;
|
||||
builder.vertexCount(numVertices)
|
||||
.normals((filament::math::float3 *)normals)
|
||||
.positions((filament::math::float3 *)this->vertices)
|
||||
.triangleCount(triangles.size())
|
||||
.triangles(triangles.data());
|
||||
|
||||
// Build the SurfaceOrientation object
|
||||
auto orientation = builder.build();
|
||||
|
||||
// Retrieve the quaternions
|
||||
auto quats = new std::vector<filament::math::quatf>(numVertices);
|
||||
orientation->getQuats(quats->data(), numVertices);
|
||||
|
||||
vertexBuffer->setBufferAt(*_engine, 4,
|
||||
VertexBuffer::BufferDescriptor(
|
||||
quats->data(),
|
||||
quats->size() * sizeof(math::quatf),
|
||||
[](void *buf, size_t, void *data)
|
||||
{
|
||||
delete (std::vector<math::quatf> *)data;
|
||||
},
|
||||
(void *)quats));
|
||||
}
|
||||
}
|
||||
|
||||
utils::Entity CustomGeometry::createInstance(MaterialInstance *materialInstance) {
|
||||
auto entity = utils::EntityManager::get().create();
|
||||
RenderableManager::Builder builder(1);
|
||||
|
||||
builder.boundingBox(boundingBox)
|
||||
.geometry(0, primitiveType, vertexBuffer, indexBuffer, 0, numIndices)
|
||||
.culling(true)
|
||||
.receiveShadows(true)
|
||||
.castShadows(true);
|
||||
|
||||
builder.material(0, materialInstance);
|
||||
builder.build(*_engine, entity);
|
||||
|
||||
return entity;
|
||||
|
||||
}
|
||||
|
||||
CustomGeometry::~CustomGeometry()
|
||||
{
|
||||
_engine->destroy(vertexBuffer);
|
||||
_engine->destroy(indexBuffer);
|
||||
}
|
||||
|
||||
void CustomGeometry::computeBoundingBox()
|
||||
{
|
||||
float minX = FLT_MAX, minY = FLT_MAX, minZ = FLT_MAX;
|
||||
float maxX = -FLT_MAX, maxY = -FLT_MAX, maxZ = -FLT_MAX;
|
||||
|
||||
for (uint32_t i = 0; i < numVertices; i += 3)
|
||||
{
|
||||
minX = std::min(vertices[i], minX);
|
||||
minY = std::min(vertices[i + 1], minY);
|
||||
minZ = std::min(vertices[i + 2], minZ);
|
||||
maxX = std::max(vertices[i], maxX);
|
||||
maxY = std::max(vertices[i + 1], maxY);
|
||||
maxZ = std::max(vertices[i + 2], maxZ);
|
||||
}
|
||||
|
||||
boundingBox = Box{{minX, minY, minZ}, {maxX, maxY, maxZ}};
|
||||
}
|
||||
|
||||
} // namespace thermion
|
||||
125
thermion_dart/native/src/scene/GeometrySceneAsset.cpp
Normal file
125
thermion_dart/native/src/scene/GeometrySceneAsset.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
#include <vector>
|
||||
|
||||
#include <gltfio/MaterialProvider.h>
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/Frustum.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/Texture.h>
|
||||
#include <filament/TransformManager.h>
|
||||
#include <filament/Viewport.h>
|
||||
#include <filament/geometry/SurfaceOrientation.h>
|
||||
|
||||
#include "Log.hpp"
|
||||
#include "scene/GeometrySceneAsset.hpp"
|
||||
#include "scene/GeometrySceneAssetBuilder.hpp"
|
||||
|
||||
namespace thermion
|
||||
{
|
||||
|
||||
using namespace filament;
|
||||
|
||||
GeometrySceneAsset::GeometrySceneAsset(
|
||||
bool isInstance,
|
||||
Engine *engine,
|
||||
VertexBuffer *vertexBuffer,
|
||||
IndexBuffer *indexBuffer,
|
||||
MaterialInstance **materialInstances,
|
||||
size_t materialInstanceCount,
|
||||
RenderableManager::PrimitiveType primitiveType,
|
||||
Box boundingBox)
|
||||
: _isInstance(isInstance),
|
||||
_engine(engine), _vertexBuffer(vertexBuffer), _indexBuffer(indexBuffer), _materialInstances(materialInstances), _materialInstanceCount(materialInstanceCount), _primitiveType(primitiveType), _boundingBox(boundingBox)
|
||||
{
|
||||
_entity = utils::EntityManager::get().create();
|
||||
|
||||
RenderableManager::Builder builder(1);
|
||||
builder.boundingBox(_boundingBox)
|
||||
.geometry(0, _primitiveType, _vertexBuffer, _indexBuffer)
|
||||
.culling(true)
|
||||
.receiveShadows(true)
|
||||
.castShadows(true);
|
||||
for (int i = 0; i < materialInstanceCount; i++)
|
||||
{
|
||||
builder.material(i, materialInstances[i]);
|
||||
}
|
||||
builder.build(*_engine, _entity);
|
||||
}
|
||||
|
||||
|
||||
|
||||
GeometrySceneAsset::~GeometrySceneAsset()
|
||||
{
|
||||
if (_engine)
|
||||
{
|
||||
if (_vertexBuffer && !_isInstance)
|
||||
_engine->destroy(_vertexBuffer);
|
||||
if (_indexBuffer && !_isInstance)
|
||||
_engine->destroy(_indexBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
SceneAsset *GeometrySceneAsset::createInstance(MaterialInstance **materialInstances, size_t materialInstanceCount)
|
||||
{
|
||||
if (_isInstance)
|
||||
{
|
||||
Log("Cannot create an instance from another instance. Ensure you are calling createInstance with the original asset.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<GeometrySceneAsset> instance = std::make_unique<GeometrySceneAsset>(
|
||||
true,
|
||||
_engine,
|
||||
_vertexBuffer,
|
||||
_indexBuffer,
|
||||
materialInstances,
|
||||
materialInstanceCount,
|
||||
_primitiveType,
|
||||
_boundingBox);
|
||||
auto *raw = instance.get();
|
||||
_instances.push_back(std::move(instance));
|
||||
return raw;
|
||||
}
|
||||
|
||||
// std::unique_ptr<GeometrySceneAsset> GeometrySceneAsset::create(
|
||||
// float *vertices, uint32_t numVertices,
|
||||
// float *normals, uint32_t numNormals,
|
||||
// float *uvs, uint32_t numUvs,
|
||||
// uint16_t *indices, uint32_t numIndices,
|
||||
// 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
|
||||
367
thermion_dart/native/src/scene/Gizmo.cpp
Normal file
367
thermion_dart/native/src/scene/Gizmo.cpp
Normal file
@@ -0,0 +1,367 @@
|
||||
#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 "scene/Gizmo.hpp"
|
||||
#include "scene/SceneManager.hpp"
|
||||
|
||||
#include "material/unlit_fixed_size.h"
|
||||
|
||||
#include "Log.hpp"
|
||||
|
||||
namespace thermion
|
||||
{
|
||||
|
||||
using namespace filament::gltfio;
|
||||
|
||||
// First, create the black cube at the center
|
||||
// The axes widgets will be parented to this entity
|
||||
Entity Gizmo::createParentEntity()
|
||||
{
|
||||
auto &transformManager = _engine->getTransformManager();
|
||||
auto &entityManager = _engine->getEntityManager();
|
||||
|
||||
auto parent = entityManager.create();
|
||||
|
||||
auto *parentMaterialInstance = _material->createInstance();
|
||||
parentMaterialInstance->setParameter("baseColorFactor", math::float4{0.0f, 1.0f, 1.0f, 1.0f});
|
||||
parentMaterialInstance->setParameter("scale", 4.0f);
|
||||
|
||||
_materialInstances.push_back(parentMaterialInstance);
|
||||
|
||||
// Create center cube vertices
|
||||
float centerCubeSize = 0.1f;
|
||||
float *centerCubeVertices = new float[8 * 3]{
|
||||
-centerCubeSize, -centerCubeSize, -centerCubeSize,
|
||||
centerCubeSize, -centerCubeSize, -centerCubeSize,
|
||||
centerCubeSize, centerCubeSize, -centerCubeSize,
|
||||
-centerCubeSize, centerCubeSize, -centerCubeSize,
|
||||
-centerCubeSize, -centerCubeSize, centerCubeSize,
|
||||
centerCubeSize, -centerCubeSize, centerCubeSize,
|
||||
centerCubeSize, centerCubeSize, centerCubeSize,
|
||||
-centerCubeSize, centerCubeSize, centerCubeSize};
|
||||
|
||||
// Create center cube indices
|
||||
uint16_t *centerCubeIndices = new uint16_t[36]{
|
||||
0, 1, 2, 2, 3, 0,
|
||||
1, 5, 6, 6, 2, 1,
|
||||
5, 4, 7, 7, 6, 5,
|
||||
4, 0, 3, 3, 7, 4,
|
||||
3, 2, 6, 6, 7, 3,
|
||||
4, 5, 1, 1, 0, 4};
|
||||
|
||||
auto centerCubeVb = VertexBuffer::Builder()
|
||||
.vertexCount(8)
|
||||
.bufferCount(1)
|
||||
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3)
|
||||
.build(*_engine);
|
||||
|
||||
centerCubeVb->setBufferAt(*_engine, 0, VertexBuffer::BufferDescriptor(centerCubeVertices, 8 * sizeof(filament::math::float3), [](void *buffer, size_t size, void *)
|
||||
{ delete[] static_cast<float *>(buffer); }));
|
||||
|
||||
auto centerCubeIb = IndexBuffer::Builder().indexCount(36).bufferType(IndexBuffer::IndexType::USHORT).build(*_engine);
|
||||
centerCubeIb->setBuffer(*_engine, IndexBuffer::BufferDescriptor(
|
||||
centerCubeIndices, 36 * sizeof(uint16_t),
|
||||
[](void *buffer, size_t size, void *)
|
||||
{ delete[] static_cast<uint16_t *>(buffer); }));
|
||||
|
||||
RenderableManager::Builder(1)
|
||||
.boundingBox({{-centerCubeSize, -centerCubeSize, -centerCubeSize},
|
||||
{centerCubeSize, centerCubeSize, centerCubeSize}})
|
||||
.material(0, parentMaterialInstance)
|
||||
.layerMask(0xFF, 1u << SceneManager::LAYERS::OVERLAY)
|
||||
.priority(7)
|
||||
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, centerCubeVb, centerCubeIb, 0, 36)
|
||||
.culling(false)
|
||||
.build(*_engine, parent);
|
||||
|
||||
auto parentTransformInstance = transformManager.getInstance(parent);
|
||||
math::mat4f cubeTransform;
|
||||
transformManager.setTransform(parentTransformInstance, cubeTransform);
|
||||
return parent;
|
||||
}
|
||||
|
||||
Gizmo::Gizmo(Engine *engine, View *view, Scene *scene, Material *material) : _engine(engine), _view(view), _scene(scene), _material(material)
|
||||
{
|
||||
auto parent = createParentEntity();
|
||||
auto x = createAxisEntity(Gizmo::Axis::X, parent);
|
||||
auto y = createAxisEntity(Gizmo::Axis::Y, parent);
|
||||
auto z = createAxisEntity(Gizmo::Axis::Z, parent);
|
||||
|
||||
auto xHitTest = createHitTestEntity(Gizmo::Axis::X, parent);
|
||||
auto yHitTest = createHitTestEntity(Gizmo::Axis::Y, parent);
|
||||
auto zHitTest = createHitTestEntity(Gizmo::Axis::Z, parent);
|
||||
|
||||
_entities = std::vector{parent, x, y, z, xHitTest, yHitTest, zHitTest};
|
||||
_parent = parent;
|
||||
_x = x;
|
||||
_y = y;
|
||||
_z = z;
|
||||
_xHitTest = xHitTest;
|
||||
_yHitTest = yHitTest;
|
||||
_zHitTest = zHitTest;
|
||||
}
|
||||
|
||||
Entity Gizmo::createAxisEntity(Gizmo::Axis axis, Entity parent)
|
||||
{
|
||||
auto &entityManager = _engine->getEntityManager();
|
||||
auto &transformManager = _engine->getTransformManager();
|
||||
auto *materialInstance = _material->createInstance();
|
||||
_materialInstances.push_back(materialInstance);
|
||||
auto entity = entityManager.create();
|
||||
|
||||
auto baseColor = inactiveColors[axis];
|
||||
|
||||
// Line and arrow vertices
|
||||
float lineLength = 0.6f;
|
||||
float lineWidth = 0.008f;
|
||||
float arrowLength = 0.06f;
|
||||
float arrowWidth = 0.02f;
|
||||
float *vertices = new float[13 * 3]{
|
||||
// Line vertices (8 vertices)
|
||||
-lineWidth, -lineWidth, 0.0f,
|
||||
lineWidth, -lineWidth, 0.0f,
|
||||
lineWidth, lineWidth, 0.0f,
|
||||
-lineWidth, lineWidth, 0.0f,
|
||||
-lineWidth, -lineWidth, lineLength,
|
||||
lineWidth, -lineWidth, lineLength,
|
||||
lineWidth, lineWidth, lineLength,
|
||||
-lineWidth, lineWidth, lineLength,
|
||||
// Arrow vertices (5 vertices)
|
||||
0.0f, 0.0f, lineLength + arrowLength, // Tip of the arrow
|
||||
-arrowWidth, -arrowWidth, lineLength, // Base of the arrow
|
||||
arrowWidth, -arrowWidth, lineLength,
|
||||
arrowWidth, arrowWidth, lineLength,
|
||||
-arrowWidth, arrowWidth, lineLength};
|
||||
|
||||
// Line and arrow indices
|
||||
uint16_t *indices = new uint16_t[24 + 18]{
|
||||
// Line indices (24 indices)
|
||||
0, 1, 5, 5, 4, 0,
|
||||
1, 2, 6, 6, 5, 1,
|
||||
2, 3, 7, 7, 6, 2,
|
||||
3, 0, 4, 4, 7, 3,
|
||||
// // Arrow indices (18 indices)
|
||||
8, 9, 10, // Front face
|
||||
8, 10, 11, // Right face
|
||||
8, 11, 12, // Back face
|
||||
8, 12, 9, // Left face
|
||||
9, 12, 11, 11, 10, 9 // Base of the arrow
|
||||
};
|
||||
|
||||
auto vb = VertexBuffer::Builder()
|
||||
.vertexCount(13)
|
||||
.bufferCount(1)
|
||||
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3)
|
||||
.build(*_engine);
|
||||
|
||||
vb->setBufferAt(*_engine, 0, VertexBuffer::BufferDescriptor(vertices, 13 * sizeof(filament::math::float3), [](void *buffer, size_t size, void *)
|
||||
{ delete[] static_cast<float *>(buffer); }));
|
||||
|
||||
auto ib = IndexBuffer::Builder().indexCount(42).bufferType(IndexBuffer::IndexType::USHORT).build(*_engine);
|
||||
ib->setBuffer(*_engine, IndexBuffer::BufferDescriptor(
|
||||
indices, 42 * sizeof(uint16_t),
|
||||
[](void *buffer, size_t size, void *)
|
||||
{ delete[] static_cast<uint16_t *>(buffer); }));
|
||||
|
||||
materialInstance->setParameter("baseColorFactor", baseColor);
|
||||
materialInstance->setParameter("scale", 4.0f);
|
||||
materialInstance->setDepthCulling(false);
|
||||
materialInstance->setDepthFunc(MaterialInstance::DepthFunc::A);
|
||||
|
||||
RenderableManager::Builder(1)
|
||||
.boundingBox({{-arrowWidth, -arrowWidth, 0},
|
||||
{arrowWidth, arrowWidth, lineLength + arrowLength}})
|
||||
.material(0, materialInstance)
|
||||
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb, ib, 0, 42)
|
||||
.priority(6)
|
||||
.layerMask(0xFF, 1u << SceneManager::LAYERS::OVERLAY)
|
||||
.culling(false)
|
||||
.receiveShadows(false)
|
||||
.castShadows(false)
|
||||
.build(*_engine, entity);
|
||||
|
||||
auto transformInstance = transformManager.getInstance(entity);
|
||||
|
||||
transformManager.setTransform(transformInstance, getRotationForAxis(axis));
|
||||
|
||||
// parent the axis to the center cube
|
||||
auto parentTransformInstance = transformManager.getInstance(parent);
|
||||
transformManager.setParent(transformInstance, parentTransformInstance);
|
||||
return entity;
|
||||
}
|
||||
|
||||
Gizmo::~Gizmo()
|
||||
{
|
||||
_scene->removeEntities(_entities.data(), _entities.size());
|
||||
|
||||
for (auto entity : _entities)
|
||||
{
|
||||
_engine->destroy(entity);
|
||||
}
|
||||
|
||||
for (auto *materialInstance : _materialInstances)
|
||||
{
|
||||
_engine->destroy(materialInstance);
|
||||
}
|
||||
}
|
||||
|
||||
Entity Gizmo::createHitTestEntity(Gizmo::Axis axis, Entity parent)
|
||||
{
|
||||
auto &entityManager = EntityManager::get();
|
||||
auto &transformManager = _engine->getTransformManager();
|
||||
|
||||
auto parentTransformInstance = transformManager.getInstance(parent);
|
||||
|
||||
float volumeWidth = 0.2f;
|
||||
float volumeLength = 1.2f;
|
||||
float volumeDepth = 0.2f;
|
||||
|
||||
float *volumeVertices = new float[8 * 3]{
|
||||
-volumeWidth / 2, -volumeDepth / 2, 0,
|
||||
volumeWidth / 2, -volumeDepth / 2, 0,
|
||||
volumeWidth / 2, -volumeDepth / 2, volumeLength,
|
||||
-volumeWidth / 2, -volumeDepth / 2, volumeLength,
|
||||
-volumeWidth / 2, volumeDepth / 2, 0,
|
||||
volumeWidth / 2, volumeDepth / 2, 0,
|
||||
volumeWidth / 2, volumeDepth / 2, volumeLength,
|
||||
-volumeWidth / 2, volumeDepth / 2, volumeLength};
|
||||
|
||||
uint16_t *volumeIndices = new uint16_t[36]{
|
||||
0, 1, 2, 2, 3, 0, // Bottom face
|
||||
4, 5, 6, 6, 7, 4, // Top face
|
||||
0, 4, 7, 7, 3, 0, // Left face
|
||||
1, 5, 6, 6, 2, 1, // Right face
|
||||
0, 1, 5, 5, 4, 0, // Front face
|
||||
3, 2, 6, 6, 7, 3 // Back face
|
||||
};
|
||||
|
||||
auto volumeVb = VertexBuffer::Builder()
|
||||
.vertexCount(8)
|
||||
.bufferCount(1)
|
||||
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3)
|
||||
.build(*_engine);
|
||||
|
||||
volumeVb->setBufferAt(*_engine, 0, VertexBuffer::BufferDescriptor(volumeVertices, 8 * sizeof(filament::math::float3), [](void *buffer, size_t size, void *)
|
||||
{ delete[] static_cast<float *>(buffer); }));
|
||||
|
||||
auto volumeIb = IndexBuffer::Builder()
|
||||
.indexCount(36)
|
||||
.bufferType(IndexBuffer::IndexType::USHORT)
|
||||
.build(*_engine);
|
||||
|
||||
volumeIb->setBuffer(*_engine, IndexBuffer::BufferDescriptor(
|
||||
volumeIndices, 36 * sizeof(uint16_t),
|
||||
[](void *buffer, size_t size, void *)
|
||||
{ delete[] static_cast<uint16_t *>(buffer); }));
|
||||
|
||||
auto entity = entityManager.create();
|
||||
auto *materialInstance = _material->createInstance();
|
||||
_materialInstances.push_back(materialInstance);
|
||||
materialInstance->setParameter("baseColorFactor", math::float4{0.0f, 0.0f, 0.0f, 0.0f});
|
||||
materialInstance->setParameter("scale", 4.0f);
|
||||
|
||||
RenderableManager::Builder(1)
|
||||
.boundingBox({{-volumeWidth / 2, -volumeDepth / 2, 0}, {volumeWidth / 2, volumeDepth / 2, volumeLength}})
|
||||
.material(0, materialInstance)
|
||||
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, volumeVb, volumeIb, 0, 36)
|
||||
.priority(7)
|
||||
.layerMask(0xFF, 1u << SceneManager::LAYERS::OVERLAY)
|
||||
.culling(false)
|
||||
.receiveShadows(false)
|
||||
.castShadows(false)
|
||||
.build(*_engine, entity);
|
||||
|
||||
auto transformInstance = transformManager.getInstance(entity);
|
||||
transformManager.setTransform(transformInstance, getRotationForAxis(axis));
|
||||
// Parent the picking volume to the center cube
|
||||
transformManager.setParent(transformInstance, parentTransformInstance);
|
||||
return entity;
|
||||
}
|
||||
|
||||
void Gizmo::highlight(Gizmo::Axis axis)
|
||||
{
|
||||
auto &rm = _engine->getRenderableManager();
|
||||
auto entity = getEntityForAxis(axis);
|
||||
if (entity.isNull())
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto renderableInstance = rm.getInstance(entity);
|
||||
|
||||
if (!renderableInstance.isValid())
|
||||
{
|
||||
Log("Invalid renderable for axis");
|
||||
return;
|
||||
}
|
||||
auto *materialInstance = rm.getMaterialInstanceAt(renderableInstance, 0);
|
||||
math::float4 baseColor = activeColors[axis];
|
||||
materialInstance->setParameter("baseColorFactor", baseColor);
|
||||
}
|
||||
|
||||
void Gizmo::unhighlight(Gizmo::Axis axis)
|
||||
{
|
||||
auto &rm = _engine->getRenderableManager();
|
||||
auto entity = getEntityForAxis(axis);
|
||||
if (entity.isNull())
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto renderableInstance = rm.getInstance(entity);
|
||||
if (!renderableInstance.isValid())
|
||||
{
|
||||
Log("Invalid renderable for axis");
|
||||
return;
|
||||
}
|
||||
auto *materialInstance = rm.getMaterialInstanceAt(renderableInstance, 0);
|
||||
math::float4 baseColor = inactiveColors[axis];
|
||||
materialInstance->setParameter("baseColorFactor", baseColor);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
if (e == _entities[i])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
61
thermion_dart/native/src/scene/GltfSceneAsset.cpp
Normal file
61
thermion_dart/native/src/scene/GltfSceneAsset.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
|
||||
#include "scene/GltfSceneAsset.hpp"
|
||||
#include "scene/GltfSceneAssetInstance.hpp"
|
||||
#include "gltfio/FilamentInstance.h"
|
||||
#include "Log.hpp"
|
||||
namespace thermion
|
||||
{
|
||||
|
||||
GltfSceneAsset::~GltfSceneAsset()
|
||||
{
|
||||
_instances.clear();
|
||||
_asset->releaseSourceData();
|
||||
_assetLoader->destroyAsset(_asset);
|
||||
}
|
||||
|
||||
SceneAsset *GltfSceneAsset::createInstance(MaterialInstance **materialInstances, size_t materialInstanceCount)
|
||||
{
|
||||
auto instanceNumber = _instances.size();
|
||||
|
||||
if (instanceNumber > _asset->getAssetInstanceCount() - 1)
|
||||
{
|
||||
Log("No instances available for reuse. When loading the asset, you must pre-allocate the number of instances you wish to make available for use. Try increasing this number.");
|
||||
return std::nullptr_t();
|
||||
}
|
||||
Log("Creating instance %d", instanceNumber);
|
||||
auto instance = _asset->getAssetInstances()[instanceNumber];
|
||||
instance->recomputeBoundingBoxes();
|
||||
instance->getAnimator()->updateBoneMatrices();
|
||||
|
||||
auto& rm = _engine->getRenderableManager();
|
||||
|
||||
if(materialInstanceCount > 0) {
|
||||
for(int i = 0; i < instance->getEntityCount(); i++) {
|
||||
auto renderableInstance = rm.getInstance(instance->getEntities()[i]);
|
||||
if(!renderableInstance.isValid()) {
|
||||
Log("Instance is not renderable");
|
||||
} else {
|
||||
for(int i = 0; i < materialInstanceCount; i++) {
|
||||
rm.setMaterialInstanceAt(renderableInstance, i, materialInstances[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<GltfSceneAssetInstance> sceneAssetInstance = std::make_unique<GltfSceneAssetInstance>(
|
||||
instance,
|
||||
_engine,
|
||||
materialInstances,
|
||||
materialInstanceCount
|
||||
);
|
||||
|
||||
auto *raw = sceneAssetInstance.get();
|
||||
|
||||
_instances.push_back(std::move(sceneAssetInstance));
|
||||
return raw;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
14
thermion_dart/native/src/scene/GltfSceneAssetInstance.cpp
Normal file
14
thermion_dart/native/src/scene/GltfSceneAssetInstance.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
#include "scene/GltfSceneAssetInstance.hpp"
|
||||
#include "gltfio/FilamentInstance.h"
|
||||
#include "Log.hpp"
|
||||
namespace thermion
|
||||
{
|
||||
|
||||
GltfSceneAssetInstance::~GltfSceneAssetInstance()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
349
thermion_dart/native/src/scene/RotationGizmo.cpp
Normal file
349
thermion_dart/native/src/scene/RotationGizmo.cpp
Normal file
@@ -0,0 +1,349 @@
|
||||
// #include <filament/Engine.h>
|
||||
// #include <filament/RenderableManager.h>
|
||||
// #include <filament/TransformManager.h>
|
||||
|
||||
// #include <gltfio/math.h>
|
||||
|
||||
// #include <utils/Entity.h>
|
||||
// #include <utils/EntityManager.h>
|
||||
|
||||
// #include <math.h>
|
||||
|
||||
// #include "scene/SceneManager.hpp"
|
||||
|
||||
// namespace thermion {
|
||||
|
||||
// using namespace filament::gltfio;
|
||||
|
||||
// RotationGizmo::RotationGizmo(Engine* engine, View* view, Scene* scene, Material* material)
|
||||
// : _engine(engine), _view(view), _scene(scene), _material(material) {
|
||||
|
||||
// auto& entityManager = EntityManager::get();
|
||||
// auto& transformManager = _engine->getTransformManager();
|
||||
|
||||
// // Create center cube
|
||||
// auto parentEntity = entityManager.create();
|
||||
// auto* parentMaterialInstance = _material->createInstance();
|
||||
// parentMaterialInstance->setParameter("baseColorFactor", math::float4{0.0f, 0.0f, 0.0f, 1.0f});
|
||||
// parentMaterialInstance->setParameter("scale", 4.0f);
|
||||
|
||||
// _entities[0] = parentEntity;
|
||||
// _materialInstances[0] = parentMaterialInstance;
|
||||
|
||||
// // Create center cube geometry
|
||||
// float centerCubeSize = 0.01f;
|
||||
// float* centerCubeVertices = new float[8 * 3]{
|
||||
// -centerCubeSize, -centerCubeSize, -centerCubeSize,
|
||||
// centerCubeSize, -centerCubeSize, -centerCubeSize,
|
||||
// centerCubeSize, centerCubeSize, -centerCubeSize,
|
||||
// -centerCubeSize, centerCubeSize, -centerCubeSize,
|
||||
// -centerCubeSize, -centerCubeSize, centerCubeSize,
|
||||
// centerCubeSize, -centerCubeSize, centerCubeSize,
|
||||
// centerCubeSize, centerCubeSize, centerCubeSize,
|
||||
// -centerCubeSize, centerCubeSize, centerCubeSize
|
||||
// };
|
||||
|
||||
// uint16_t* centerCubeIndices = new uint16_t[36]{
|
||||
// 0, 1, 2, 2, 3, 0,
|
||||
// 1, 5, 6, 6, 2, 1,
|
||||
// 5, 4, 7, 7, 6, 5,
|
||||
// 4, 0, 3, 3, 7, 4,
|
||||
// 3, 2, 6, 6, 7, 3,
|
||||
// 4, 5, 1, 1, 0, 4
|
||||
// };
|
||||
|
||||
// auto centerCubeVb = VertexBuffer::Builder()
|
||||
// .vertexCount(8)
|
||||
// .bufferCount(1)
|
||||
// .attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3)
|
||||
// .build(*engine);
|
||||
|
||||
// centerCubeVb->setBufferAt(*engine, 0,
|
||||
// VertexBuffer::BufferDescriptor(centerCubeVertices, 8 * sizeof(filament::math::float3),
|
||||
// [](void* buffer, size_t size, void*) { delete[] static_cast<float*>(buffer); }));
|
||||
|
||||
// auto centerCubeIb = IndexBuffer::Builder()
|
||||
// .indexCount(36)
|
||||
// .bufferType(IndexBuffer::IndexType::USHORT)
|
||||
// .build(*engine);
|
||||
|
||||
// centerCubeIb->setBuffer(*engine,
|
||||
// IndexBuffer::BufferDescriptor(centerCubeIndices, 36 * sizeof(uint16_t),
|
||||
// [](void* buffer, size_t size, void*) { delete[] static_cast<uint16_t*>(buffer); }));
|
||||
|
||||
// RenderableManager::Builder(1)
|
||||
// .boundingBox({{-centerCubeSize, -centerCubeSize, -centerCubeSize},
|
||||
// {centerCubeSize, centerCubeSize, centerCubeSize}})
|
||||
// .material(0, parentMaterialInstance)
|
||||
// .layerMask(0xFF, 1u << SceneManager::LAYERS::OVERLAY)
|
||||
// .priority(7)
|
||||
// .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, centerCubeVb, centerCubeIb, 0, 36)
|
||||
// .culling(false)
|
||||
// .build(*engine, parentEntity);
|
||||
|
||||
// // Create rotation circles
|
||||
// constexpr int segments = 32;
|
||||
// float radius = 0.5f;
|
||||
// float* vertices;
|
||||
// uint16_t* indices;
|
||||
// int vertexCount, indexCount;
|
||||
|
||||
// createCircle(radius, segments, vertices, indices, vertexCount, indexCount);
|
||||
|
||||
// auto vb = VertexBuffer::Builder()
|
||||
// .vertexCount(vertexCount)
|
||||
// .bufferCount(1)
|
||||
// .attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3)
|
||||
// .build(*engine);
|
||||
|
||||
// vb->setBufferAt(*engine, 0,
|
||||
// VertexBuffer::BufferDescriptor(vertices, vertexCount * sizeof(filament::math::float3),
|
||||
// [](void* buffer, size_t size, void*) { delete[] static_cast<float*>(buffer); }));
|
||||
|
||||
// auto ib = IndexBuffer::Builder()
|
||||
// .indexCount(indexCount)
|
||||
// .bufferType(IndexBuffer::IndexType::USHORT)
|
||||
// .build(*engine);
|
||||
|
||||
// ib->setBuffer(*engine,
|
||||
// IndexBuffer::BufferDescriptor(indices, indexCount * sizeof(uint16_t),
|
||||
// [](void* buffer, size_t size, void*) { delete[] static_cast<uint16_t*>(buffer); }));
|
||||
|
||||
// // Create the three circular rotation handles
|
||||
// for (int i = 0; i < 3; i++) {
|
||||
// auto* materialInstance = _material->createInstance();
|
||||
// auto entity = entityManager.create();
|
||||
// _entities[i + 1] = entity;
|
||||
// _materialInstances[i + 1] = materialInstance;
|
||||
|
||||
// auto baseColor = inactiveColors[i];
|
||||
// math::mat4f transform;
|
||||
|
||||
// switch (i) {
|
||||
// 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;
|
||||
// }
|
||||
|
||||
// materialInstance->setParameter("baseColorFactor", baseColor);
|
||||
// materialInstance->setParameter("scale", 4.0f);
|
||||
|
||||
// RenderableManager::Builder(1)
|
||||
// .boundingBox({{-radius, -radius, -0.01f}, {radius, radius, 0.01f}})
|
||||
// .material(0, materialInstance)
|
||||
// .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb, ib, 0, indexCount)
|
||||
// .priority(6)
|
||||
// .layerMask(0xFF, 1u << SceneManager::LAYERS::OVERLAY)
|
||||
// .culling(false)
|
||||
// .receiveShadows(false)
|
||||
// .castShadows(false)
|
||||
// .build(*engine, entity);
|
||||
|
||||
// auto transformInstance = transformManager.getInstance(entity);
|
||||
// transformManager.setTransform(transformInstance, transform);
|
||||
// transformManager.setParent(transformInstance, transformManager.getInstance(parentEntity));
|
||||
// }
|
||||
|
||||
// createHitTestEntities();
|
||||
// setVisibility(true);
|
||||
// }
|
||||
|
||||
// void RotationGizmo::createCircle(float radius, int segments, float*& vertices, uint16_t*& indices, int& vertexCount, int& indexCount) {
|
||||
// vertexCount = segments * 2;
|
||||
// indexCount = segments * 6;
|
||||
|
||||
// vertices = new float[vertexCount * 3];
|
||||
// indices = new uint16_t[indexCount];
|
||||
|
||||
// float thickness = 0.01f;
|
||||
|
||||
// // Generate vertices for inner and outer circles
|
||||
// for (int i = 0; i < segments; i++) {
|
||||
// float angle = (2.0f * M_PI * i) / segments;
|
||||
// float x = cosf(angle);
|
||||
// float y = sinf(angle);
|
||||
|
||||
// // Inner circle vertex
|
||||
// vertices[i * 6] = x * (radius - thickness);
|
||||
// vertices[i * 6 + 1] = y * (radius - thickness);
|
||||
// vertices[i * 6 + 2] = 0.0f;
|
||||
|
||||
// // Outer circle vertex
|
||||
// vertices[i * 6 + 3] = x * (radius + thickness);
|
||||
// vertices[i * 6 + 4] = y * (radius + thickness);
|
||||
// vertices[i * 6 + 5] = 0.0f;
|
||||
// }
|
||||
|
||||
// // Generate indices for triangles
|
||||
// for (int i = 0; i < segments; i++) {
|
||||
// int next = (i + 1) % segments;
|
||||
|
||||
// // First triangle
|
||||
// indices[i * 6] = i * 2;
|
||||
// indices[i * 6 + 1] = i * 2 + 1;
|
||||
// indices[i * 6 + 2] = next * 2 + 1;
|
||||
|
||||
// // Second triangle
|
||||
// indices[i * 6 + 3] = i * 2;
|
||||
// indices[i * 6 + 4] = next * 2 + 1;
|
||||
// indices[i * 6 + 5] = next * 2;
|
||||
// }
|
||||
// }
|
||||
|
||||
// void RotationGizmo::createHitTestEntities() {
|
||||
// auto& entityManager = EntityManager::get();
|
||||
// auto& transformManager = _engine->getTransformManager();
|
||||
|
||||
// float radius = 0.5f;
|
||||
// float thickness = 0.1f;
|
||||
|
||||
// // Create hit test volumes for each rotation circle
|
||||
// for (int i = 4; i < 7; i++) {
|
||||
// _entities[i] = entityManager.create();
|
||||
// _materialInstances[i] = _material->createInstance();
|
||||
|
||||
// _materialInstances[i]->setParameter("baseColorFactor", math::float4{0.0f, 0.0f, 0.0f, 0.0f});
|
||||
// _materialInstances[i]->setParameter("scale", 4.0f);
|
||||
|
||||
// math::mat4f transform;
|
||||
// switch (i - 4) {
|
||||
// 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;
|
||||
// }
|
||||
|
||||
// // Create a thicker invisible volume aroun
|
||||
|
||||
// // Create a thicker invisible volume around each rotation circle for hit testing
|
||||
// float* volumeVertices;
|
||||
// uint16_t* volumeIndices;
|
||||
// int volumeVertexCount, volumeIndexCount;
|
||||
// createCircle(radius, 32, volumeVertices, volumeIndices, volumeVertexCount, volumeIndexCount);
|
||||
|
||||
// auto volumeVb = VertexBuffer::Builder()
|
||||
// .vertexCount(volumeVertexCount)
|
||||
// .bufferCount(1)
|
||||
// .attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3)
|
||||
// .build(*_engine);
|
||||
|
||||
// volumeVb->setBufferAt(*_engine, 0,
|
||||
// VertexBuffer::BufferDescriptor(volumeVertices, volumeVertexCount * sizeof(filament::math::float3),
|
||||
// [](void* buffer, size_t size, void*) { delete[] static_cast<float*>(buffer); }));
|
||||
|
||||
// auto volumeIb = IndexBuffer::Builder()
|
||||
// .indexCount(volumeIndexCount)
|
||||
// .bufferType(IndexBuffer::IndexType::USHORT)
|
||||
// .build(*_engine);
|
||||
|
||||
// volumeIb->setBuffer(*_engine,
|
||||
// IndexBuffer::BufferDescriptor(volumeIndices, volumeIndexCount * sizeof(uint16_t),
|
||||
// [](void* buffer, size_t size, void*) { delete[] static_cast<uint16_t*>(buffer); }));
|
||||
|
||||
// RenderableManager::Builder(1)
|
||||
// .boundingBox({{-radius, -radius, -thickness/2}, {radius, radius, thickness/2}})
|
||||
// .material(0, _materialInstances[i])
|
||||
// .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, volumeVb, volumeIb, 0, volumeIndexCount)
|
||||
// .priority(7)
|
||||
// .layerMask(0xFF, 1u << SceneManager::LAYERS::OVERLAY)
|
||||
// .culling(false)
|
||||
// .receiveShadows(false)
|
||||
// .castShadows(false)
|
||||
// .build(*_engine, _entities[i]);
|
||||
|
||||
// auto instance = transformManager.getInstance(_entities[i]);
|
||||
// transformManager.setTransform(instance, transform);
|
||||
// transformManager.setParent(instance, transformManager.getInstance(_entities[0]));
|
||||
// }
|
||||
// }
|
||||
|
||||
// RotationGizmo::~RotationGizmo() {
|
||||
// _scene->removeEntities(_entities, 7);
|
||||
|
||||
// for (int i = 0; i < 7; i++) {
|
||||
// _engine->destroy(_entities[i]);
|
||||
// _engine->destroy(_materialInstances[i]);
|
||||
// }
|
||||
// }
|
||||
|
||||
// void RotationGizmo::highlight(Entity entity) {
|
||||
// auto& rm = _engine->getRenderableManager();
|
||||
// auto renderableInstance = rm.getInstance(entity);
|
||||
// auto materialInstance = rm.getMaterialInstanceAt(renderableInstance, 0);
|
||||
|
||||
// math::float4 baseColor;
|
||||
// if (entity == x()) {
|
||||
// baseColor = activeColors[Axis::X];
|
||||
// } else if (entity == y()) {
|
||||
// baseColor = activeColors[Axis::Y];
|
||||
// } else if (entity == z()) {
|
||||
// baseColor = activeColors[Axis::Z];
|
||||
// } else {
|
||||
// baseColor = math::float4{1.0f, 1.0f, 1.0f, 1.0f};
|
||||
// }
|
||||
|
||||
// materialInstance->setParameter("baseColorFactor", baseColor);
|
||||
// }
|
||||
|
||||
// void RotationGizmo::unhighlight() {
|
||||
// auto& rm = _engine->getRenderableManager();
|
||||
|
||||
// for (int i = 0; i < 3; i++) {
|
||||
// auto renderableInstance = rm.getInstance(_entities[i + 1]);
|
||||
// auto materialInstance = rm.getMaterialInstanceAt(renderableInstance, 0);
|
||||
// materialInstance->setParameter("baseColorFactor", inactiveColors[i]);
|
||||
// }
|
||||
// }
|
||||
|
||||
// void RotationGizmo::pick(uint32_t x, uint32_t y, PickCallback callback) {
|
||||
// auto handler = new RotationGizmo::PickCallbackHandler(this, callback);
|
||||
// _view->pick(x, y, [=](filament::View::PickingQueryResult const& result) {
|
||||
// handler->handle(result);
|
||||
// delete handler;
|
||||
// });
|
||||
// }
|
||||
|
||||
// void RotationGizmo::PickCallbackHandler::handle(filament::View::PickingQueryResult const& result) {
|
||||
// auto x = static_cast<int32_t>(result.fragCoords.x);
|
||||
// auto y = static_cast<int32_t>(result.fragCoords.y);
|
||||
|
||||
// for (int i = 0; i < 7; i++) {
|
||||
// if (_gizmo->_entities[i] == result.renderable) {
|
||||
// if (i < 4) {
|
||||
// return;
|
||||
// }
|
||||
// _gizmo->highlight(_gizmo->_entities[i - 4]);
|
||||
// _callback(static_cast<Axis>(i - 4), x, y, _gizmo->_view);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// _gizmo->unhighlight();
|
||||
// }
|
||||
|
||||
// bool RotationGizmo::isGizmoEntity(Entity e) {
|
||||
// for (int i = 0; i < 7; i++) {
|
||||
// if (e == _entities[i]) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// void RotationGizmo::setVisibility(bool visible) {
|
||||
// if (visible) {
|
||||
// _scene->addEntities(_entities, 7);
|
||||
// } else {
|
||||
// _scene->removeEntities(_entities, 7);
|
||||
// }
|
||||
// }
|
||||
|
||||
// }
|
||||
946
thermion_dart/native/src/scene/SceneManager.cpp
Normal file
946
thermion_dart/native/src/scene/SceneManager.cpp
Normal file
@@ -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