#include "Animation.h" #include // Выполнить анимацию для канала с учетом времени относительно начала 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(std::chrono::steady_clock::now() - begin_time).count() / 1000.0f; for (Channel & channel : channels) { // channel.interpolation = STEP; channel.process(dtime, endings); } }