Класс анимации и её каналов
This commit is contained in:
		
							parent
							
								
									cae502b197
								
							
						
					
					
						commit
						c0055c378b
					
				
							
								
								
									
										74
									
								
								include/Animation.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								include/Animation.h
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										137
									
								
								src/Animation.cpp
									
									
									
									
									
										Normal 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); | ||||||
|  |     } | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user