Skip to content

Instantly share code, notes, and snippets.

@ArnCarveris
Last active June 28, 2019 05:48
Show Gist options
  • Save ArnCarveris/494eaba3bf396ecedaf3a1674fb7a2bf to your computer and use it in GitHub Desktop.
Save ArnCarveris/494eaba3bf396ecedaf3a1674fb7a2bf to your computer and use it in GitHub Desktop.
#pragma once
#include <vector>
#include <entt/entity/registry.hpp>
namespace entt
{
template<typename Entity>
class Animation;
struct AnimationCurve final {
using fn_type = void(*)(const float&, const std::vector<float>&, float&);
fn_type function;
std::vector<float> data;
};
template<typename Component>
struct AnimationFunctor final {
using trail_fn_type = void(*)(Registry<Entity> &, const Entity&, const int&, const Component*, Component&);
using interpolator_fn_type = void(*)(const float&, const Component&, const Component&, Component&);
trail_fn_type trail;
interpolator_fn_type interpolator;
};
template<typename Component>
struct AnimationData final {
AnimationFunctor<Component> functor;
Component default;
std::vector<Component> frame;
std::vector<float> time;
std::vector<AnimationCurve> curve;
};
struct AnimationState final {
float elapsed{ 0.0f };
bool loop{ false };
};
struct AnimationFrame final {
int trail{ 0 };
size_t from{ 0 };
size_t to{ 0 };
float progress{ 0.0f };
};
template<typename Component>
struct AnimationFrameState final {
AnimationFrame frame;
std::shared_ptr<void> data;
};
/*! @brief TODO */
template<typename Entity>
class AnimationRegistry final {
friend class Animation<Entity>;
using component_type = typename Registry<Entity>::component_type;
using search_fn_type = bool(*)(const float&, const float*, const float*, size_t&);
using recalc_fn_type = void(*)(search_fn_type, Registry<Entity> &);
using apply_fn_type = void(*)(Registry<Entity> &);
template<typename Component>
struct Data {
AnimationFunctor<Component> functor;
};
struct Handler {
recalc_fn_type recalc;
apply_fn_type apply;
std::shared_ptr<void> data;
};
template<typename Component>
static void frame_recalc(search_fn_type search, const AnimationState& state, AnimationFrameState<Component>& target) {
auto* d = static_cast<AnimationData<Component>*>(target.data.get());
auto& time = d->time;
float progress;
float elapsed{ std::min(time.back(), state.elapsed) };
{
const float* first = &time.front();
const float* last = &time.back() - 1;
auto from = target.frame.from;
if (!search(elapsed, first, last, from)) {
from = time.size() - 2;
}
target.frame.trail = int(from) - int(target.frame.from);
target.frame.from = from;
target.frame.to = from + 1;
} {
auto& from = time[target.frame.from];
auto& to = time[target.frame.to];
progress = (elapsed - from) / (to - from);
} {
auto& curve = d->curve[target.frame.from];
curve.function(progress, curve.data, target.frame.progress);
}
}
template<typename Component, typename ...Args>
static void frame_recalc_view(search_fn_type search, Registry<Entity> &registry, Args&&... args) {
registry.view<AnimationState, AnimationFrameState<Component>>(std::forward<Args>(args)...).each([&](auto& entity, auto& state, auto& target) {
frame_recalc<Component>(search, state, target);
});
}
template<typename Component>
static void frame_apply(Registry<Entity> &registry, const Entity& entity, const AnimationFrameState<Component>& state, Component& target) {
auto* d = static_cast<AnimationData<Component>*>(state.data.get());
auto& frame = d->frame;
assert(frame.size() > 1);
assert(frame.size() > state.frame.from);
assert(frame.size() > state.frame.to);
auto& from = frame[state.frame.from];
auto& to = frame[state.frame.to];
d->functor.interpolator(state.frame.progress, from, to, target);
d->functor.trail(registry, entity, state.frame.trail, &from, target);
}
template<typename Component, typename ...Args>
static void frame_apply_view(Registry<Entity> &registry, Args&&... args) {
registry.view<AnimationFrameState<Component>, Component>(std::forward<Args>(args)...).each([&registry](auto& entity, auto& state, auto& target) {
frame_apply<Component>(registry, entity, state, target);
});
}
template<typename Component>
static void default_trail_fn(Registry<Entity> &, const Entity&, const int&, const Component*, Component&) {
}
template<typename Component>
static void stepped_interpolator_fn(const float&, const Component& from, const Component&, Component& target) {
target = from;
}
template<typename Component>
static void frame_recalc_view_fn(search_fn_type search, Registry<Entity> &registry) {
frame_recalc_view<Component>(search, registry);
}
template<typename Component>
static void frame_apply_view_fn(Registry<Entity> &registry) {
frame_apply_view<Component>(registry);
}
template<typename Component>
Handler& handler() {
auto cid = registry.type<Component>();
auto it = handlers.find(cid);
if (it == handlers.end())
{
auto data = std::make_shared<Data<Component>>();
data->functor.trail = &default_trail_fn<Component>;
data->functor.interpolator = &stepped_interpolator_fn<Component>;
it = handlers.emplace
(
cid,
Handler
{
&frame_recalc_view_fn<Component>,
&frame_apply_view_fn<Component>,
data
}
).first;
}
return it->second;
}
template<typename Component>
Data<Component>* data(const Handler& ref) {
return static_cast<Data<Component>*>(ref.data.get());
}
template<typename Component>
Data<Component>* data() {
return data<Component>(handler<Component>());
}
public:
/*! @brief TODO */
AnimationRegistry() = delete;
/*! @brief TODO */
AnimationRegistry(Registry<Entity> &registry)
: registry{ registry }
{}
/*! @brief TODO */
~AnimationRegistry() = default;
/*! @brief TODO */
AnimationRegistry(const AnimationRegistry &) = delete;
/*! @brief TODO */
AnimationRegistry(AnimationRegistry &&) = default;
/*! @brief TODO */
AnimationRegistry & operator=(const AnimationRegistry &) = delete;
/*! @brief TODO */
AnimationRegistry & operator=(AnimationRegistry &&) = default;
/*! @brief TODO */
template<typename Fn>
void searcher(Fn&& fn) {
search = fn;
}
/*! @brief TODO */
template<typename Component, typename Fn>
void trail(Fn&& fn) {
data<Component>()->functor.trail = fn;
}
/*! @brief TODO */
template<typename Component, typename Fn>
void interpolator(Fn&& fn) {
data<Component>()->functor.interpolator = fn;
}
/*! @brief TODO */
void select(const float& time) {
assert(time >= 0);
for (auto& state : registry.view<AnimationState>(raw_t{})) {
state.elapsed = time;
}
}
/*! @brief TODO */
void advance(const float& delta) {
assert(delta > 0);
for (auto& state : registry.view<AnimationState>(raw_t{})) {
state.elapsed += delta;
}
}
/*! @brief TODO */
void recalc() {
for (auto &handler : handlers) {
handler.second.recalc(search, registry);
}
}
/*! @brief TODO */
void apply() const {
for (auto &handler : handlers) {
handler.second.apply(registry);
}
}
std::unique_ptr<Animation<Entity>> create() {
return std::make_unique<Animation<Entity>>(*this);
}
private:
std::unordered_map<component_type, Handler> handlers;
Registry<Entity> &registry;
search_fn_type search{ nullptr };
};
/*! @brief TODO */
template<typename Entity>
class Animation final {
public:
/*! @brief TODO */
using registry_type = AnimationRegistry<Entity>;
private:
struct Handler;
using Frame = AnimationFrame;
using Curve = AnimationCurve;
template<typename Component>
using Data = AnimationData<Component>;
template<typename Component>
using FrameState = AnimationFrameState<Component>;
using State = AnimationState;
using component_type = typename Registry<Entity>::component_type;
using fn_type = void(*)(const Handler&, registry_type&, const Entity&);
using recalc_fn_type = void(*)(const Handler&, registry_type&, const Entity&, const State&);
struct Handler {
fn_type set;
fn_type reset;
fn_type unset;
fn_type assign;
fn_type accommodate;
fn_type remove;
recalc_fn_type recalc;
fn_type apply;
std::shared_ptr<void> data;
};
static void stepped_curve_fn(const float& in, const std::vector<float>&, float& out) {
out = in;
}
template<typename Component>
static void default_noset_fn(const Handler& handler, registry_type&registry, const Entity& entity) {
}
template<typename Component>
static void frame_set_fn(const Handler& handler, registry_type&registry, const Entity& entity) {
registry.registry.assign<FrameState<Component>>(entity).data = handler.data;
}
template<typename Component>
static void frame_reset_fn(const Handler& handler, registry_type&registry, const Entity& entity) {
registry.registry.accommodate<FrameState<Component>>(entity).data = handler.data;
}
template<typename Component>
static void frame_unset_fn(const Handler& handler, registry_type&registry, const Entity& entity) {
registry.registry.remove<FrameState<Component>>(entity);
}
template<typename Component>
static void assign_fn(const Handler& handler, registry_type&registry, const Entity& entity) {
auto* data = static_cast<Data<Component>*>(handler.data.get());
registry.registry.assign<Component>(entity, data->default);
}
template<typename Component>
static void accommodate_fn(const Handler& handler, registry_type&registry, const Entity& entity) {
auto* data = static_cast<Data<Component>*>(handler.data.get());
registry.registry.accommodate<Component>(entity, data->default);
}
template<typename Component>
static void remove_fn(const Handler& handler, registry_type&registry, const Entity& entity) {
registry.registry.remove<Component>(entity);
}
template<typename Component>
static void default_recalc_fn(const Handler&, registry_type&, const Entity&, const State&) {
}
template<typename Component>
static void frame_recalc_fn(const Handler& handler, registry_type&registry, const Entity& entity, const State& state) {
registry_type::frame_recalc<Component>(registry.search, state, registry.registry.get<FrameState<Component>>(entity));
}
template<typename Component>
static void default_apply_fn(const Handler& handler, registry_type&registry, const Entity& entity) {
}
template<typename Component>
static void frame_apply_fn(const Handler& handler, registry_type&registry, const Entity& entity) {
registry_type::frame_apply<Component>(registry.registry, entity, registry.registry.get<FrameState<Component>>(entity), registry.registry.get<Component>(entity));
}
template<typename Component>
Handler& handler() {
auto cid = registry.registry.template type<Component>();
auto it = handlers.find(cid);
if (it == handlers.end())
{
auto data = std::make_shared<Data<Component>>();
data->functor = registry.data<Component>()->functor;
it = handlers.emplace
(
cid,
Handler
{
&default_noset_fn<Component>,
&default_noset_fn<Component>,
&default_noset_fn<Component>,
&assign_fn<Component>,
&accommodate_fn<Component>,
&remove_fn<Component>,
nullptr,
nullptr,
data
}
).first;
}
return it->second;
}
template<typename Component>
Data<Component>* data(const Handler& ref) {
return static_cast<Data<Component>*>(ref.data.get());
}
template<typename Component>
Data<Component>* data() {
return data<Component>(handler<Component>());
}
public:
/*! @brief TODO */
using entity_type = Entity;
/*! @brief TODO */
using size_type = std::size_t;
/*! @brief TODO */
using curve_type = Curve;
/*! @brief TODO */
using state_type = State;
/*! @brief TODO */
Animation() = delete;
/*! @brief TODO */
Animation(registry_type &registry)
: registry{ registry }
{}
/*! @brief TODO */
~Animation() = default;
/*! @brief TODO */
Animation(const Animation &) = delete;
/*! @brief TODO */
Animation(Animation &&registry) = default;
/*! @brief TODO */
Animation & operator=(const Animation &) = delete;
/*! @brief TODO */
Animation & operator=(Animation &&) = default;
/*! @brief TODO */
template<typename Component, typename Fn>
void trail(Fn&& fn) {
data<Component>()->functor.trail = fn;
}
/*! @brief TODO */
template<typename Component, typename Fn>
void interpolator(Fn&& fn) {
data<Component>()->functor.interpolator = fn;
}
/*! @brief TODO */
template<typename Component, typename... Args>
Component & default(Args &&... args) {
auto& h = handler<Component>();
auto* d = data<Component>(h);
h.recalc = &default_recalc_fn<Component>;
h.apply = &default_apply_fn<Component>;
d->default = Component{ std::forward<Args>(args)... };
return d->default;
}
/*! @brief TODO */
template<typename Component>
Component & frame(const float& time) {
return frame<Component>(time, curve_type{ &stepped_curve_fn,{} });
}
/*! @brief TODO */
template<typename Component, typename... Args>
inline std::enable_if_t<(sizeof...(Args) > 0), Component &>
frame(const float& time, Args &&... args) {
return frame<Component>(time, curve_type{ &stepped_curve_fn,{} }, std::forward<Args>(args)...);
}
/*! @brief TODO */
template<typename Component>
Component & frame(const float& time, curve_type&& curve) {
return frame<Component>(time, std::move(curve), data<Component>()->default);
}
/*! @brief TODO */
template<typename Component, typename... Args>
inline std::enable_if_t<(sizeof...(Args) > 0), Component &>
frame(const float& time, curve_type&& curve, Args &&... args) {
auto& h = handler<Component>();
auto* d = data<Component>(h);
assert(curve.function);
assert(d->time.empty() || d->time.back() < time);
d->time.emplace_back(time);
d->curve.emplace_back(std::move(curve));
d->frame.emplace_back(std::forward<Args>(args)...);
if (d->frame.size() > 1) {
h.set = &frame_set_fn<Component>;
h.reset = &frame_reset_fn<Component>;
h.unset = &frame_unset_fn<Component>;
h.recalc = &frame_recalc_fn<Component>;
h.apply = &frame_apply_fn<Component>;
}
else {
h.set = &default_noset_fn<Component>;
h.reset = &default_noset_fn<Component>;
h.unset = &default_noset_fn<Component>;
h.recalc = &default_recalc_fn<Component>;
h.apply = &default_apply_fn<Component>;
}
length = std::max(length, time);
return d->frame.back();
}
/*! @brief TODO */
template<typename Component>
void remove() ENTT_NOEXCEPT {
handlers.erase(registry.template type<Component>())
}
/*! @brief TODO */
void set(const entity_type& entity) {
registry.registry.assign<State>(entity);
for (auto &handler : handlers) {
handler.second.set(handler.second, registry, entity);
}
}
/*! @brief TODO */
void reset(const entity_type& entity) {
registry.registry.accommodate<State>(entity);
for (auto &handler : handlers) {
handler.second.reset(handler.second, registry, entity);
}
}
/*! @brief TODO */
void unset(const entity_type& entity) {
registry.registry.remove<State>(entity);
for (auto &handler : handlers) {
handler.second.unset(handler.second, registry, entity);
}
}
/*! @brief TODO */
void assign(const entity_type& entity) {
for (auto &handler : handlers) {
handler.second.assign(handler.second, registry, entity);
}
}
/*! @brief TODO */
void accommodate(const entity_type& entity) {
for (auto &handler : handlers) {
handler.second.accommodate(handler.second, registry, entity);
}
}
/*! @brief TODO */
void remove(const entity_type& entity) ENTT_NOEXCEPT {
for (auto &handler : handlers) {
handler.second.remove(handler.second, registry, entity);
}
}
/*! @brief TODO */
void update(const float& progress, const entity_type& entity) {
assert(progress >= 0.0);
assert(progress <= 1.0f);
auto& state = registry.registry.get<State>(entity);
state.elapsed = progress * length;
}
/*! @brief TODO */
void select(const float& time, const entity_type& entity) {
assert(time >= 0);
auto& state = registry.registry.get<State>(entity);
state.elapsed = time;
}
/*! @brief TODO */
void advance(const float& delta, const entity_type& entity) {
assert(delta > 0);
auto& state = registry.registry.get<State>(entity);
state.elapsed += delta;
}
void recalc(const entity_type& entity) {
auto& state = registry.registry.get<State>(entity);
for (auto &handler : handlers) {
handler.second.recalc(handler.second, registry, entity, state);
}
}
/*! @brief TODO */
void apply(const entity_type& entity) {
for (auto &handler : handlers) {
handler.second.apply(handler.second, registry, entity);
}
}
/*! @brief TODO */
const float& duration() const {
return length;
}
private:
std::unordered_map<component_type, Handler> handlers;
AnimationRegistry<Entity> &registry;
float length{ 0 };
};
}
#pragma once
#include "animation.hpp"
namespace entt
{
template<
typename ResourceID,
template<typename> class ResourceHandle,
typename Texture,
typename Rect,
typename ResourceManager,
typename AnimationRegistry,
typename EntityRegistry
>
void animation_test(ResourceManager& res_man, AnimationRegistry& anim_reg, EntityRegistry& entity_reg)
{
using texture_t = ResourceHandle<typename Texture::Resource>;
using rect_t = Rect;
auto obtain_texture = [&res_man](const char* id) {
return res_man.obtain<typename Texture::Resource>(ResourceID{ id });
};
auto linear_search = [](const float& time, const float* begin, const float* end, size_t& out) {
for (size_t i = 0; begin != end; ++i, ++begin) {
if (begin[0] >= time) {
out = i;
return true;
}
}
return false;
};
//sets algorithm for seraching frame index with given time
anim_reg.searcher(linear_search);
auto animation = anim_reg.create();
animation->default<rect_t>(0.0f, 0.0f, 102.0f, 180.0f);
animation->default<texture_t>(obtain_texture("castle_0.png"));
animation->frame<texture_t>(0.0f);
animation->frame<texture_t>(0.25f, obtain_texture("castle_1.png"));
animation->frame<texture_t>(0.50f, obtain_texture("castle_2.png"));
animation->frame<texture_t>(0.75f, obtain_texture("castle_3.png"));
animation->frame<texture_t>(1.0f);
auto e = entity_reg.create();
//prepares animation state for given entity
animation->set(e);
animation->assign(e);
//compute animation time for given entity
animation->update(0.0f, e);
animation->select(0.3f, e);
animation->advance(0.16f, e);
animation->advance(1.16f, e);
//recalculate animation state for each component
animation->recalc(e);
//apply each component with calculated animation state for given entity
animation->apply(e);
//removes animation from given entity
animation->unset(e);
animation->remove(e);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment