Класс анимации и её каналов
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