Класс анимации и её каналов
This commit is contained in:
parent
f71502a861
commit
a0a19fee55
|
@ -0,0 +1,74 @@
|
|||
#ifndef ANIMATION_H
|
||||
#define ANIMATION_H
|
||||
|
||||
#include "Model.h"
|
||||
|
||||
#include <chrono> // Время
|
||||
|
||||
// Тип интерполяции
|
||||
enum INTERPOLIATION_TYPE
|
||||
{
|
||||
STEP,
|
||||
LINEAR,
|
||||
CUBICSPLINE
|
||||
};
|
||||
|
||||
// Анимируемый параметр
|
||||
enum TARGET_PATH
|
||||
{
|
||||
POSITION,
|
||||
ROTATION,
|
||||
SCALE
|
||||
};
|
||||
|
||||
// Поведение при завершении шкалы ключевых кадров
|
||||
enum ANIM_ENDINGS
|
||||
{
|
||||
STOP,
|
||||
TO_BEGIN,
|
||||
CYCLED
|
||||
};
|
||||
|
||||
union PARAMETER_TYPE
|
||||
{
|
||||
glm::quat quat;
|
||||
glm::vec3 vec3;
|
||||
};
|
||||
|
||||
// Касательные для кубической-сплайн интерполяции
|
||||
struct Tangents
|
||||
{
|
||||
PARAMETER_TYPE in;
|
||||
PARAMETER_TYPE out;
|
||||
};
|
||||
|
||||
// Канал анимации
|
||||
struct Channel
|
||||
{
|
||||
void process(float dtime, ANIM_ENDINGS endings); // Выполнить анимацию для канала с учетом времени относительно начала
|
||||
|
||||
class Node* target = NULL; // Анимируемый узел
|
||||
TARGET_PATH path = POSITION; // Анимируемый параметр
|
||||
|
||||
INTERPOLIATION_TYPE interpolation = STEP; // Тип интерполяции
|
||||
std::vector<float> timestamps; // Временные метки !ОБЯЗАТЕЛЬНО ОТСОРТИРОВАНЫ ПО ВОЗРАСТАНИЮ!
|
||||
std::vector<PARAMETER_TYPE> values; // Данные по параметру (ИНДЕКС СООТВЕТСТВУЕТ ВРЕМЕННЫМ МЕТКАМ)
|
||||
std::vector<Tangents> tangents; // Касательные для CUBICSPLINE
|
||||
};
|
||||
|
||||
// Класс анимации
|
||||
class Animation
|
||||
{
|
||||
public:
|
||||
void begin(); // Задает состояние анимации как начало через запоминание времени
|
||||
void end(); // Заканчивает выполнение анимации
|
||||
void process(); // Вычисляет анимацию для каждого канала на основании текущего времени
|
||||
bool isEnabled(); // Возвращает состояние анимации (вкл/выкл)
|
||||
std::vector<Channel> channels; // Каналы анимации
|
||||
ANIM_ENDINGS endings = CYCLED;
|
||||
private:
|
||||
std::chrono::steady_clock::time_point begin_time; // Время начала анимации
|
||||
bool enabled = false;
|
||||
};
|
||||
|
||||
#endif // ANIMATION_H
|
|
@ -0,0 +1,137 @@
|
|||
#include "Animation.h"
|
||||
|
||||
#include <GLM/gtx/compatibility.hpp>
|
||||
|
||||
// Выполнить анимацию для канала с учетом времени относительно начала
|
||||
void Channel::process(float dtime, ANIM_ENDINGS endings)
|
||||
{
|
||||
// Если указатель на узел не пустой и есть ключевые кадры
|
||||
if (target && timestamps.size() && timestamps.size() == values.size())
|
||||
{
|
||||
// Если анимация зациклена
|
||||
if (endings == CYCLED)
|
||||
{
|
||||
// Получаем общую длительность анимации
|
||||
float totalDuration = timestamps.back();
|
||||
// Обновляем dtime для зацикливания
|
||||
if (totalDuration > 0)
|
||||
{
|
||||
dtime = fmod(dtime, totalDuration);
|
||||
}
|
||||
}
|
||||
|
||||
// Итоговое значение параметра
|
||||
PARAMETER_TYPE parameterValue;
|
||||
// Если только один кадр, используем значение этого кадра
|
||||
if (timestamps.size() == 1)
|
||||
{
|
||||
// Применяем значение к целевому узлу
|
||||
parameterValue = values[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Поиск подходящих индексов для интерполяции
|
||||
size_t index1 = 0, index2 = 1;
|
||||
for (; index2 < timestamps.size(); ++index2)
|
||||
{
|
||||
if (timestamps[index2] >= dtime)
|
||||
{
|
||||
break;
|
||||
}
|
||||
index1 = index2;
|
||||
}
|
||||
|
||||
// Если время выходит за рамки последнего кадра, используем значение в зависимости от поведения при окончании анимации
|
||||
if (index2 == timestamps.size())
|
||||
{
|
||||
if (endings == STOP)
|
||||
parameterValue = values.back();
|
||||
else
|
||||
if (endings == TO_BEGIN)
|
||||
parameterValue = values.front();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Вычисляем коэффициент интерполяции
|
||||
float t = (dtime - timestamps[index1]) / (timestamps[index2] - timestamps[index1]);
|
||||
|
||||
// Выбор метода интерполяции и интерполяция
|
||||
switch (interpolation)
|
||||
{
|
||||
case STEP:
|
||||
parameterValue = values[index1];
|
||||
break;
|
||||
case LINEAR:
|
||||
if (path == ROTATION)
|
||||
parameterValue.quat = glm::slerp(values[index1].quat, values[index2].quat, t);
|
||||
else
|
||||
parameterValue.vec3 = glm::lerp(values[index1].vec3, values[index2].vec3, t);
|
||||
break;
|
||||
case CUBICSPLINE:
|
||||
float t2 = t*t;
|
||||
float t3 = t2*t;
|
||||
if (path == ROTATION)
|
||||
{
|
||||
// Обеспечение кратчайшего пути для интерполяции
|
||||
glm::quat q2Adjusted = values[index2]. quat;
|
||||
glm::quat t2Adjusted = tangents[index2].in. quat;
|
||||
if (glm::dot(values[index1].quat, values[index2].quat) < 0)
|
||||
{
|
||||
q2Adjusted = -q2Adjusted;
|
||||
t2Adjusted = -t2Adjusted;
|
||||
}
|
||||
parameterValue.quat = (2*t3 - 3*t2 + 1) * values[index1]. quat
|
||||
+ (t3 - 2*t2 + t) * tangents[index1].out.quat
|
||||
+ (-2*t3 + 3*t2) * q2Adjusted
|
||||
+ (t3 - t2) * t2Adjusted;
|
||||
parameterValue.quat = glm::normalize(parameterValue.quat);
|
||||
}
|
||||
else
|
||||
parameterValue.vec3 = (2*t3 - 3*t2 + 1) * values[index1]. vec3
|
||||
+ (t3 - 2*t2 + t) * tangents[index1].out.vec3
|
||||
+ (-2*t3 + 3*t2) * values[index2]. vec3
|
||||
+ (t3 - t2) * tangents[index2].in. vec3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (path == POSITION)
|
||||
target->e_position() = parameterValue.vec3;
|
||||
if (path == ROTATION)
|
||||
target->e_rotation() = parameterValue.quat;
|
||||
if (path == SCALE)
|
||||
target->e_scale() = parameterValue.vec3;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Задает состояние анимации как начало
|
||||
void Animation::begin()
|
||||
{
|
||||
begin_time = std::chrono::steady_clock::now();
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
// Заканчивает выполнение анимации
|
||||
void Animation::end()
|
||||
{
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
// Возвращает состояние анимации (вкл/выкл)
|
||||
bool Animation::isEnabled()
|
||||
{
|
||||
return enabled;
|
||||
}
|
||||
|
||||
// Выполняет анимацию для всех каналов
|
||||
void Animation::process()
|
||||
{
|
||||
float dtime = std::chrono::duration<float, std::milli>(std::chrono::steady_clock::now() - begin_time).count() / 1000.0f;
|
||||
for (Channel & channel : channels)
|
||||
{
|
||||
// channel.interpolation = STEP;
|
||||
channel.process(dtime, endings);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue