925 lines
27 KiB
C++
925 lines
27 KiB
C++
#include "animation_scene.h"
|
|
|
|
#include "animation/animation.h"
|
|
#include "animation/controller.h"
|
|
#include "animation/events.h"
|
|
#include "animation/property_animation.h"
|
|
#include "engine/associative_array.h"
|
|
#include "engine/crc32.h"
|
|
#include "engine/engine.h"
|
|
#include "engine/atomic.h"
|
|
#include "engine/job_system.h"
|
|
#include "engine/log.h"
|
|
#include "engine/profiler.h"
|
|
#include "engine/reflection.h"
|
|
#include "engine/resource_manager.h"
|
|
#include "engine/stream.h"
|
|
#include "engine/universe.h"
|
|
#include "nodes.h"
|
|
#include "renderer/model.h"
|
|
#include "renderer/pose.h"
|
|
#include "renderer/render_scene.h"
|
|
|
|
|
|
namespace Lumix
|
|
{
|
|
|
|
struct Animation;
|
|
struct Engine;
|
|
struct Universe;
|
|
|
|
enum class AnimationSceneVersion
|
|
{
|
|
LATEST
|
|
};
|
|
|
|
|
|
static const ComponentType MODEL_INSTANCE_TYPE = reflection::getComponentType("model_instance");
|
|
static const ComponentType ANIMABLE_TYPE = reflection::getComponentType("animable");
|
|
static const ComponentType PROPERTY_ANIMATOR_TYPE = reflection::getComponentType("property_animator");
|
|
static const ComponentType ANIMATOR_TYPE = reflection::getComponentType("animator");
|
|
|
|
|
|
struct AnimationSceneImpl final : AnimationScene
|
|
{
|
|
friend struct AnimationSystemImpl;
|
|
|
|
struct Animator
|
|
{
|
|
EntityRef entity;
|
|
anim::Controller* resource = nullptr;
|
|
u32 default_set = 0;
|
|
anim::RuntimeContext* ctx = nullptr;
|
|
LocalRigidTransform root_motion = {{0, 0, 0}, {0, 0, 0, 1}};
|
|
|
|
struct IK {
|
|
float weight = 0;
|
|
Vec3 target;
|
|
} inverse_kinematics[4];
|
|
};
|
|
|
|
|
|
struct PropertyAnimator
|
|
{
|
|
struct Key
|
|
{
|
|
int frame0;
|
|
int frame1;
|
|
float value0;
|
|
float value1;
|
|
};
|
|
|
|
enum Flags
|
|
{
|
|
LOOPED = 1 << 0,
|
|
DISABLED = 1 << 1
|
|
};
|
|
|
|
PropertyAnimator(IAllocator& allocator) : keys(allocator) {}
|
|
|
|
PropertyAnimation* animation;
|
|
Array<Key> keys;
|
|
|
|
FlagSet<Flags, u32> flags;
|
|
float time;
|
|
};
|
|
|
|
|
|
AnimationSceneImpl(Engine& engine, IPlugin& anim_system, Universe& universe, IAllocator& allocator)
|
|
: m_universe(universe)
|
|
, m_engine(engine)
|
|
, m_anim_system(anim_system)
|
|
, m_animables(allocator)
|
|
, m_property_animators(allocator)
|
|
, m_animators(allocator)
|
|
, m_allocator(allocator)
|
|
, m_animator_map(allocator)
|
|
{
|
|
m_is_game_running = false;
|
|
}
|
|
|
|
void init() override {
|
|
m_render_scene = static_cast<RenderScene*>(m_universe.getScene(crc32("renderer")));
|
|
ASSERT(m_render_scene);
|
|
}
|
|
|
|
|
|
int getVersion() const override { return (int)AnimationSceneVersion::LATEST; }
|
|
|
|
|
|
void clear() override
|
|
{
|
|
for (PropertyAnimator& anim : m_property_animators)
|
|
{
|
|
unloadResource(anim.animation);
|
|
}
|
|
m_property_animators.clear();
|
|
|
|
for (Animable& animable : m_animables)
|
|
{
|
|
unloadResource(animable.animation);
|
|
}
|
|
m_animables.clear();
|
|
|
|
for (Animator& animator : m_animators)
|
|
{
|
|
unloadResource(animator.resource);
|
|
setSource(animator, nullptr);
|
|
}
|
|
m_animators.clear();
|
|
}
|
|
|
|
|
|
const OutputMemoryStream* getEventStream(EntityRef entity) const override {
|
|
auto iter = m_animator_map.find(entity);
|
|
const Animator& animator = m_animators[iter.value()];
|
|
return animator.ctx ? &animator.ctx->events : nullptr;
|
|
}
|
|
|
|
|
|
void setAnimatorIK(EntityRef entity, u32 index, float weight, const Vec3& target) override {
|
|
auto iter = m_animator_map.find(entity);
|
|
Animator& animator = m_animators[iter.value()];
|
|
Animator::IK& ik = animator.inverse_kinematics[index];
|
|
ik.weight = clamp(weight, 0.f, 1.f);
|
|
ik.target = target;
|
|
}
|
|
|
|
|
|
int getAnimatorInputIndex(EntityRef entity, const char* name) const override
|
|
{
|
|
const Animator& animator = m_animators[m_animator_map[entity]];
|
|
anim::InputDecl& decl = animator.resource->m_inputs;
|
|
for (u32 i = 0; i < lengthOf(decl.inputs); ++i) {
|
|
if (decl.inputs[i].type != anim::InputDecl::EMPTY && equalStrings(decl.inputs[i].name, name)) return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
void setAnimatorFloatInput(EntityRef entity, u32 input_idx, float value)
|
|
{
|
|
auto iter = m_animator_map.find(entity);
|
|
if (!iter.isValid()) return;
|
|
|
|
Animator& animator = m_animators[iter.value()];
|
|
const anim::InputDecl& decl = animator.resource->m_inputs;
|
|
if (input_idx >= decl.inputs_count) return;
|
|
if (!animator.ctx) return;
|
|
|
|
if (decl.inputs[input_idx].type == anim::InputDecl::FLOAT) {
|
|
memcpy(&animator.ctx->inputs[decl.inputs[input_idx].offset], &value, sizeof(value));
|
|
}
|
|
else {
|
|
logWarning("Trying to set float to ", decl.inputs[input_idx].name);
|
|
}
|
|
}
|
|
|
|
|
|
void setAnimatorU32Input(EntityRef entity, u32 input_idx, u32 value)
|
|
{
|
|
auto iter = m_animator_map.find(entity);
|
|
if (!iter.isValid()) return;
|
|
|
|
Animator& animator = m_animators[iter.value()];
|
|
const anim::InputDecl& decl = animator.resource->m_inputs;
|
|
if (input_idx >= decl.inputs_count) return;
|
|
if (!animator.ctx) return;
|
|
|
|
if (decl.inputs[input_idx].type == anim::InputDecl::U32) {
|
|
*(u32*)&animator.ctx->inputs[decl.inputs[input_idx].offset] = value;
|
|
}
|
|
else {
|
|
logWarning("Trying to set int to ", decl.inputs[input_idx].name);
|
|
}
|
|
}
|
|
|
|
|
|
void setAnimatorBoolInput(EntityRef entity, u32 input_idx, bool value)
|
|
{
|
|
auto iter = m_animator_map.find(entity);
|
|
if (!iter.isValid()) return;
|
|
|
|
Animator& animator = m_animators[iter.value()];
|
|
const anim::InputDecl& decl = animator.resource->m_inputs;
|
|
if (input_idx >= decl.inputs_count) return;
|
|
if (!animator.ctx) return;
|
|
|
|
if (decl.inputs[input_idx].type == anim::InputDecl::BOOL) {
|
|
*(bool*)&animator.ctx->inputs[decl.inputs[input_idx].offset] = value;
|
|
}
|
|
else {
|
|
logWarning("Trying to set bool to ", decl.inputs[input_idx].name);
|
|
}
|
|
}
|
|
|
|
|
|
float getAnimationLength(int animation_idx) override
|
|
{
|
|
auto* animation = static_cast<Animation*>(animation_idx > 0 ? m_engine.getLuaResource(animation_idx) : nullptr);
|
|
if (animation) return animation->getLength().seconds();
|
|
return 0;
|
|
}
|
|
|
|
|
|
Animable& getAnimable(EntityRef entity) override
|
|
{
|
|
return m_animables[entity];
|
|
}
|
|
|
|
|
|
Animation* getAnimableAnimation(EntityRef entity) override
|
|
{
|
|
return m_animables[entity].animation;
|
|
}
|
|
|
|
|
|
void startGame() override
|
|
{
|
|
m_is_game_running = true;
|
|
}
|
|
|
|
|
|
void stopGame() override
|
|
{
|
|
m_is_game_running = false;
|
|
}
|
|
|
|
|
|
Universe& getUniverse() override { return m_universe; }
|
|
|
|
|
|
static void unloadResource(Resource* res)
|
|
{
|
|
if (!res) return;
|
|
|
|
res->decRefCount();
|
|
}
|
|
|
|
|
|
void setSource(Animator& animator, anim::Controller* res)
|
|
{
|
|
if (animator.resource == res) return;
|
|
if (animator.resource != nullptr) {
|
|
if (animator.ctx) {
|
|
animator.resource->destroyRuntime(*animator.ctx);
|
|
animator.ctx = nullptr;
|
|
}
|
|
animator.resource->getObserverCb().unbind<&AnimationSceneImpl::onControllerResourceChanged>(this);
|
|
}
|
|
animator.resource = res;
|
|
if (animator.resource != nullptr) {
|
|
animator.resource->onLoaded<&AnimationSceneImpl::onControllerResourceChanged>(this);
|
|
}
|
|
}
|
|
|
|
|
|
void onControllerResourceChanged(Resource::State old_state, Resource::State new_state, Resource& resource)
|
|
{
|
|
for (Animator& animator : m_animators) {
|
|
if (animator.resource == &resource) {
|
|
if(new_state == Resource::State::READY) {
|
|
if (!animator.ctx) {
|
|
animator.ctx = animator.resource->createRuntime(animator.default_set);
|
|
}
|
|
}
|
|
else {
|
|
if (animator.ctx) {
|
|
animator.resource->destroyRuntime(*animator.ctx);
|
|
animator.ctx = nullptr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void destroyPropertyAnimator(EntityRef entity)
|
|
{
|
|
int idx = m_property_animators.find(entity);
|
|
auto& animator = m_property_animators.at(idx);
|
|
unloadResource(animator.animation);
|
|
m_property_animators.erase(entity);
|
|
m_universe.onComponentDestroyed(entity, PROPERTY_ANIMATOR_TYPE, this);
|
|
}
|
|
|
|
|
|
void destroyAnimable(EntityRef entity)
|
|
{
|
|
auto& animable = m_animables[entity];
|
|
unloadResource(animable.animation);
|
|
m_animables.erase(entity);
|
|
m_universe.onComponentDestroyed(entity, ANIMABLE_TYPE, this);
|
|
}
|
|
|
|
|
|
void destroyAnimator(EntityRef entity)
|
|
{
|
|
const u32 idx = m_animator_map[entity];
|
|
Animator& animator = m_animators[idx];
|
|
unloadResource(animator.resource);
|
|
setSource(animator, nullptr);
|
|
const Animator& last = m_animators.back();
|
|
m_animator_map[last.entity] = idx;
|
|
m_animator_map.erase(entity);
|
|
m_animators.swapAndPop(idx);
|
|
m_universe.onComponentDestroyed(entity, ANIMATOR_TYPE, this);
|
|
}
|
|
|
|
|
|
void serialize(OutputMemoryStream& serializer) override
|
|
{
|
|
serializer.write((u32)m_animables.size());
|
|
for (const Animable& animable : m_animables)
|
|
{
|
|
serializer.write(animable.entity);
|
|
serializer.writeString(animable.animation ? animable.animation->getPath().c_str() : "");
|
|
}
|
|
|
|
serializer.write((u32)m_property_animators.size());
|
|
for (int i = 0, n = m_property_animators.size(); i < n; ++i)
|
|
{
|
|
const PropertyAnimator& animator = m_property_animators.at(i);
|
|
EntityRef entity = m_property_animators.getKey(i);
|
|
serializer.write(entity);
|
|
serializer.writeString(animator.animation ? animator.animation->getPath().c_str() : "");
|
|
serializer.write(animator.flags.base);
|
|
}
|
|
|
|
serializer.write((u32)m_animators.size());
|
|
for (const Animator& animator : m_animators)
|
|
{
|
|
serializer.write(animator.default_set);
|
|
serializer.write(animator.entity);
|
|
serializer.writeString(animator.resource ? animator.resource->getPath().c_str() : "");
|
|
}
|
|
}
|
|
|
|
|
|
void deserialize(InputMemoryStream& serializer, const EntityMap& entity_map, i32 version) override
|
|
{
|
|
u32 count;
|
|
serializer.read(count);
|
|
m_animables.reserve(count + m_animables.size());
|
|
for (u32 i = 0; i < count; ++i)
|
|
{
|
|
Animable animable;
|
|
serializer.read(animable.entity);
|
|
animable.entity = entity_map.get(animable.entity);
|
|
animable.time = Time::fromSeconds(0);
|
|
|
|
const char* path = serializer.readString();
|
|
animable.animation = path[0] == '\0' ? nullptr : loadAnimation(Path(path));
|
|
m_animables.insert(animable.entity, animable);
|
|
m_universe.onComponentCreated(animable.entity, ANIMABLE_TYPE, this);
|
|
}
|
|
|
|
serializer.read(count);
|
|
m_property_animators.reserve(count + m_property_animators.size());
|
|
for (u32 i = 0; i < count; ++i)
|
|
{
|
|
EntityRef entity;
|
|
serializer.read(entity);
|
|
entity = entity_map.get(entity);
|
|
|
|
PropertyAnimator& animator = m_property_animators.emplace(entity, m_allocator);
|
|
const char* path = serializer.readString();
|
|
serializer.read(animator.flags.base);
|
|
animator.time = 0;
|
|
animator.animation = loadPropertyAnimation(Path(path));
|
|
m_universe.onComponentCreated(entity, PROPERTY_ANIMATOR_TYPE, this);
|
|
}
|
|
|
|
|
|
serializer.read(count);
|
|
m_animators.reserve(m_animators.size() + count);
|
|
for (u32 i = 0; i < count; ++i)
|
|
{
|
|
Animator animator;
|
|
serializer.read(animator.default_set);
|
|
serializer.read(animator.entity);
|
|
animator.entity = entity_map.get(animator.entity);
|
|
|
|
const char* tmp = serializer.readString();
|
|
setSource(animator, tmp[0] ? loadController(Path(tmp)) : nullptr);
|
|
m_animator_map.insert(animator.entity, m_animators.size());
|
|
m_animators.push(animator);
|
|
m_universe.onComponentCreated(animator.entity, ANIMATOR_TYPE, this);
|
|
}
|
|
}
|
|
|
|
|
|
void setAnimatorSource(EntityRef entity, const Path& path) override
|
|
{
|
|
Animator& animator = m_animators[m_animator_map[entity]];
|
|
unloadResource(animator.resource);
|
|
setSource(animator, path.isEmpty() ? nullptr : loadController(path));
|
|
if (animator.resource && animator.resource->isReady() && m_is_game_running) {
|
|
animator.ctx = animator.resource->createRuntime(animator.default_set);
|
|
}
|
|
}
|
|
|
|
anim::Controller* getAnimatorController(EntityRef entity) override {
|
|
const Animator& animator = m_animators[m_animator_map[entity]];
|
|
return animator.resource;
|
|
}
|
|
|
|
Path getAnimatorSource(EntityRef entity) override
|
|
{
|
|
const Animator& animator = m_animators[m_animator_map[entity]];
|
|
return animator.resource ? animator.resource->getPath() : Path("");
|
|
}
|
|
|
|
bool isPropertyAnimatorEnabled(EntityRef entity) override
|
|
{
|
|
return !m_property_animators.get(entity).flags.isSet(PropertyAnimator::DISABLED);
|
|
}
|
|
|
|
|
|
void enablePropertyAnimator(EntityRef entity, bool enabled) override
|
|
{
|
|
PropertyAnimator& animator = m_property_animators.get(entity);
|
|
animator.flags.set(PropertyAnimator::DISABLED, !enabled);
|
|
animator.time = 0;
|
|
if (!enabled)
|
|
{
|
|
applyPropertyAnimator(entity, animator);
|
|
}
|
|
}
|
|
|
|
|
|
Path getPropertyAnimation(EntityRef entity) override
|
|
{
|
|
const auto& animator = m_property_animators.get(entity);
|
|
return animator.animation ? animator.animation->getPath() : Path("");
|
|
}
|
|
|
|
|
|
void setPropertyAnimation(EntityRef entity, const Path& path) override
|
|
{
|
|
auto& animator = m_property_animators.get(entity);
|
|
animator.time = 0;
|
|
unloadResource(animator.animation);
|
|
animator.animation = loadPropertyAnimation(path);
|
|
}
|
|
|
|
|
|
Path getAnimation(EntityRef entity) override
|
|
{
|
|
const auto& animable = m_animables[entity];
|
|
return animable.animation ? animable.animation->getPath() : Path("");
|
|
}
|
|
|
|
|
|
void setAnimation(EntityRef entity, const Path& path) override
|
|
{
|
|
auto& animable = m_animables[entity];
|
|
unloadResource(animable.animation);
|
|
animable.animation = loadAnimation(path);
|
|
animable.time = Time::fromSeconds(0);
|
|
}
|
|
|
|
|
|
void updateAnimable(Animable& animable, float time_delta) const
|
|
{
|
|
if (!animable.animation || !animable.animation->isReady()) return;
|
|
EntityRef entity = animable.entity;
|
|
if (!m_universe.hasComponent(entity, MODEL_INSTANCE_TYPE)) return;
|
|
|
|
Model* model = m_render_scene->getModelInstanceModel(entity);
|
|
if (!model->isReady()) return;
|
|
|
|
Pose* pose = m_render_scene->lockPose(entity);
|
|
if (!pose) return;
|
|
|
|
model->getRelativePose(*pose);
|
|
animable.animation->getRelativePose(animable.time, *pose, *model, nullptr);
|
|
pose->computeAbsolute(*model);
|
|
|
|
Time t = animable.time + Time::fromSeconds(time_delta);
|
|
const Time l = animable.animation->getLength();
|
|
t = t % l;
|
|
animable.time = t;
|
|
|
|
m_render_scene->unlockPose(entity, true);
|
|
}
|
|
|
|
|
|
void updateAnimable(EntityRef entity, float time_delta) override
|
|
{
|
|
Animable& animable = m_animables[entity];
|
|
updateAnimable(animable, time_delta);
|
|
}
|
|
|
|
|
|
void updateAnimator(EntityRef entity, float time_delta) override {
|
|
Animator& animator = m_animators[m_animator_map[entity]];
|
|
updateAnimator(animator, time_delta);
|
|
}
|
|
|
|
void setAnimatorInput(EntityRef entity, u32 input_idx, float value) override {
|
|
Animator& animator = m_animators[m_animator_map[entity]];
|
|
if (!animator.ctx) return;
|
|
|
|
const anim::InputDecl& decl = animator.resource->m_inputs;
|
|
if (input_idx >= decl.inputs_count) return;
|
|
if (decl.inputs[input_idx].type != anim::InputDecl::FLOAT) return;
|
|
|
|
*(float*)&animator.ctx->inputs[decl.inputs[input_idx].offset] = value;
|
|
}
|
|
|
|
void setAnimatorInput(EntityRef entity, u32 input_idx, bool value) override {
|
|
Animator& animator = m_animators[m_animator_map[entity]];
|
|
if (!animator.ctx) return;
|
|
|
|
const anim::InputDecl& decl = animator.resource->m_inputs;
|
|
if (input_idx >= decl.inputs_count) return;
|
|
if (decl.inputs[input_idx].type != anim::InputDecl::BOOL) return;
|
|
|
|
*(bool*)&animator.ctx->inputs[decl.inputs[input_idx].offset] = value;
|
|
}
|
|
|
|
float getAnimatorFloatInput(EntityRef entity, u32 input_idx) override {
|
|
Animator& animator = m_animators[m_animator_map[entity]];
|
|
if (!animator.ctx) return 0;
|
|
|
|
const anim::InputDecl& decl = animator.resource->m_inputs;
|
|
ASSERT(input_idx < lengthOf(decl.inputs));
|
|
ASSERT(decl.inputs[input_idx].type == anim::InputDecl::FLOAT);
|
|
|
|
return *(float*)&animator.ctx->inputs[decl.inputs[input_idx].offset];
|
|
}
|
|
|
|
bool getAnimatorBoolInput(EntityRef entity, u32 input_idx) override {
|
|
Animator& animator = m_animators[m_animator_map[entity]];
|
|
if (!animator.ctx) return 0;
|
|
|
|
const anim::InputDecl& decl = animator.resource->m_inputs;
|
|
ASSERT(input_idx < lengthOf(decl.inputs));
|
|
ASSERT(decl.inputs[input_idx].type == anim::InputDecl::BOOL);
|
|
|
|
return *(bool*)&animator.ctx->inputs[decl.inputs[input_idx].offset];
|
|
}
|
|
|
|
u32 getAnimatorU32Input(EntityRef entity, u32 input_idx) override {
|
|
Animator& animator = m_animators[m_animator_map[entity]];
|
|
if (!animator.ctx) return 0;
|
|
|
|
const anim::InputDecl& decl = animator.resource->m_inputs;
|
|
ASSERT(input_idx < lengthOf(decl.inputs));
|
|
ASSERT(decl.inputs[input_idx].type == anim::InputDecl::U32);
|
|
|
|
return *(u32*)&animator.ctx->inputs[decl.inputs[input_idx].offset];
|
|
}
|
|
|
|
void setAnimatorInput(EntityRef entity, u32 input_idx, u32 value) override {
|
|
Animator& animator = m_animators[m_animator_map[entity]];
|
|
if (!animator.ctx) return;
|
|
|
|
const anim::InputDecl& decl = animator.resource->m_inputs;
|
|
if (input_idx >= decl.inputs_count) return;
|
|
if (decl.inputs[input_idx].type != anim::InputDecl::U32) return;
|
|
|
|
*(u32*)&animator.ctx->inputs[decl.inputs[input_idx].offset] = value;
|
|
}
|
|
|
|
LocalRigidTransform getAnimatorRootMotion(EntityRef entity) override
|
|
{
|
|
auto iter = m_animator_map.find(entity);
|
|
if (!iter.isValid()) return {};
|
|
Animator& animator = m_animators[iter.value()];
|
|
return animator.root_motion;
|
|
}
|
|
|
|
|
|
void applyAnimatorSet(EntityRef entity, u32 idx) override
|
|
{
|
|
Animator& animator = m_animators[m_animator_map[entity]];
|
|
for (auto& entry : animator.resource->m_animation_entries)
|
|
{
|
|
if (entry.set != idx) continue;
|
|
animator.ctx->animations[entry.slot] = entry.animation;
|
|
}
|
|
}
|
|
|
|
|
|
void setAnimatorDefaultSet(EntityRef entity, u32 idx) override
|
|
{
|
|
Animator& animator = m_animators[m_animator_map[entity]];
|
|
animator.default_set = idx;
|
|
}
|
|
|
|
|
|
u32 getAnimatorDefaultSet(EntityRef entity) override
|
|
{
|
|
Animator& animator = m_animators[m_animator_map[entity]];
|
|
return animator.default_set;
|
|
}
|
|
|
|
void updateAnimator(Animator& animator, float time_delta)
|
|
{
|
|
if (!animator.resource || !animator.resource->isReady()) return;
|
|
if (!animator.ctx) {
|
|
animator.ctx = animator.resource->createRuntime(animator.default_set);
|
|
}
|
|
|
|
const EntityRef entity = animator.entity;
|
|
if (!m_universe.hasComponent(entity, MODEL_INSTANCE_TYPE)) return;
|
|
|
|
Model* model = m_render_scene->getModelInstanceModel(entity);
|
|
if (!model->isReady()) return;
|
|
|
|
Pose* pose = m_render_scene->lockPose(entity);
|
|
if (!pose) return;
|
|
|
|
animator.ctx->model = model;
|
|
animator.ctx->time_delta = Time::fromSeconds(time_delta);
|
|
animator.ctx->root_bone_hash = crc32(animator.resource->m_root_motion_bone);
|
|
animator.resource->update(*animator.ctx, animator.root_motion);
|
|
|
|
model->getRelativePose(*pose);
|
|
animator.resource->getPose(*animator.ctx, *pose);
|
|
|
|
for (Animator::IK& ik : animator.inverse_kinematics) {
|
|
if (ik.weight == 0) break;
|
|
const u32 idx = u32(&ik - animator.inverse_kinematics);
|
|
updateIK(animator.resource->m_ik[idx], ik, *pose, *model);
|
|
}
|
|
|
|
pose->computeAbsolute(*model);
|
|
|
|
m_render_scene->unlockPose(entity, true);
|
|
}
|
|
|
|
static LocalRigidTransform getAbsolutePosition(const Pose& pose, const Model& model, int bone_index)
|
|
{
|
|
const Model::Bone& bone = model.getBone(bone_index);
|
|
LocalRigidTransform bone_transform{pose.positions[bone_index], pose.rotations[bone_index]};
|
|
if (bone.parent_idx < 0)
|
|
{
|
|
return bone_transform;
|
|
}
|
|
return getAbsolutePosition(pose, model, bone.parent_idx) * bone_transform;
|
|
}
|
|
|
|
static void updateIK(anim::Controller::IK& res_ik, Animator::IK& ik, Pose& pose, Model& model)
|
|
{
|
|
u32 indices[anim::Controller::IK::MAX_BONES_COUNT];
|
|
LocalRigidTransform transforms[anim::Controller::IK::MAX_BONES_COUNT];
|
|
Vec3 old_pos[anim::Controller::IK::MAX_BONES_COUNT];
|
|
float len[anim::Controller::IK::MAX_BONES_COUNT - 1];
|
|
float len_sum = 0;
|
|
for (int i = 0; i < res_ik.bones_count; ++i) {
|
|
auto iter = model.getBoneIndex(res_ik.bones[i]);
|
|
if (!iter.isValid()) return;
|
|
|
|
indices[i] = iter.value();
|
|
}
|
|
|
|
// convert from bone space to object space
|
|
const Model::Bone& first_bone = model.getBone(indices[0]);
|
|
LocalRigidTransform roots_parent;
|
|
if (first_bone.parent_idx >= 0) {
|
|
roots_parent = getAbsolutePosition(pose, model, first_bone.parent_idx);
|
|
}
|
|
else {
|
|
roots_parent.pos = Vec3::ZERO;
|
|
roots_parent.rot = Quat::IDENTITY;
|
|
}
|
|
|
|
LocalRigidTransform parent_tr = roots_parent;
|
|
for (int i = 0; i < res_ik.bones_count; ++i) {
|
|
LocalRigidTransform tr{pose.positions[indices[i]], pose.rotations[indices[i]]};
|
|
transforms[i] = parent_tr * tr;
|
|
old_pos[i] = transforms[i].pos;
|
|
if (i > 0) {
|
|
len[i - 1] = length(transforms[i].pos - transforms[i - 1].pos);
|
|
len_sum += len[i - 1];
|
|
}
|
|
parent_tr = transforms[i];
|
|
}
|
|
|
|
Vec3 target = ik.target;
|
|
Vec3 to_target = target - transforms[0].pos;
|
|
if (len_sum * len_sum < squaredLength(to_target)) {
|
|
to_target = normalize(to_target);
|
|
target = transforms[0].pos + to_target * len_sum;
|
|
}
|
|
|
|
for (int iteration = 0; iteration < res_ik.max_iterations; ++iteration) {
|
|
transforms[res_ik.bones_count - 1].pos = target;
|
|
|
|
for (int i = res_ik.bones_count - 1; i > 1; --i) {
|
|
Vec3 dir = normalize((transforms[i - 1].pos - transforms[i].pos));
|
|
transforms[i - 1].pos = transforms[i].pos + dir * len[i - 1];
|
|
}
|
|
|
|
for (int i = 1; i < res_ik.bones_count; ++i) {
|
|
Vec3 dir = normalize((transforms[i].pos - transforms[i - 1].pos));
|
|
transforms[i].pos = transforms[i - 1].pos + dir * len[i - 1];
|
|
}
|
|
}
|
|
|
|
// compute rotations from new positions
|
|
for (int i = res_ik.bones_count - 2; i >= 0; --i) {
|
|
Vec3 old_d = old_pos[i + 1] - old_pos[i];
|
|
Vec3 new_d = transforms[i + 1].pos - transforms[i].pos;
|
|
|
|
Quat rel_rot = Quat::vec3ToVec3(old_d, new_d);
|
|
transforms[i].rot = rel_rot * transforms[i].rot;
|
|
}
|
|
|
|
// convert from object space to bone space
|
|
LocalRigidTransform ik_out[anim::Controller::IK::MAX_BONES_COUNT];
|
|
for (int i = res_ik.bones_count - 1; i > 0; --i) {
|
|
transforms[i] = transforms[i - 1].inverted() * transforms[i];
|
|
ik_out[i].pos = transforms[i].pos;
|
|
}
|
|
for (int i = res_ik.bones_count - 2; i > 0; --i) {
|
|
ik_out[i].rot = transforms[i].rot;
|
|
}
|
|
ik_out[res_ik.bones_count - 1].rot = pose.rotations[indices[res_ik.bones_count - 1]];
|
|
|
|
if (first_bone.parent_idx >= 0) {
|
|
ik_out[0].rot = roots_parent.rot.conjugated() * transforms[0].rot;
|
|
}
|
|
else {
|
|
ik_out[0].rot = transforms[0].rot;
|
|
}
|
|
ik_out[0].pos = pose.positions[indices[0]];
|
|
|
|
const float w = ik.weight;
|
|
for (u32 i = 0; i < res_ik.bones_count; ++i) {
|
|
const u32 idx = indices[i];
|
|
pose.positions[idx] = lerp(pose.positions[idx], ik_out[i].pos, w);
|
|
pose.rotations[idx] = nlerp(pose.rotations[idx], ik_out[i].rot, w);
|
|
}
|
|
}
|
|
|
|
|
|
void applyPropertyAnimator(EntityRef entity, PropertyAnimator& animator)
|
|
{
|
|
const PropertyAnimation* animation = animator.animation;
|
|
int frame = int(animator.time * animation->fps + 0.5f);
|
|
frame = frame % animation->curves[0].frames.back();
|
|
for (PropertyAnimation::Curve& curve : animation->curves)
|
|
{
|
|
if (curve.frames.size() < 2) continue;
|
|
for (int i = 1, n = curve.frames.size(); i < n; ++i)
|
|
{
|
|
if (frame <= curve.frames[i])
|
|
{
|
|
float t = (frame - curve.frames[i - 1]) / float(curve.frames[i] - curve.frames[i - 1]);
|
|
float v = curve.values[i] * t + curve.values[i - 1] * (1 - t);
|
|
ComponentUID cmp;
|
|
cmp.type = curve.cmp_type;
|
|
cmp.scene = m_universe.getScene(cmp.type);
|
|
cmp.entity = entity;
|
|
ASSERT(curve.property->setter);
|
|
curve.property->set(cmp, -1, v);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void updatePropertyAnimators(float time_delta)
|
|
{
|
|
PROFILE_FUNCTION();
|
|
for (int anim_idx = 0, c = m_property_animators.size(); anim_idx < c; ++anim_idx)
|
|
{
|
|
EntityRef entity = m_property_animators.getKey(anim_idx);
|
|
PropertyAnimator& animator = m_property_animators.at(anim_idx);
|
|
const PropertyAnimation* animation = animator.animation;
|
|
if (!animation || !animation->isReady()) continue;
|
|
if (animation->curves.empty()) continue;
|
|
if (animation->curves[0].frames.empty()) continue;
|
|
if (animator.flags.isSet(PropertyAnimator::DISABLED)) continue;
|
|
|
|
animator.time += time_delta;
|
|
|
|
applyPropertyAnimator(entity, animator);
|
|
}
|
|
}
|
|
|
|
|
|
void updateAnimables(float time_delta)
|
|
{
|
|
PROFILE_FUNCTION();
|
|
if (m_animables.size() == 0) return;
|
|
|
|
jobs::forEach(m_animables.size(), 1, [&](i32 idx, i32){
|
|
Animable& animable = m_animables.at(idx);
|
|
updateAnimable(animable, time_delta);
|
|
});
|
|
}
|
|
|
|
|
|
void update(float time_delta, bool paused) override
|
|
{
|
|
PROFILE_FUNCTION();
|
|
if (!m_is_game_running) return;
|
|
if (paused) return;
|
|
|
|
updateAnimables(time_delta);
|
|
updatePropertyAnimators(time_delta);
|
|
|
|
jobs::forEach(m_animators.size(), 1, [&](i32 idx, i32){
|
|
updateAnimator(m_animators[idx], time_delta);
|
|
});
|
|
}
|
|
|
|
|
|
PropertyAnimation* loadPropertyAnimation(const Path& path) const
|
|
{
|
|
if (path.isEmpty()) return nullptr;
|
|
ResourceManagerHub& rm = m_engine.getResourceManager();
|
|
return rm.load<PropertyAnimation>(path);
|
|
}
|
|
|
|
|
|
Animation* loadAnimation(const Path& path) const
|
|
{
|
|
ResourceManagerHub& rm = m_engine.getResourceManager();
|
|
return rm.load<Animation>(path);
|
|
}
|
|
|
|
|
|
anim::Controller* loadController(const Path& path) const
|
|
{
|
|
ResourceManagerHub& rm = m_engine.getResourceManager();
|
|
return rm.load<anim::Controller>(path);
|
|
}
|
|
|
|
|
|
void createPropertyAnimator(EntityRef entity)
|
|
{
|
|
PropertyAnimator& animator = m_property_animators.emplace(entity, m_allocator);
|
|
animator.animation = nullptr;
|
|
animator.time = 0;
|
|
m_universe.onComponentCreated(entity, PROPERTY_ANIMATOR_TYPE, this);
|
|
}
|
|
|
|
|
|
void createAnimable(EntityRef entity)
|
|
{
|
|
Animable& animable = m_animables.insert(entity);
|
|
animable.time = Time::fromSeconds(0);
|
|
animable.animation = nullptr;
|
|
animable.entity = entity;
|
|
|
|
m_universe.onComponentCreated(entity, ANIMABLE_TYPE, this);
|
|
}
|
|
|
|
|
|
void createAnimator(EntityRef entity)
|
|
{
|
|
m_animator_map.insert(entity, m_animators.size());
|
|
Animator& animator = m_animators.emplace();
|
|
animator.entity = entity;
|
|
|
|
m_universe.onComponentCreated(entity, ANIMATOR_TYPE, this);
|
|
}
|
|
|
|
|
|
IPlugin& getPlugin() const override { return m_anim_system; }
|
|
|
|
|
|
IAllocator& m_allocator;
|
|
Universe& m_universe;
|
|
IPlugin& m_anim_system;
|
|
Engine& m_engine;
|
|
AssociativeArray<EntityRef, Animable> m_animables;
|
|
AssociativeArray<EntityRef, PropertyAnimator> m_property_animators;
|
|
HashMap<EntityRef, u32> m_animator_map;
|
|
Array<Animator> m_animators;
|
|
RenderScene* m_render_scene;
|
|
bool m_is_game_running;
|
|
};
|
|
|
|
|
|
UniquePtr<AnimationScene> AnimationScene::create(Engine& engine, IPlugin& plugin, Universe& universe, IAllocator& allocator)
|
|
{
|
|
return UniquePtr<AnimationSceneImpl>::create(allocator, engine, plugin, universe, allocator);
|
|
}
|
|
|
|
void AnimationScene::reflect(Engine& engine) {
|
|
LUMIX_SCENE(AnimationSceneImpl, "animation")
|
|
.LUMIX_CMP(PropertyAnimator, "property_animator", "Animation / Property animator")
|
|
.LUMIX_PROP(PropertyAnimation, "Animation").resourceAttribute(PropertyAnimation::TYPE)
|
|
.prop<&AnimationScene::isPropertyAnimatorEnabled, &AnimationScene::enablePropertyAnimator>("Enabled")
|
|
.LUMIX_CMP(Animator, "animator", "Animation / Animator")
|
|
.function<(void (AnimationScene::*)(EntityRef, u32, u32))&AnimationScene::setAnimatorInput>("setU32Input", "AnimationScene::setAnimatorInput")
|
|
.function<(void (AnimationScene::*)(EntityRef, u32, float))&AnimationScene::setAnimatorInput>("setFloatInput", "AnimationScene::setAnimatorInput")
|
|
.function<(void (AnimationScene::*)(EntityRef, u32, bool))&AnimationScene::setAnimatorInput>("setBoolInput", "AnimationScene::setAnimatorInput")
|
|
.LUMIX_FUNC_EX(getAnimatorInputIndex, "getInputIndex")
|
|
.LUMIX_FUNC_EX(setAnimatorIK, "setIK")
|
|
.LUMIX_PROP(AnimatorSource, "Source").resourceAttribute(anim::Controller::TYPE)
|
|
.LUMIX_PROP(AnimatorDefaultSet, "Default set")
|
|
.LUMIX_CMP(Animable, "animable", "Animation / Animable")
|
|
.LUMIX_PROP(Animation, "Animation").resourceAttribute(Animation::TYPE)
|
|
;
|
|
}
|
|
|
|
} // namespace Lumix
|