Класс анимации и её каналов

This commit is contained in:
parent f71502a861
commit a0a19fee55
2 changed files with 211 additions and 0 deletions

74
include/Animation.h Normal file
View File

@ -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

137
src/Animation.cpp Normal file
View File

@ -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);
}
}