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