diff --git a/include/Scene.h b/include/Scene.h index aecae3e..60d2571 100644 --- a/include/Scene.h +++ b/include/Scene.h @@ -5,6 +5,7 @@ #include "Model.h" #include "Camera.h" +#include "Animation.h" #include #include @@ -33,7 +34,9 @@ class Scene std::list nodes; // Список пустых узлов std::list models; // Список моделей для рендера std::list cameras; // Список камер - + + std::vector animations; // Список анимаций + std::map animation_names; // Имя анимации - индекс protected: void rebuld_tree(const Scene& from); // Перестройка дерева после копирования или присваивания template diff --git a/src/Scene.cpp b/src/Scene.cpp index fd9dc98..b46e664 100644 --- a/src/Scene.cpp +++ b/src/Scene.cpp @@ -20,7 +20,7 @@ Scene& Scene::operator=(const Scene& other) nodes = other.nodes; models = other.models; cameras = other.cameras; - + rebuld_tree(other); return *this; @@ -29,7 +29,7 @@ Scene& Scene::operator=(const Scene& other) // Рендер сцены void Scene::render(ShaderProgram &shaderProgram, UBO &material_buffer) { - for (auto & model : models) + for (auto & model : models) model.render(shaderProgram, material_buffer); } @@ -277,6 +277,55 @@ void collectGLTFnodes(int node_id, std::vector &nodes, tinygltf::Model &in_ collectGLTFnodes(child, nodes, in_model); } +float getFloatChannelOutput(int type, const void* array, int index) +{ + float result; + + switch (type) + { + case TINYGLTF_COMPONENT_TYPE_BYTE: + { + const char* bvalues = reinterpret_cast (array); + result = bvalues[index] / 127.0; + if (result < -1.0) + result = -1.0; + + break; + } + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: + { + const unsigned char* ubvalues = reinterpret_cast (array); + result = ubvalues[index] / 255.0; + + break; + } + case TINYGLTF_COMPONENT_TYPE_SHORT: + { + const short* svalues = reinterpret_cast (array); + result = svalues[index] / 32767.0; + if (result < -1.0) + result = -1.0; + + break; + } + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: + { + const unsigned short* usvalues = reinterpret_cast(array); + result = usvalues[index] / 65535.0; + + break; + } + default: + { + const float* fvalues = reinterpret_cast (array); + result = fvalues[index]; + } + + } + + return result; +} + Scene loadGLTFtoScene(std::string filename) { Scene result; @@ -302,7 +351,7 @@ Scene loadGLTFtoScene(std::string filename) auto &buffer = in_model.buffers[bufferView.buffer]; BOs.push_back(BO((BUFFER_TYPE)bufferView.target, buffer.data.data() + bufferView.byteOffset, bufferView.byteLength)); } - + // Адрес директории для относительных путей изображений std::string dir = filename.substr(0, filename.find_last_of("/\\") + 1); // Загрузим используемые текстуры @@ -343,7 +392,7 @@ Scene loadGLTFtoScene(std::string filename) std::vector pNodes(in_model.nodes.size(), NULL); // Индексы родителей (-1 - корневой узел сцены) std::vector parents_id(in_model.nodes.size(), -1); - + // Цикл по сценам for (auto &scene : in_model.scenes) { @@ -488,7 +537,7 @@ Scene loadGLTFtoScene(std::string filename) } } } - + result.models.push_back(model); // Добавляем к сцене // Если ещё не сохранили if (!pNodes[node_id]) @@ -526,6 +575,116 @@ Scene loadGLTFtoScene(std::string filename) } } + // Цикл по анимациям + for (auto &in_animation : in_model.animations) + { + Animation animation; + + for (auto &in_channel : in_animation.channels) + { + Channel channel; + + channel.target = pNodes[in_channel.target_node]; // Анимируемый узел + // Анимируемый параметр + if (in_channel.target_path == "translation") + channel.path = POSITION; + else + if (in_channel.target_path == "rotation") + channel.path = ROTATION; + else + if (in_channel.target_path == "scale") + channel.path = SCALE; + else + throw std::runtime_error("Неподдерживаемый параметр анимации"); + + + // Получение сэмплера для канала + const auto& sampler = in_animation.samplers[in_channel.sampler]; + + // Тип интерполяции + if (sampler.interpolation == "LINEAR") + channel.interpolation = LINEAR; + else + if (sampler.interpolation == "STEP") + channel.interpolation = STEP; + else + if (sampler.interpolation == "CUBICSPLINE") + channel.interpolation = CUBICSPLINE; + else + throw std::runtime_error("Неподдерживаемый тип интерполяции"); + + // Получение временных меток ключевых кадров (Input Accessor) + const auto& inputAccessor = in_model.accessors[sampler.input]; + const auto& inputBufferView = in_model.bufferViews[inputAccessor.bufferView]; + const auto& inputBuffer = in_model.buffers[inputBufferView.buffer]; + const float* keyframeTimes = reinterpret_cast(&inputBuffer.data[inputBufferView.byteOffset + inputAccessor.byteOffset]); + // Скопируем через метод insert + channel.timestamps.insert(channel.timestamps.end(), keyframeTimes, keyframeTimes + inputAccessor.count); + + // Получение данных ключевых кадров (Output Accessor) + const auto& outputAccessor = in_model.accessors[sampler.output]; + const auto& outputBufferView = in_model.bufferViews[outputAccessor.bufferView]; + const auto& outputBuffer = in_model.buffers[outputBufferView.buffer]; + + // Зарезервируем место + channel.values.resize(inputAccessor.count); + if (channel.interpolation == CUBICSPLINE) + channel.tangents.resize(inputAccessor.count); + + // Проверим формат и запишем данные с учетом преобразования + if (( (channel.path == POSITION || channel.path == SCALE) + && outputAccessor.type == TINYGLTF_TYPE_VEC3) // == 3 + || ( channel.path == ROTATION + && outputAccessor.type == TINYGLTF_TYPE_VEC4) // == 4 + ) + { + // Цикл по ключевым кадрам + for (int keyframe = 0; keyframe < inputAccessor.count; keyframe++) + // Цикл по компонентам + for (int component = 0; component < outputAccessor.type; component++) + { + // Для CUBICSPLINE интерполяции требуется дополнительно списать касательные + if (channel.interpolation == CUBICSPLINE) + { + if (channel.path == ROTATION) + { + channel.tangents[keyframe].in. quat[component] = getFloatChannelOutput(outputAccessor.componentType, &outputBuffer.data[outputBufferView.byteOffset + outputAccessor.byteOffset], keyframe*outputAccessor.type*3 + component); + channel.values [keyframe]. quat[component] = getFloatChannelOutput(outputAccessor.componentType, &outputBuffer.data[outputBufferView.byteOffset + outputAccessor.byteOffset], keyframe*outputAccessor.type*3 + outputAccessor.type + component); + channel.tangents[keyframe].out.quat[component] = getFloatChannelOutput(outputAccessor.componentType, &outputBuffer.data[outputBufferView.byteOffset + outputAccessor.byteOffset], keyframe*outputAccessor.type*3 + outputAccessor.type*2 + component); + } + else + { + channel.tangents[keyframe].in. vec3[component] = getFloatChannelOutput(outputAccessor.componentType, &outputBuffer.data[outputBufferView.byteOffset + outputAccessor.byteOffset], keyframe*outputAccessor.type*3 + component); + channel.values [keyframe]. vec3[component] = getFloatChannelOutput(outputAccessor.componentType, &outputBuffer.data[outputBufferView.byteOffset + outputAccessor.byteOffset], keyframe*outputAccessor.type*3 + outputAccessor.type + component); + channel.tangents[keyframe].out.vec3[component] = getFloatChannelOutput(outputAccessor.componentType, &outputBuffer.data[outputBufferView.byteOffset + outputAccessor.byteOffset], keyframe*outputAccessor.type*3 + outputAccessor.type*2 + component); + } + } + else + if (channel.path == ROTATION) + channel.values [keyframe]. quat[component] = getFloatChannelOutput(outputAccessor.componentType, &outputBuffer.data[outputBufferView.byteOffset + outputAccessor.byteOffset], keyframe*outputAccessor.type + component); + else + channel.values [keyframe]. vec3[component] = getFloatChannelOutput(outputAccessor.componentType, &outputBuffer.data[outputBufferView.byteOffset + outputAccessor.byteOffset], keyframe*outputAccessor.type + component); + } + } + else + throw std::runtime_error("Неподдерживаемые данные анимации"); + + animation.channels.push_back(channel); + } + + // Имя анимации + // Если имени нет - сгенерируем + if (in_animation.name.empty()) + { + std::string name = filename + std::to_string(result.animations.size()); + result.animation_names[name] = result.animations.size(); + } + else + result.animation_names[in_animation.name] = result.animations.size(); + + result.animations.push_back(animation); + } + // Зададим трансформацию и родителей для узлов // Цикл по всем индексам узлов for (int node_id = 0; node_id < in_model.nodes.size(); node_id++)