LumixEngine/src/animation/animation_system.cpp
Mikulas Florek 4f0f7ed068 api
2017-09-23 21:56:46 +02:00

1218 lines
34 KiB
C++

#include "animation_system.h"
#include "animation/animation.h"
#include "animation/controller.h"
#include "animation/events.h"
#include "engine/base_proxy_allocator.h"
#include "engine/blob.h"
#include "engine/crc32.h"
#include "engine/engine.h"
#include "engine/json_serializer.h"
#include "engine/lua_wrapper.h"
#include "engine/job_system.h"
#include "engine/profiler.h"
#include "engine/property_descriptor.h"
#include "engine/property_register.h"
#include "engine/resource_manager.h"
#include "engine/serializer.h"
#include "engine/universe/universe.h"
#include "renderer/model.h"
#include "renderer/pose.h"
#include "renderer/render_scene.h"
#include <cfloat>
#include <cmath>
namespace Lumix
{
enum class AnimationSceneVersion
{
SHARED_CONTROLLER,
LATEST
};
static const ComponentType ANIMABLE_TYPE = PropertyRegister::getComponentType("animable");
static const ComponentType CONTROLLER_TYPE = PropertyRegister::getComponentType("anim_controller");
static const ComponentType SHARED_CONTROLLER_TYPE = PropertyRegister::getComponentType("shared_anim_controller");
static const ResourceType ANIMATION_TYPE("animation");
static const ResourceType CONTROLLER_RESOURCE_TYPE("anim_controller");
struct AnimSetPropertyDescriptor : public IEnumPropertyDescriptor
{
AnimSetPropertyDescriptor(const char* name)
{
setName(name);
m_type = ENUM;
}
void set(ComponentUID cmp, int index, InputBlob& stream) const override
{
int value;
stream.read(&value, sizeof(value));
(static_cast<AnimationScene*>(cmp.scene)->setControllerDefaultSet)(cmp.handle, value);
};
void get(ComponentUID cmp, int index, OutputBlob& stream) const override
{
int value;
ASSERT(index == -1);
value = (static_cast<AnimationScene*>(cmp.scene)->getControllerDefaultSet)(cmp.handle);
stream.write(&value, sizeof(value));
}
int getEnumCount(IScene* scene, ComponentHandle cmp) override
{
Anim::ControllerResource* res = static_cast<AnimationScene*>(scene)->getControllerResource(cmp);
return res->m_sets_names.size();
}
const char* getEnumItemName(IScene* scene, ComponentHandle cmp, int index) override
{
Anim::ControllerResource* res = static_cast<AnimationScene*>(scene)->getControllerResource(cmp);
return res->m_sets_names[index];
}
void getEnumItemName(IScene* scene, ComponentHandle cmp, int index, char* buf, int max_size) override
{
Anim::ControllerResource* res = static_cast<AnimationScene*>(scene)->getControllerResource(cmp);
copyString(buf, max_size, res->m_sets_names[index]);
}
};
namespace FS
{
class FileSystem;
};
class Animation;
class Engine;
class JsonSerializer;
class Universe;
struct AnimationSystemImpl LUMIX_FINAL : public IPlugin
{
explicit AnimationSystemImpl(Engine& engine);
~AnimationSystemImpl();
void registerLuaAPI();
void createScenes(Universe& ctx) override;
void destroyScene(IScene* scene) override;
const char* getName() const override { return "animation"; }
IAllocator& m_allocator;
Engine& m_engine;
AnimationManager m_animation_manager;
Anim::ControllerManager m_controller_manager;
private:
void operator=(const AnimationSystemImpl&);
AnimationSystemImpl(const AnimationSystemImpl&);
};
struct AnimationSceneImpl LUMIX_FINAL : public AnimationScene
{
friend struct AnimationSystemImpl;
struct SharedController
{
Entity entity;
Entity parent;
};
struct Controller
{
Controller(IAllocator& allocator) : input(allocator), animations(allocator) {}
Entity entity;
Anim::ControllerResource* resource = nullptr;
Anim::ComponentInstance* root = nullptr;
u32 default_set = 0;
Array<u8> input;
HashMap<u32, Animation*> animations;
struct IK
{
enum { MAX_BONES_COUNT = 8 };
float weight = 0;
i16 max_iterations = 5;
i16 bones_count = 4;
u32 bones[MAX_BONES_COUNT];
Vec3 target;
} inverse_kinematics[4];
};
struct Animable
{
float time;
float time_scale;
float start_time;
Animation* animation;
Entity entity;
};
AnimationSceneImpl(AnimationSystemImpl& anim_system, Engine& engine, Universe& universe, IAllocator& allocator)
: m_universe(universe)
, m_engine(engine)
, m_anim_system(anim_system)
, m_animables(allocator)
, m_controllers(allocator)
, m_shared_controllers(allocator)
, m_event_stream(allocator)
{
m_is_game_running = false;
m_render_scene = static_cast<RenderScene*>(universe.getScene(crc32("renderer")));
universe.registerComponentType(ANIMABLE_TYPE, this, &AnimationSceneImpl::serializeAnimable, &AnimationSceneImpl::deserializeAnimable);
universe.registerComponentType(CONTROLLER_TYPE, this, &AnimationSceneImpl::serializeController, &AnimationSceneImpl::deserializeController);
universe.registerComponentType(SHARED_CONTROLLER_TYPE, this, &AnimationSceneImpl::serializeSharedController, &AnimationSceneImpl::deserializeSharedController);
ASSERT(m_render_scene);
}
void serializeSharedController(ISerializer& serializer, ComponentHandle cmp)
{
SharedController& ctrl = m_shared_controllers[{cmp.index}];
serializer.write("parent", ctrl.parent);
}
void deserializeSharedController(IDeserializer& serializer, Entity entity, int /*scene_version*/)
{
Entity parent;
serializer.read(&parent);
m_shared_controllers.insert(entity, {entity, parent});
m_universe.addComponent(entity, SHARED_CONTROLLER_TYPE, this, {entity.index});
}
void serializeAnimable(ISerializer& serializer, ComponentHandle cmp)
{
Animable& animable = m_animables[{cmp.index}];
serializer.write("time_scale", animable.time_scale);
serializer.write("start_time", animable.start_time);
serializer.write("animation", animable.animation ? animable.animation->getPath().c_str() : "");
}
void deserializeAnimable(IDeserializer& serializer, Entity entity, int /*scene_version*/)
{
Animable& animable = m_animables.insert(entity);
animable.entity = entity;
serializer.read(&animable.time_scale);
serializer.read(&animable.start_time);
char tmp[MAX_PATH_LENGTH];
serializer.read(tmp, lengthOf(tmp));
auto* res = tmp[0] ? m_engine.getResourceManager().get(ANIMATION_TYPE)->load(Path(tmp)) : nullptr;
animable.animation = (Animation*)res;
m_universe.addComponent(entity, ANIMABLE_TYPE, this, {entity.index});
}
void serializeController(ISerializer& serializer, ComponentHandle cmp)
{
Controller& controller = m_controllers.get({cmp.index});
serializer.write("source", controller.resource ? controller.resource->getPath().c_str() : "");
serializer.write("default_set", controller.default_set);
}
int getVersion() const override { return (int)AnimationSceneVersion::LATEST; }
void deserializeController(IDeserializer& serializer, Entity entity, int scene_version)
{
Controller& controller = m_controllers.emplace(entity, m_anim_system.m_allocator);
controller.entity = entity;
char tmp[MAX_PATH_LENGTH];
serializer.read(tmp, lengthOf(tmp));
if (scene_version > (int)AnimationSceneVersion::SHARED_CONTROLLER)
{
serializer.read(&controller.default_set);
}
auto* res = tmp[0] ? m_engine.getResourceManager().get(CONTROLLER_RESOURCE_TYPE)->load(Path(tmp)) : nullptr;
setControllerResource(controller, (Anim::ControllerResource*)res);
m_universe.addComponent(entity, CONTROLLER_TYPE, this, {entity.index});
}
const OutputBlob& getEventStream() const override
{
return m_event_stream;
}
void clear() override
{
for (Animable& animable : m_animables)
{
unloadAnimation(animable.animation);
}
m_animables.clear();
for (Controller& controller : m_controllers)
{
unloadController(controller.resource);
setControllerResource(controller, nullptr);
}
m_controllers.clear();
}
static int setIK(lua_State* L)
{
AnimationSceneImpl* scene = LuaWrapper::checkArg<AnimationSceneImpl*>(L, 1);
ComponentHandle cmp = LuaWrapper::checkArg<ComponentHandle>(L, 2);
Controller& controller = scene->m_controllers.get({ cmp.index });
int index = LuaWrapper::checkArg<int>(L, 3);
Controller::IK& ik = controller.inverse_kinematics[index];
ik.weight = LuaWrapper::checkArg<float>(L, 4);
ik.target = LuaWrapper::checkArg<Vec3>(L, 5);
Transform tr = scene->m_universe.getTransform(controller.entity);
ik.target = tr.inverted().transform(ik.target);
ik.bones_count = lua_gettop(L) - 5;
if (ik.bones_count > lengthOf(ik.bones))
{
luaL_argerror(L, ik.bones_count, "Too many arguments");
}
for (int i = 0; i < ik.bones_count; ++i)
{
const char* bone = LuaWrapper::checkArg<const char*>(L, i + 6);
ik.bones[i] = crc32(bone);
}
return 0;
}
int getControllerInputIndex(ComponentHandle cmp, const char* name) const
{
const Controller& controller = m_controllers[{cmp.index}];
Anim::InputDecl& decl = controller.resource->m_input_decl;
for (int 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 setControllerFloatInput(ComponentHandle cmp, int input_idx, float value)
{
Controller& controller = m_controllers.get({ cmp.index });
if (!controller.root)
{
g_log_warning.log("Animation") << "Trying to set input " << input_idx << " before the controller is ready";
return;
}
Anim::InputDecl& decl = controller.resource->m_input_decl;
if (input_idx < 0 || input_idx >= lengthOf(decl.inputs)) return;
if (decl.inputs[input_idx].type == Anim::InputDecl::FLOAT)
{
*(float*)&controller.input[decl.inputs[input_idx].offset] = value;
}
else
{
g_log_warning.log("Animation") << "Trying to set float to " << decl.inputs[input_idx].name;
}
}
void setControllerIntInput(ComponentHandle cmp, int input_idx, int value)
{
Controller& controller = m_controllers.get({ cmp.index });
if (!controller.root)
{
g_log_warning.log("Animation") << "Trying to set input " << input_idx << " before the controller is ready";
return;
}
Anim::InputDecl& decl = controller.resource->m_input_decl;
if (decl.inputs[input_idx].type == Anim::InputDecl::INT)
{
*(int*)&controller.input[decl.inputs[input_idx].offset] = value;
}
else
{
g_log_warning.log("Animation") << "Trying to set int to " << decl.inputs[input_idx].name;
}
}
void setControllerBoolInput(ComponentHandle cmp, int input_idx, bool value)
{
Controller& controller = m_controllers.get({ cmp.index });
if (!controller.root)
{
g_log_warning.log("Animation") << "Trying to set input " << input_idx << " before the controller is ready";
return;
}
Anim::InputDecl& decl = controller.resource->m_input_decl;
if (decl.inputs[input_idx].type == Anim::InputDecl::BOOL)
{
*(bool*)&controller.input[decl.inputs[input_idx].offset] = value;
}
else
{
g_log_warning.log("Animation") << "Trying to set bool to " << decl.inputs[input_idx].name;
}
}
float getAnimationLength(int animation_idx)
{
auto* animation = static_cast<Animation*>(animation_idx > 0 ? m_engine.getLuaResource(animation_idx) : nullptr);
if (animation) return animation->getLength();
return 0;
}
float getAnimableTime(ComponentHandle cmp) override
{
return m_animables[{cmp.index}].time;
}
void setAnimableTime(ComponentHandle cmp, float time) override
{
m_animables[{cmp.index}].time = time;
}
Animation* getAnimableAnimation(ComponentHandle cmp) override
{
return m_animables[{cmp.index}].animation;
}
void startGame() override
{
for (auto& controller : m_controllers)
{
initControllerRuntime(controller);
}
m_is_game_running = true;
}
void stopGame() override
{
for (auto& controller : m_controllers)
{
LUMIX_DELETE(m_anim_system.m_allocator, controller.root);
controller.root = nullptr;
}
m_is_game_running = false;
}
Universe& getUniverse() override { return m_universe; }
ComponentHandle getComponent(Entity entity, ComponentType type) override
{
if (type == ANIMABLE_TYPE)
{
if (m_animables.find(entity) < 0) return INVALID_COMPONENT;
return {entity.index};
}
else if (type == CONTROLLER_TYPE)
{
if (m_controllers.find(entity) < 0) return INVALID_COMPONENT;
return {entity.index};
}
else if (type == SHARED_CONTROLLER_TYPE)
{
if (m_shared_controllers.find(entity) < 0) return INVALID_COMPONENT;
return {entity.index};
}
return INVALID_COMPONENT;
}
ComponentHandle createComponent(ComponentType type, Entity entity) override
{
if (type == ANIMABLE_TYPE) return createAnimable(entity);
if (type == CONTROLLER_TYPE) return createController(entity);
if (type == SHARED_CONTROLLER_TYPE) return createSharedController(entity);
return INVALID_COMPONENT;
}
void unloadAnimation(Animation* animation)
{
if (!animation) return;
animation->getResourceManager().unload(*animation);
}
void unloadController(Anim::ControllerResource* res)
{
if (!res) return;
res->getResourceManager().unload(*res);
}
void setControllerResource(Controller& controller, Anim::ControllerResource* res)
{
if (controller.resource == res) return;
if (controller.resource != nullptr)
{
controller.resource->getObserverCb().unbind<AnimationSceneImpl, &AnimationSceneImpl::onControllerResourceChanged>(this);
}
if (controller.root != nullptr)
{
LUMIX_DELETE(m_engine.getAllocator(), controller.root);
controller.root = nullptr;
controller.default_set = 0;
controller.animations.clear();
controller.input.clear();
}
controller.resource = res;
if (controller.resource != nullptr)
{
controller.resource->onLoaded<AnimationSceneImpl, &AnimationSceneImpl::onControllerResourceChanged>(this);
}
}
void onControllerResourceChanged(Resource::State, Resource::State new_state, Resource& resource)
{
for (auto& controller : m_controllers)
{
if ((controller.resource == &resource) && (controller.root != nullptr) && (new_state != Resource::State::READY))
{
LUMIX_DELETE(m_engine.getAllocator(), controller.root);
controller.root = nullptr;
controller.default_set = 0;
controller.animations.clear();
controller.input.clear();
}
}
}
void destroyComponent(ComponentHandle component, ComponentType type) override
{
if (type == ANIMABLE_TYPE)
{
Entity entity = {component.index};
auto& animable = m_animables[entity];
unloadAnimation(animable.animation);
m_animables.erase(entity);
m_universe.destroyComponent(entity, type, this, component);
}
else if (type == CONTROLLER_TYPE)
{
Entity entity = {component.index};
auto& controller = m_controllers.get(entity);
unloadController(controller.resource);
setControllerResource(controller, nullptr);
m_controllers.erase(entity);
m_universe.destroyComponent(entity, type, this, component);
}
else if (type == SHARED_CONTROLLER_TYPE)
{
Entity entity = {component.index};
m_shared_controllers.erase(entity);
m_universe.destroyComponent(entity, type, this, component);
}
}
void serialize(OutputBlob& serializer) override
{
serializer.write((i32)m_animables.size());
for (const Animable& animable : m_animables)
{
serializer.write(animable.entity);
serializer.write(animable.time_scale);
serializer.write(animable.start_time);
serializer.writeString(animable.animation ? animable.animation->getPath().c_str() : "");
}
serializer.write(m_controllers.size());
for (const Controller& controller : m_controllers)
{
serializer.write(controller.default_set);
serializer.write(controller.entity);
serializer.writeString(controller.resource ? controller.resource->getPath().c_str() : "");
}
serializer.write(m_shared_controllers.size());
for (const SharedController& controller : m_shared_controllers)
{
serializer.write(controller.entity);
serializer.write(controller.parent);
}
}
void deserialize(InputBlob& serializer) override
{
i32 count;
serializer.read(count);
m_animables.reserve(count);
for (int i = 0; i < count; ++i)
{
Animable animable;
serializer.read(animable.entity);
serializer.read(animable.time_scale);
serializer.read(animable.start_time);
animable.time = animable.start_time;
char path[MAX_PATH_LENGTH];
serializer.readString(path, sizeof(path));
animable.animation = path[0] == '\0' ? nullptr : loadAnimation(Path(path));
m_animables.insert(animable.entity, animable);
ComponentHandle cmp = {animable.entity.index};
m_universe.addComponent(animable.entity, ANIMABLE_TYPE, this, cmp);
}
serializer.read(count);
m_controllers.reserve(count);
for (int i = 0; i < count; ++i)
{
Controller controller(m_anim_system.m_allocator);
serializer.read(controller.default_set);
serializer.read(controller.entity);
char tmp[MAX_PATH_LENGTH];
serializer.readString(tmp, lengthOf(tmp));
setControllerResource(controller, tmp[0] ? loadController(Path(tmp)) : nullptr);
m_controllers.insert(controller.entity, controller);
ComponentHandle cmp = { controller.entity.index };
m_universe.addComponent(controller.entity, CONTROLLER_TYPE, this, cmp);
}
serializer.read(count);
m_shared_controllers.reserve(count);
for (int i = 0; i < count; ++i)
{
SharedController controller;
serializer.read(controller.entity);
serializer.read(controller.parent);
m_shared_controllers.insert(controller.entity, controller);
ComponentHandle cmp = {controller.entity.index};
m_universe.addComponent(controller.entity, SHARED_CONTROLLER_TYPE, this, cmp);
}
}
void setSharedControllerParent(ComponentHandle cmp, Entity parent) override
{
m_shared_controllers[{cmp.index}].parent = parent;
}
Entity getSharedControllerParent(ComponentHandle cmp) override { return m_shared_controllers[{cmp.index}].parent; }
float getAnimableTimeScale(ComponentHandle cmp) { return m_animables[{cmp.index}].time_scale; }
void setAnimableTimeScale(ComponentHandle cmp, float time_scale) { m_animables[{cmp.index}].time_scale = time_scale; }
float getAnimableStartTime(ComponentHandle cmp) { return m_animables[{cmp.index}].start_time; }
void setAnimableStartTime(ComponentHandle cmp, float time) { m_animables[{cmp.index}].start_time = time; }
void setControllerSource(ComponentHandle cmp, const Path& path) override
{
auto& controller = m_controllers.get({cmp.index});
unloadController(controller.resource);
setControllerResource(controller, loadController(path));
if (controller.resource->isReady() && m_is_game_running)
{
initControllerRuntime(controller);
}
}
Path getControllerSource(ComponentHandle cmp) override
{
const auto& controller = m_controllers.get({cmp.index});
return controller.resource ? controller.resource->getPath() : Path("");
}
Path getAnimation(ComponentHandle cmp) override
{
const auto& animable = m_animables[{cmp.index}];
return animable.animation ? animable.animation->getPath() : Path("");
}
void setAnimation(ComponentHandle cmp, const Path& path) override
{
auto& animable = m_animables[{cmp.index}];
unloadAnimation(animable.animation);
animable.animation = loadAnimation(path);
animable.time = 0;
}
void updateAnimable(Animable& animable, float time_delta) const
{
if (!animable.animation || !animable.animation->isReady()) return;
ComponentHandle model_instance = m_render_scene->getModelInstanceComponent(animable.entity);
if (model_instance == INVALID_COMPONENT) return;
Model* model = m_render_scene->getModelInstanceModel(model_instance);
if (!model->isReady()) return;
Pose* pose = m_render_scene->lockPose(model_instance);
if (!pose) return;
model->getRelativePose(*pose);
animable.animation->getRelativePose(animable.time, *pose, *model);
pose->computeAbsolute(*model);
float t = animable.time + time_delta * animable.time_scale;
float l = animable.animation->getLength();
while (t > l) t -= l;
animable.time = t;
m_render_scene->unlockPose(model_instance, true);
}
void updateAnimable(ComponentHandle cmp, float time_delta) override
{
Animable& animable = m_animables[{cmp.index}];
updateAnimable(animable, time_delta);
}
void updateController(ComponentHandle cmp, float time_delta) override
{
Controller& controller = m_controllers.get({cmp.index});
updateController(controller, time_delta);
processEventStream();
m_event_stream.clear();
}
void setControllerInput(ComponentHandle cmp, int input_idx, float value) override
{
Controller& ctrl = m_controllers.get({ cmp.index });
Anim::InputDecl& decl = ctrl.resource->m_input_decl;
if (!ctrl.root) return;
if (input_idx >= lengthOf(decl.inputs)) return;
if (decl.inputs[input_idx].type != Anim::InputDecl::FLOAT) return;
*(float*)&ctrl.input[decl.inputs[input_idx].offset] = value;
}
void setControllerInput(ComponentHandle cmp, int input_idx, bool value) override
{
Controller& ctrl = m_controllers.get({ cmp.index });
Anim::InputDecl& decl = ctrl.resource->m_input_decl;
if (!ctrl.root) return;
if (input_idx >= lengthOf(decl.inputs)) return;
if (decl.inputs[input_idx].type != Anim::InputDecl::BOOL) return;
*(bool*)&ctrl.input[decl.inputs[input_idx].offset] = value;
}
void setControllerInput(ComponentHandle cmp, int input_idx, int value) override
{
Controller& ctrl = m_controllers.get({ cmp.index });
Anim::InputDecl& decl = ctrl.resource->m_input_decl;
if (!ctrl.root) return;
if (input_idx >= lengthOf(decl.inputs)) return;
if (decl.inputs[input_idx].type != Anim::InputDecl::INT) return;
*(bool*)&ctrl.input[decl.inputs[input_idx].offset] = value;
}
Anim::ComponentInstance* getControllerRoot(ComponentHandle cmp) override
{
return m_controllers.get({cmp.index}).root;
}
RigidTransform getControllerRootMotion(ComponentHandle cmp) override
{
Controller& ctrl = m_controllers.get({cmp.index});
return ctrl.root ? ctrl.root->getRootMotion() : RigidTransform({0, 0, 0}, {0, 0, 0, 1});
}
Entity getControllerEntity(ComponentHandle cmp) override { return {cmp.index}; }
u8* getControllerInput(ComponentHandle cmp)
{
auto& input = m_controllers.get({cmp.index}).input;
return input.empty() ? nullptr : &input[0];
}
void applyControllerSet(ComponentHandle cmp, const char* set_name) override
{
Controller& ctrl = m_controllers.get({cmp.index});
u32 set_name_hash = crc32(set_name);
int set_idx = ctrl.resource->m_sets_names.find([set_name_hash](const StaticString<32>& val) {
return crc32(val) == set_name_hash;
});
if (set_idx < 0) return;
for (auto& entry : ctrl.resource->m_animation_set)
{
if (entry.set != set_idx) continue;
ctrl.animations[entry.hash] = entry.animation;
}
if (ctrl.root) ctrl.root->onAnimationSetUpdated(ctrl.animations);
}
void setControllerDefaultSet(ComponentHandle cmp, int set) override
{
Controller& ctrl = m_controllers.get({cmp.index});
ctrl.default_set = ctrl.resource ? crc32(ctrl.resource->m_sets_names[set]) : 0;
}
int getControllerDefaultSet(ComponentHandle cmp) override
{
Controller& ctrl = m_controllers.get({ cmp.index });
auto is_default_set = [&ctrl](const StaticString<32>& val) {
return crc32(val) == ctrl.default_set;
};
int idx = 0;
if(ctrl.resource) idx = ctrl.resource->m_sets_names.find(is_default_set);
return idx < 0 ? 0 : idx;
}
Anim::ControllerResource* getControllerResource(ComponentHandle cmp) override
{
return m_controllers.get({cmp.index}).resource;
}
bool initControllerRuntime(Controller& controller)
{
if (!controller.resource->isReady()) return false;
if (controller.resource->m_input_decl.getSize() == 0) return false;
controller.root = controller.resource->createInstance(m_anim_system.m_allocator);
controller.input.resize(controller.resource->m_input_decl.getSize());
int set_idx = 0;
for (int i = 0; i < controller.resource->m_sets_names.size(); ++i)
{
if (controller.default_set == crc32(controller.resource->m_sets_names[i]))
{
set_idx = i;
break;
}
}
for (auto& entry : controller.resource->m_animation_set)
{
if (entry.set != set_idx) continue;
controller.animations.insert(entry.hash, entry.animation);
}
setMemory(&controller.input[0], 0, controller.input.size());
Anim::RunningContext rc;
rc.time_delta = 0;
rc.allocator = &m_anim_system.m_allocator;
rc.input = &controller.input[0];
rc.current = nullptr;
rc.anim_set = &controller.animations;
rc.event_stream = &m_event_stream;
rc.controller = {controller.entity.index};
controller.root->enter(rc, nullptr);
return true;
}
void updateSharedController(SharedController& controller, float time_delta)
{
if (!controller.parent.isValid()) return;
int parent_controller_idx = m_controllers.find(controller.parent);
if (parent_controller_idx < 0) return;
Controller& parent_controller = m_controllers.at(parent_controller_idx);
if (!parent_controller.root) return;
ComponentHandle model_instance = m_render_scene->getModelInstanceComponent(controller.entity);
if (model_instance == INVALID_COMPONENT) return;
Pose* pose = m_render_scene->lockPose(model_instance);
if (!pose) return;
Model* model = m_render_scene->getModelInstanceModel(model_instance);
model->getPose(*pose);
pose->computeRelative(*model);
parent_controller.root->fillPose(m_anim_system.m_engine, *pose, *model, 1);
pose->computeAbsolute(*model);
m_render_scene->unlockPose(model_instance, true);
}
void updateController(Controller& controller, float time_delta)
{
if (!controller.resource->isReady())
{
LUMIX_DELETE(m_anim_system.m_allocator, controller.root);
controller.root = nullptr;
return;
}
if (!controller.root && !initControllerRuntime(controller)) return;
Anim::RunningContext rc;
rc.time_delta = time_delta;
rc.current = controller.root;
rc.allocator = &m_anim_system.m_allocator;
rc.input = &controller.input[0];
rc.anim_set = &controller.animations;
rc.event_stream = &m_event_stream;
rc.controller = {controller.entity.index};
controller.root = controller.root->update(rc, true);
ComponentHandle model_instance = m_render_scene->getModelInstanceComponent(controller.entity);
if (model_instance == INVALID_COMPONENT) return;
Pose* pose = m_render_scene->lockPose(model_instance);
if (!pose) return;
Model* model = m_render_scene->getModelInstanceModel(model_instance);
model->getPose(*pose);
pose->computeRelative(*model);
controller.root->fillPose(m_anim_system.m_engine, *pose, *model, 1);
pose->computeAbsolute(*model);
for (Controller::IK& ik : controller.inverse_kinematics)
{
if (ik.weight == 0) break;
updateIK(ik, *pose, *model, controller.entity);
}
m_render_scene->unlockPose(model_instance, true);
}
void updateIK(Controller::IK& ik, Pose& pose, Model& model, Entity& entity)
{
decltype(model.getBoneIndex(0)) bones_iters[Controller::IK::MAX_BONES_COUNT];
for (int i = 0; i < ik.bones_count; ++i)
{
bones_iters[i] = model.getBoneIndex(ik.bones[i]);
if (!bones_iters[i].isValid()) return;
}
int indices[Controller::IK::MAX_BONES_COUNT];
Vec3 pos[Controller::IK::MAX_BONES_COUNT];
float len[Controller::IK::MAX_BONES_COUNT - 1];
float len_sum = 0;
for (int i = 0; i < ik.bones_count; ++i)
{
indices[i] = bones_iters[i].value();
pos[i] = pose.positions[indices[i]];
if (i > 0)
{
len[i - 1] = (pos[i] - pos[i - 1]).length();
len_sum += len[i - 1];
}
}
Vec3 target = ik.target;
Vec3 to_target = target - pos[0];
if (len_sum * len_sum < to_target.squaredLength()) {
to_target.normalize();
target = pos[0] + to_target * len_sum;
}
for (int iteration = 0; iteration < ik.max_iterations; ++iteration)
{
pos[ik.bones_count - 1] = target;
// backward
for (int i = ik.bones_count - 1; i > 0; --i)
{
Vec3 dir = (pos[i - 1] - pos[i]).normalized();
pos[i - 1] = pos[i] + dir * len[i - 1];
}
// backward
for (int i = 1; i < ik.bones_count; ++i)
{
Vec3 dir = (pos[i] - pos[i - 1]).normalized();
pos[i] = pos[i - 1] + dir * len[i - 1];
}
}
for (int i = 0; i < ik.bones_count; ++i)
{
if (i < ik.bones_count - 1)
{
Vec3 old_d = pose.positions[indices[i + 1]] - pose.positions[indices[i]];
Vec3 new_d = pos[i + 1] - pos[i];
old_d.normalize();
new_d.normalize();
Quat rel_rot = Quat::vec3ToVec3(old_d, new_d);
pose.rotations[indices[i]] = rel_rot * pose.rotations[indices[i]];
}
pose.positions[indices[i]] = pos[i];
}
}
void updateAnimables(float time_delta)
{
if (m_animables.size() == 0) return;
IAllocator& allocator = m_engine.getAllocator();
JobSystem::LambdaJob jobs[16];
int job_count = Math::minimum(lengthOf(jobs), m_animables.size());
ASSERT(job_count > 0);
volatile int counter = 0;
for (int i = 0; i < job_count; ++i)
{
JobSystem::fromLambda([time_delta, this, i, job_count]() {
PROFILE_BLOCK("Animate Job");
int all_count = m_animables.size();
int batch_count = all_count / job_count;
if (i == job_count - 1) batch_count = all_count - ((job_count - 1) * batch_count);
for (int j = 0; j < batch_count; ++j)
{
Animable& animable = m_animables.at(j + i * all_count / job_count);
AnimationSceneImpl::updateAnimable(animable, time_delta);
}
}, &jobs[i], nullptr);
}
JobSystem::runJobs(jobs, job_count, &counter);
JobSystem::waitOutsideJob(&counter);
}
void update(float time_delta, bool paused) override
{
PROFILE_FUNCTION();
if (!m_is_game_running) return;
if (paused) return;
m_event_stream.clear();
updateAnimables(time_delta);
for (Controller& controller : m_controllers)
{
AnimationSceneImpl::updateController(controller, time_delta);
}
for (SharedController& controller : m_shared_controllers)
{
AnimationSceneImpl::updateSharedController(controller, time_delta);
}
processEventStream();
}
void processEventStream()
{
InputBlob blob(m_event_stream);
u32 set_input_type = crc32("set_input");
while (blob.getPosition() < blob.getSize())
{
u32 type;
u8 size;
ComponentHandle cmp;
blob.read(type);
blob.read(cmp);
blob.read(size);
if (type == set_input_type)
{
Anim::SetInputEvent event;
blob.read(event);
Controller& ctrl = m_controllers.get({cmp.index});
if (ctrl.resource->isReady())
{
Anim::InputDecl& decl = ctrl.resource->m_input_decl;
Anim::InputDecl::Input& input = decl.inputs[event.input_idx];
switch (input.type)
{
case Anim::InputDecl::BOOL: *(bool*)&ctrl.input[input.offset] = event.b_value; break;
case Anim::InputDecl::INT: *(int*)&ctrl.input[input.offset] = event.i_value; break;
case Anim::InputDecl::FLOAT: *(float*)&ctrl.input[input.offset] = event.f_value; break;
default: ASSERT(false); break;
}
}
}
else
{
blob.skip(size);
}
}
}
Animation* loadAnimation(const Path& path)
{
ResourceManager& rm = m_engine.getResourceManager();
return static_cast<Animation*>(rm.get(ANIMATION_TYPE)->load(path));
}
Anim::ControllerResource* loadController(const Path& path)
{
ResourceManager& rm = m_engine.getResourceManager();
return static_cast<Anim::ControllerResource*>(rm.get(CONTROLLER_RESOURCE_TYPE)->load(path));
}
ComponentHandle createAnimable(Entity entity)
{
Animable& animable = m_animables.insert(entity);
animable.time = 0;
animable.animation = nullptr;
animable.entity = entity;
animable.time_scale = 1;
animable.start_time = 0;
ComponentHandle cmp = {entity.index};
m_universe.addComponent(entity, ANIMABLE_TYPE, this, cmp);
return cmp;
}
ComponentHandle createController(Entity entity)
{
Controller& controller = m_controllers.emplace(entity, m_anim_system.m_allocator);
controller.entity = entity;
ComponentHandle cmp = {entity.index};
m_universe.addComponent(entity, CONTROLLER_TYPE, this, cmp);
return cmp;
}
ComponentHandle createSharedController(Entity entity)
{
m_shared_controllers.insert(entity, {entity, INVALID_ENTITY});
ComponentHandle cmp = {entity.index};
m_universe.addComponent(entity, SHARED_CONTROLLER_TYPE, this, cmp);
return cmp;
}
IPlugin& getPlugin() const override { return m_anim_system; }
Universe& m_universe;
AnimationSystemImpl& m_anim_system;
Engine& m_engine;
AssociativeArray<Entity, Animable> m_animables;
AssociativeArray<Entity, Controller> m_controllers;
AssociativeArray<Entity, SharedController> m_shared_controllers;
RenderScene* m_render_scene;
bool m_is_game_running;
OutputBlob m_event_stream;
};
AnimationSystemImpl::AnimationSystemImpl(Engine& engine)
: m_allocator(engine.getAllocator())
, m_engine(engine)
, m_animation_manager(m_allocator)
, m_controller_manager(m_allocator)
{
m_animation_manager.create(ANIMATION_TYPE, m_engine.getResourceManager());
m_controller_manager.create(CONTROLLER_RESOURCE_TYPE, m_engine.getResourceManager());
PropertyRegister::add("anim_controller",
LUMIX_NEW(m_allocator, ResourcePropertyDescriptor<AnimationSceneImpl>)("Source",
&AnimationSceneImpl::getControllerSource,
&AnimationSceneImpl::setControllerSource,
"Animation controller (*.act)",
CONTROLLER_RESOURCE_TYPE));
PropertyRegister::add("anim_controller", LUMIX_NEW(m_allocator, AnimSetPropertyDescriptor)("Default set"));
PropertyRegister::add("animable",
LUMIX_NEW(m_allocator, ResourcePropertyDescriptor<AnimationSceneImpl>)("Animation",
&AnimationSceneImpl::getAnimation,
&AnimationSceneImpl::setAnimation,
"Animation (*.ani)",
ANIMATION_TYPE));
PropertyRegister::add("animable",
LUMIX_NEW(m_allocator, DecimalPropertyDescriptor<AnimationSceneImpl>)(
"Start time", &AnimationSceneImpl::getAnimableStartTime, &AnimationSceneImpl::setAnimableStartTime, 0, FLT_MAX, 0.1f));
PropertyRegister::add("animable",
LUMIX_NEW(m_allocator, DecimalPropertyDescriptor<AnimationSceneImpl>)(
"Time scale", &AnimationSceneImpl::getAnimableTimeScale, &AnimationSceneImpl::setAnimableTimeScale, 0, FLT_MAX, 0.1f));
PropertyRegister::add("shared_anim_controller",
LUMIX_NEW(m_allocator, EntityPropertyDescriptor<AnimationSceneImpl>)(
"Parent", &AnimationSceneImpl::getSharedControllerParent, &AnimationSceneImpl::setSharedControllerParent));
registerLuaAPI();
}
AnimationSystemImpl::~AnimationSystemImpl()
{
m_animation_manager.destroy();
m_controller_manager.destroy();
}
void AnimationSystemImpl::registerLuaAPI()
{
lua_State* L = m_engine.getState();
#define REGISTER_FUNCTION(name) \
do {\
auto f = &LuaWrapper::wrapMethod<AnimationSceneImpl, decltype(&AnimationSceneImpl::name), &AnimationSceneImpl::name>; \
LuaWrapper::createSystemFunction(L, "Animation", #name, f); \
} while(false) \
REGISTER_FUNCTION(getAnimationLength);
REGISTER_FUNCTION(setControllerIntInput);
REGISTER_FUNCTION(setControllerBoolInput);
REGISTER_FUNCTION(setControllerFloatInput);
REGISTER_FUNCTION(getControllerInputIndex);
#undef REGISTER_FUNCTION
LuaWrapper::createSystemFunction(L, "Animation", "setIK", &AnimationSceneImpl::setIK); \
}
void AnimationSystemImpl::createScenes(Universe& ctx)
{
auto* scene = LUMIX_NEW(m_allocator, AnimationSceneImpl)(*this, m_engine, ctx, m_allocator);
ctx.addScene(scene);
}
void AnimationSystemImpl::destroyScene(IScene* scene) { LUMIX_DELETE(m_allocator, scene); }
LUMIX_PLUGIN_ENTRY(animation)
{
return LUMIX_NEW(engine.getAllocator(), AnimationSystemImpl)(engine);
}
}