LumixEngine/src/animation/animation_system.cpp

511 lines
11 KiB
C++
Raw Normal View History

2014-06-16 21:18:15 +02:00
#include "animation_system.h"
#include "animation/animation.h"
#include "core/base_proxy_allocator.h"
#include "core/blob.h"
2014-06-16 21:18:15 +02:00
#include "core/crc32.h"
#include "core/json_serializer.h"
#include "core/profiler.h"
2014-06-29 15:20:21 +02:00
#include "core/resource_manager.h"
#include "editor/asset_browser.h"
#include "editor/imgui/imgui.h"
2016-01-28 13:48:33 +01:00
#include "editor/property_grid.h"
#include "editor/studio_app.h"
#include "editor/world_editor.h"
2015-08-11 23:55:58 +02:00
#include "engine.h"
#include "engine/property_descriptor.h"
#include "engine/property_register.h"
2016-02-03 04:37:09 +01:00
#include "renderer/model.h"
#include "renderer/pose.h"
2015-08-17 23:45:26 +02:00
#include "renderer/render_scene.h"
2014-06-16 21:18:15 +02:00
#include "universe/universe.h"
2014-08-22 22:02:53 +02:00
2014-06-16 21:18:15 +02:00
namespace Lumix
{
static const uint32 RENDERABLE_HASH = crc32("renderable");
static const uint32 ANIMABLE_HASH = crc32("animable");
2014-06-16 21:18:15 +02:00
2015-07-23 23:17:51 +02:00
namespace FS
{
class FileSystem;
};
2015-07-23 23:17:51 +02:00
class Animation;
class Engine;
class JsonSerializer;
class Universe;
2014-06-16 21:18:15 +02:00
2016-01-28 13:48:33 +01:00
struct AnimationSceneImpl : public IScene
2015-07-23 23:17:51 +02:00
{
struct Animable
2014-06-16 21:18:15 +02:00
{
2015-07-23 23:17:51 +02:00
bool m_is_free;
2015-07-24 22:38:11 +02:00
ComponentIndex m_renderable;
2015-07-23 23:17:51 +02:00
float m_time;
class Animation* m_animation;
Entity m_entity;
};
2014-06-16 21:18:15 +02:00
2016-01-28 13:48:33 +01:00
AnimationSceneImpl(IPlugin& anim_system, Engine& engine, Universe& ctx, IAllocator& allocator)
2016-01-20 22:37:00 +01:00
: m_universe(ctx)
2015-07-23 23:17:51 +02:00
, m_engine(engine)
, m_anim_system(anim_system)
, m_animables(allocator)
{
m_is_game_running = false;
2015-08-12 22:57:22 +02:00
m_render_scene = nullptr;
uint32 hash = crc32("renderer");
2016-01-20 22:37:00 +01:00
for (auto* scene : ctx.getScenes())
2015-08-12 22:57:22 +02:00
{
if (crc32(scene->getPlugin().getName()) == hash)
{
m_render_scene = static_cast<RenderScene*>(scene);
break;
}
}
ASSERT(m_render_scene);
2015-07-24 22:38:11 +02:00
m_render_scene->renderableCreated()
.bind<AnimationSceneImpl, &AnimationSceneImpl::onRenderableCreated>(this);
2015-07-24 22:38:11 +02:00
m_render_scene->renderableDestroyed()
.bind<AnimationSceneImpl, &AnimationSceneImpl::onRenderableDestroyed>(this);
2015-07-23 23:17:51 +02:00
}
2014-06-16 21:18:15 +02:00
2015-07-25 00:09:11 +02:00
~AnimationSceneImpl()
2015-07-23 23:17:51 +02:00
{
for(auto& animable : m_animables)
{
if(animable.m_is_free) continue;
unloadAnimation(animable.m_animation);
}
2016-01-20 22:37:00 +01:00
m_render_scene = static_cast<RenderScene*>(m_universe.getScene(crc32("renderer")));
if (m_render_scene)
{
m_render_scene->renderableCreated()
.unbind<AnimationSceneImpl, &AnimationSceneImpl::onRenderableCreated>(this);
m_render_scene->renderableDestroyed()
.unbind<AnimationSceneImpl, &AnimationSceneImpl::onRenderableDestroyed>(this);
}
2015-07-23 23:17:51 +02:00
}
2014-06-16 21:18:15 +02:00
2015-07-19 18:37:31 +02:00
void startGame() override
{
m_is_game_running = true;
}
void stopGame() override
{
m_is_game_running = false;
}
2015-11-20 16:58:10 +01:00
Universe& getUniverse() override { return m_universe; }
2015-07-19 18:37:31 +02:00
2015-01-04 15:05:20 +01:00
ComponentIndex getComponent(Entity entity, uint32 type) override
{
ASSERT(ownComponentType(type));
for (int i = 0; i < m_animables.size(); ++i)
{
if (!m_animables[i].m_is_free && m_animables[i].m_entity == entity) return i;
}
return INVALID_COMPONENT;
}
2015-11-20 16:58:10 +01:00
bool ownComponentType(uint32 type) const override
2015-07-23 23:17:51 +02:00
{
return type == ANIMABLE_HASH;
}
2015-01-04 15:05:20 +01:00
2015-07-23 23:17:51 +02:00
2015-11-20 16:58:10 +01:00
ComponentIndex createComponent(uint32 type,
2015-07-24 08:42:35 +02:00
Entity entity) override
2015-07-23 23:17:51 +02:00
{
if (type == ANIMABLE_HASH)
{
2015-07-25 00:09:11 +02:00
return createAnimable(entity);
2015-07-23 23:17:51 +02:00
}
2015-07-24 22:38:11 +02:00
return INVALID_COMPONENT;
2015-07-23 23:17:51 +02:00
}
2014-06-16 21:18:15 +02:00
void unloadAnimation(Animation* animation)
{
if(!animation) return;
auto& rm = animation->getResourceManager();
auto* animation_manager = rm.get(ResourceManager::ANIMATION);
animation_manager->unload(*animation);
}
void destroyComponent(ComponentIndex component, uint32 type) override
2015-07-23 23:17:51 +02:00
{
if (type == ANIMABLE_HASH)
{
unloadAnimation(m_animables[component].m_animation);
2015-07-23 23:17:51 +02:00
m_animables[component].m_is_free = true;
m_universe.destroyComponent(m_animables[component].m_entity, type, this, component);
2015-07-23 23:17:51 +02:00
}
}
2014-06-16 21:18:15 +02:00
2015-11-20 16:58:10 +01:00
void serialize(OutputBlob& serializer) override
2015-07-23 23:17:51 +02:00
{
serializer.write((int32)m_animables.size());
2015-07-23 23:17:51 +02:00
for (int i = 0; i < m_animables.size(); ++i)
{
2015-07-24 22:38:11 +02:00
serializer.write(m_animables[i].m_entity);
2015-07-23 23:17:51 +02:00
serializer.write(m_animables[i].m_time);
serializer.write(m_animables[i].m_is_free);
serializer.writeString(
2016-01-07 13:33:52 +01:00
m_animables[i].m_animation ? m_animables[i].m_animation->getPath().c_str() : "");
2015-07-23 23:17:51 +02:00
}
}
2014-06-16 21:18:15 +02:00
2015-11-20 16:58:10 +01:00
void deserialize(InputBlob& serializer, int) override
2015-07-23 23:17:51 +02:00
{
int32 count;
2015-07-23 23:17:51 +02:00
serializer.read(count);
m_animables.resize(count);
for (int i = 0; i < count; ++i)
{
serializer.read(m_animables[i].m_entity);
2015-07-24 22:38:11 +02:00
ComponentIndex renderable =
2015-07-23 23:17:51 +02:00
m_render_scene->getRenderableComponent(m_animables[i].m_entity);
2015-07-24 22:38:11 +02:00
if (renderable >= 0)
2014-06-16 21:18:15 +02:00
{
2015-07-23 23:17:51 +02:00
m_animables[i].m_renderable = renderable;
2014-06-16 21:18:15 +02:00
}
2015-07-23 23:17:51 +02:00
serializer.read(m_animables[i].m_time);
serializer.read(m_animables[i].m_is_free);
char path[MAX_PATH_LENGTH];
2015-07-23 23:17:51 +02:00
serializer.readString(path, sizeof(path));
m_animables[i].m_animation = path[0] == '\0' ? nullptr : loadAnimation(Path(path));
2016-01-07 13:33:52 +01:00
m_universe.addComponent(m_animables[i].m_entity, ANIMABLE_HASH, this, i);
2015-07-23 23:17:51 +02:00
}
}
2014-06-16 21:18:15 +02:00
Path getAnimation(ComponentIndex cmp)
2015-07-23 23:17:51 +02:00
{
2015-07-31 02:42:25 +02:00
return m_animables[cmp].m_animation
? m_animables[cmp].m_animation->getPath()
: Path("");
2015-07-23 23:17:51 +02:00
}
void setAnimation(ComponentIndex cmp, const Path& path)
2015-07-23 23:17:51 +02:00
{
unloadAnimation(m_animables[cmp].m_animation);
2015-07-24 08:42:35 +02:00
m_animables[cmp].m_animation = loadAnimation(path);
m_animables[cmp].m_time = 0;
2015-07-23 23:17:51 +02:00
}
2014-07-31 23:45:57 +02:00
2014-08-21 01:22:57 +02:00
2016-01-28 13:48:33 +01:00
void updateAnimable(ComponentIndex cmp, float time_delta)
{
Animable& animable = m_animables[cmp];
if(!animable.m_is_free && animable.m_animation &&
animable.m_animation->isReady())
{
auto* pose = m_render_scene->getPose(animable.m_renderable);
if(!pose) return;
animable.m_animation->getPose(
animable.m_time,
*pose,
*m_render_scene->getRenderableModel(animable.m_renderable));
2016-02-03 04:37:09 +01:00
auto* model = m_render_scene->getRenderableModel(animable.m_renderable);
for (int i = 0; i < pose->getCount(); ++i)
{
if (model->getBone(i).parent_idx >= 0)
{
m_render_scene->addDebugLine(pose->getPositions()[i],
pose->getPositions()[model->getBone(i).parent_idx], 0xffffffff, 0);
m_render_scene->addDebugLine(model->getBone(i).position,
model->getBone(model->getBone(i).parent_idx).position, 0xfff00fff, 0);
}
}
2016-01-28 13:48:33 +01:00
float t = animable.m_time + time_delta;
float l = animable.m_animation->getLength();
while(t > l)
{
t -= l;
}
animable.m_time = t;
}
}
void update(float time_delta, bool paused) override
2015-07-23 23:17:51 +02:00
{
PROFILE_FUNCTION();
if (m_animables.empty()) return;
if (!m_is_game_running) return;
2015-07-23 23:17:51 +02:00
for (int i = 0, c = m_animables.size(); i < c; ++i)
{
2016-01-28 13:48:33 +01:00
updateAnimable(i, time_delta);
2015-07-23 23:17:51 +02:00
}
}
2014-08-17 17:55:34 +02:00
Animation* loadAnimation(const Path& path)
2015-07-23 23:17:51 +02:00
{
ResourceManager& rm = m_engine.getResourceManager();
return static_cast<Animation*>(rm.get(ResourceManager::ANIMATION)->load(path));
2015-07-23 23:17:51 +02:00
}
2014-08-21 01:22:57 +02:00
2015-07-24 22:38:11 +02:00
void onRenderableCreated(ComponentIndex cmp)
2015-07-23 23:17:51 +02:00
{
2015-07-24 22:38:11 +02:00
Entity entity = m_render_scene->getRenderableEntity(cmp);
for (int i = 0; i < m_animables.size(); ++i)
2015-07-23 23:17:51 +02:00
{
2015-07-24 22:38:11 +02:00
if (m_animables[i].m_entity == entity)
2014-08-21 01:22:57 +02:00
{
2015-07-24 22:38:11 +02:00
m_animables[i].m_renderable = cmp;
break;
}
}
}
void onRenderableDestroyed(ComponentIndex cmp)
{
Entity entity = m_render_scene->getRenderableEntity(cmp);
for (int i = 0; i < m_animables.size(); ++i)
{
if (m_animables[i].m_entity == entity)
{
m_animables[i].m_renderable = INVALID_COMPONENT;
break;
2014-08-21 01:22:57 +02:00
}
2015-07-23 23:17:51 +02:00
}
}
2014-08-21 01:22:57 +02:00
2015-07-25 00:09:11 +02:00
ComponentIndex createAnimable(Entity entity)
2015-07-23 23:17:51 +02:00
{
2015-07-25 19:33:19 +02:00
Animable* src = nullptr;
2015-07-23 23:17:51 +02:00
for (int i = 0, c = m_animables.size(); i < c; ++i)
{
if (m_animables[i].m_is_free)
2014-08-21 01:22:57 +02:00
{
2015-07-23 23:17:51 +02:00
src = &m_animables[i];
break;
2014-08-21 01:22:57 +02:00
}
2015-07-23 23:17:51 +02:00
}
2016-01-14 16:22:25 +01:00
Animable& animable = src ? *src : m_animables.emplace();
2015-07-23 23:17:51 +02:00
animable.m_time = 0;
animable.m_is_free = false;
2015-07-24 22:38:11 +02:00
animable.m_renderable = INVALID_COMPONENT;
2015-07-25 19:33:19 +02:00
animable.m_animation = nullptr;
2015-07-23 23:17:51 +02:00
animable.m_entity = entity;
2015-07-24 22:38:11 +02:00
ComponentIndex renderable =
m_render_scene->getRenderableComponent(entity);
if (renderable >= 0)
2015-07-23 23:17:51 +02:00
{
animable.m_renderable = renderable;
}
2015-07-25 00:09:11 +02:00
m_universe.addComponent(
2015-07-23 23:17:51 +02:00
entity, ANIMABLE_HASH, this, m_animables.size() - 1);
2015-07-25 00:09:11 +02:00
return m_animables.size() - 1;
2015-07-23 23:17:51 +02:00
}
2014-08-21 01:22:57 +02:00
2015-11-20 16:58:10 +01:00
IPlugin& getPlugin() const override { return m_anim_system; }
2014-08-21 01:22:57 +02:00
2015-07-23 23:17:51 +02:00
Universe& m_universe;
IPlugin& m_anim_system;
Engine& m_engine;
Array<Animable> m_animables;
RenderScene* m_render_scene;
bool m_is_game_running;
2015-07-23 23:17:51 +02:00
};
2014-08-21 01:22:57 +02:00
2015-07-23 23:17:51 +02:00
struct AnimationSystemImpl : public IPlugin
{
public:
AnimationSystemImpl(Engine& engine)
: m_allocator(engine.getAllocator())
, m_engine(engine)
, m_animation_manager(m_allocator)
2014-08-21 01:22:57 +02:00
{
PropertyRegister::registerComponentType("animable", "Animable");
PropertyRegister::add("animable", LUMIX_NEW(m_allocator, ResourcePropertyDescriptor<AnimationSceneImpl>)("Animation",
&AnimationSceneImpl::getAnimation,
&AnimationSceneImpl::setAnimation,
"Animation (*.ani)",
ResourceManager::ANIMATION,
m_allocator));
2015-07-23 23:17:51 +02:00
}
2014-08-21 01:22:57 +02:00
2016-01-20 22:37:00 +01:00
IScene* createScene(Universe& ctx) override
2015-07-23 23:17:51 +02:00
{
return LUMIX_NEW(m_allocator, AnimationSceneImpl)(
2015-08-12 22:57:22 +02:00
*this, m_engine, ctx, m_allocator);
2015-07-23 23:17:51 +02:00
}
2014-11-07 22:18:47 +01:00
2015-11-20 16:58:10 +01:00
void destroyScene(IScene* scene) override
2015-07-23 23:17:51 +02:00
{
LUMIX_DELETE(m_allocator, scene);
2015-07-23 23:17:51 +02:00
}
2014-08-21 01:22:57 +02:00
2014-08-17 17:55:34 +02:00
2015-11-20 16:58:10 +01:00
const char* getName() const override { return "animation"; }
2014-08-17 17:55:34 +02:00
2015-07-23 23:17:51 +02:00
2015-11-20 16:58:10 +01:00
bool create() override
2015-07-23 23:17:51 +02:00
{
m_animation_manager.create(ResourceManager::ANIMATION,
m_engine.getResourceManager());
return true;
}
2014-08-17 17:55:34 +02:00
2015-11-20 16:58:10 +01:00
void destroy() override {}
2014-08-17 17:55:34 +02:00
2016-01-28 15:19:56 +01:00
Lumix::IAllocator& m_allocator;
2015-07-23 23:17:51 +02:00
Engine& m_engine;
AnimationManager m_animation_manager;
2015-07-23 23:17:51 +02:00
private:
void operator=(const AnimationSystemImpl&);
AnimationSystemImpl(const AnimationSystemImpl&);
};
2014-08-17 17:55:34 +02:00
2016-01-28 13:48:33 +01:00
struct AssetBrowserPlugin : AssetBrowser::IPlugin
{
2016-01-28 13:48:33 +01:00
AssetBrowserPlugin(StudioApp& app)
: m_app(app)
{}
2016-01-28 13:48:33 +01:00
bool onGUI(Lumix::Resource* resource, Lumix::uint32 type) override
{
if(type == ResourceManager::ANIMATION)
{
2016-01-28 13:48:33 +01:00
auto* animation = static_cast<Animation*>(resource);
ImGui::LabelText("FPS", "%d", animation->getFPS());
ImGui::LabelText("Length", "%.3fs", animation->getLength());
ImGui::LabelText("Frames", "%d", animation->getFrameCount());
2016-01-28 13:48:33 +01:00
return true;
}
2016-01-28 13:48:33 +01:00
return false;
}
2016-01-28 13:48:33 +01:00
void onResourceUnloaded(Resource* resource) override
{
}
2016-01-28 13:48:33 +01:00
const char* getName() const override
{
return "Animation";
}
bool hasResourceManager(uint32 type) const override
{
return type == ResourceManager::ANIMATION;
}
uint32 getResourceType(const char* ext) override
{
if(compareString(ext, "ani") == 0) return ResourceManager::ANIMATION;
return 0;
}
StudioApp& m_app;
};
2016-01-28 13:48:33 +01:00
struct PropertyGridPlugin : PropertyGrid::IPlugin
{
PropertyGridPlugin(StudioApp& app)
: m_app(app)
{
m_is_playing = false;
m_time_scale = 1;
}
void onGUI(PropertyGrid& grid, Lumix::ComponentUID cmp) override
{
if(cmp.type != ANIMABLE_HASH) return;
auto* scene = static_cast<AnimationSceneImpl*>(cmp.scene);
auto& animable = scene->m_animables[cmp.index];
if(!animable.m_animation) return;
if(!animable.m_animation->isReady()) return;
2016-01-28 13:48:33 +01:00
ImGui::Checkbox("Play", &m_is_playing);
if(ImGui::SliderFloat("Time", &animable.m_time, 0, animable.m_animation->getLength()))
{
2016-01-28 13:48:33 +01:00
scene->updateAnimable(cmp.index, 0);
}
2016-01-28 13:48:33 +01:00
if(m_is_playing)
{
2016-01-28 13:48:33 +01:00
ImGui::InputFloat("Time scale", &m_time_scale, 0.1f, 1.0f);
m_time_scale = Math::maxValue(0.0f, m_time_scale);
float time_delta = m_app.getWorldEditor()->getEngine().getLastTimeDelta();
scene->updateAnimable(cmp.index, time_delta * m_time_scale);
}
2016-01-28 13:48:33 +01:00
}
2016-01-28 13:48:33 +01:00
StudioApp& m_app;
bool m_is_playing;
float m_time_scale;
};
2016-01-28 13:48:33 +01:00
extern "C" LUMIX_ANIMATION_API void setStudioApp(StudioApp& app)
{
auto& allocator = app.getWorldEditor()->getAllocator();
2016-01-28 13:48:33 +01:00
auto* ab_plugin = LUMIX_NEW(allocator, AssetBrowserPlugin)(app);
app.getAssetBrowser()->addPlugin(*ab_plugin);
2016-01-28 13:48:33 +01:00
auto* pg_plugin = LUMIX_NEW(allocator, PropertyGridPlugin)(app);
app.getPropertyGrid()->addPlugin(*pg_plugin);
}
2015-07-23 23:17:51 +02:00
extern "C" IPlugin* createPlugin(Engine& engine)
{
return LUMIX_NEW(engine.getAllocator(), AnimationSystemImpl)(engine);
2015-07-23 23:17:51 +02:00
}
2014-06-16 21:18:15 +02:00
} // ~namespace Lumix