reconstruct gltf loader

pull/2/head
ink-soul 2023-06-03 16:00:04 +08:00
parent f5096914ae
commit 93ceae3657
1 changed files with 543 additions and 216 deletions

View File

@ -432,148 +432,190 @@ void glTFModel::Texture::fromglTfImage(tinygltf::Image& gltfImage, std::string p
} }
/* void glTFModel::Model::loadImages(tinygltf::Model& gltfModel, vks::VulkanDevice* device, VkQueue transferQueue)
void VulkanglTFModel::loadTextures(tinygltf::Model& input)
{ {
textures.resize(input.textures.size()); for (tinygltf::Image& image : gltfModel.images) {
for (size_t i = 0; i < input.textures.size(); i++) { glTFModel::Texture texture;
textures[i].imageIndex = input.textures[i].source; texture.fromglTfImage(image, path, device, transferQueue);
textures.push_back(texture);
} }
// Create an empty texture to be used for empty material images
createEmptyTexture(transferQueue);
} }
void VulkanglTFModel::loadAnimations(tinygltf::Model& input)
void glTFModel::Model::loadAnimations(tinygltf::Model& gltfModel)
{ {
animations.resize(input.animations.size()); for (tinygltf::Animation& anim : gltfModel.animations) {
glTFModel::Animation animation{};
for (size_t i = 0; i < input.animations.size(); ++i) animation.name = anim.name;
{ if (anim.name.empty()) {
auto glTFAnimation = input.animations[i]; animation.name = std::to_string(animations.size());
animations[i].name = glTFAnimation.name;
//Samplers
animations[i].samplers.resize(glTFAnimation.samplers.size());
for (size_t j = 0; j < glTFAnimation.samplers.size(); ++j)
{
auto glTFSampler = glTFAnimation.samplers[j];
auto& dstSampler = animations[i].samplers[j];
dstSampler.interpolation = glTFSampler.interpolation;
// Read sampler keyframe input time values
{
const auto& accessor = input.accessors[glTFSampler.input];
const auto& bufferView = input.bufferViews[accessor.bufferView];
const auto& buffer = input.buffers[bufferView.buffer];
const void* dataPtr = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
const float* buf = static_cast<const float*>(dataPtr);
for (size_t index = 0; index < accessor.count; ++index)
{
dstSampler.inputs.push_back(buf[index]);
} }
// Adjust animation's start and end times
for (auto input : animations[i].samplers[j].inputs) // Samplers
for (auto& samp : anim.samplers) {
glTFModel::AnimationSampler sampler{};
if (samp.interpolation == "LINEAR") {
sampler.interpolation = AnimationSampler::InterpolationType::LINEAR;
}
if (samp.interpolation == "STEP") {
sampler.interpolation = AnimationSampler::InterpolationType::STEP;
}
if (samp.interpolation == "CUBICSPLINE") {
sampler.interpolation = AnimationSampler::InterpolationType::CUBICSPLINE;
}
// Read sampler input time values
{ {
if (input < animations[i].start) const tinygltf::Accessor& accessor = gltfModel.accessors[samp.input];
{ const tinygltf::BufferView& bufferView = gltfModel.bufferViews[accessor.bufferView];
animations[i].start = input; const tinygltf::Buffer& buffer = gltfModel.buffers[bufferView.buffer];
assert(accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT);
float* buf = new float[accessor.count];
memcpy(buf, &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(float));
for (size_t index = 0; index < accessor.count; index++) {
sampler.inputs.push_back(buf[index]);
}
delete[] buf;
for (auto input : sampler.inputs) {
if (input < animation.start) {
animation.start = input;
}; };
if (input > animations[i].end) if (input > animation.end) {
{ animation.end = input;
animations[i].end = input;
} }
} }
} }
// Read sampler keyframe output translate/rotate/scale values // Read sampler output T/R/S values
{ {
const auto& accessor = input.accessors[glTFSampler.output]; const tinygltf::Accessor& accessor = gltfModel.accessors[samp.output];
const auto& bufferView = input.bufferViews[accessor.bufferView]; const tinygltf::BufferView& bufferView = gltfModel.bufferViews[accessor.bufferView];
const auto& buffer = input.buffers[bufferView.buffer]; const tinygltf::Buffer& buffer = gltfModel.buffers[bufferView.buffer];
const void* dataPtr = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
switch (accessor.type) assert(accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT);
{
case TINYGLTF_TYPE_VEC3: switch (accessor.type) {
{ case TINYGLTF_TYPE_VEC3: {
const glm::vec3* buf = static_cast<const glm::vec3*>(dataPtr); glm::vec3* buf = new glm::vec3[accessor.count];
for (size_t index = 0; index < accessor.count; index++) memcpy(buf, &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(glm::vec3));
{ for (size_t index = 0; index < accessor.count; index++) {
dstSampler.outputsVec4.push_back(glm::vec4(buf[index], 0.0f)); sampler.outputsVec4.push_back(glm::vec4(buf[index], 0.0f));
} }
delete[] buf;
break; break;
} }
case TINYGLTF_TYPE_VEC4: case TINYGLTF_TYPE_VEC4: {
{ glm::vec4* buf = new glm::vec4[accessor.count];
const glm::vec4* buf = static_cast<const glm::vec4*>(dataPtr); memcpy(buf, &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(glm::vec4));
for (size_t index = 0; index < accessor.count; index++) for (size_t index = 0; index < accessor.count; index++) {
{ sampler.outputsVec4.push_back(buf[index]);
dstSampler.outputsVec4.push_back(buf[index]);
} }
delete[] buf;
break; break;
} }
default: default: {
{
std::cout << "unknown type" << std::endl; std::cout << "unknown type" << std::endl;
break; break;
} }
} }
} }
animation.samplers.push_back(sampler);
} }
animations[i].channels.resize(glTFAnimation.channels.size()); // Channels
for (size_t j = 0; j < glTFAnimation.channels.size(); ++j) for (auto& source : anim.channels) {
{ glTFModel::AnimationChannel channel{};
auto glTFChannel = glTFAnimation.channels[j];
auto& dstChannel = animations[i].channels[j]; if (source.target_path == "rotation") {
dstChannel.path = glTFChannel.target_path; channel.path = AnimationChannel::PathType::ROTATION;
dstChannel.samplerIndex = glTFChannel.sampler;
dstChannel.node = nodeFromIndex(glTFChannel.target_node);
} }
if (source.target_path == "translation") {
channel.path = AnimationChannel::PathType::TRANSLATION;
}
if (source.target_path == "scale") {
channel.path = AnimationChannel::PathType::SCALE;
}
if (source.target_path == "weights") {
std::cout << "weights not yet supported, skipping channel" << std::endl;
continue;
}
channel.samplerIndex = source.sampler;
channel.node = nodeFromIndex(source.target_node);
if (!channel.node) {
continue;
}
animation.channels.push_back(channel);
}
animations.push_back(animation);
} }
} }
*/
/*
void VulkanglTFModel::loadMaterials(tinygltf::Model& input)
{
materials.resize(input.materials.size());
for (size_t i = 0; i < input.materials.size(); i++) {
// We only read the most basic properties required for our sample
tinygltf::Material glTFMaterial = input.materials[i];
// Get the base color factor
if (glTFMaterial.values.find("baseColorFactor") != glTFMaterial.values.end()) {
materials[i].baseColorFactor = glm::make_vec4(glTFMaterial.values["baseColorFactor"].ColorFactor().data());
}
// Get base color texture index
if (glTFMaterial.values.find("baseColorTexture") != glTFMaterial.values.end()) {
materials[i].baseColorTextureIndex = glTFMaterial.values["baseColorTexture"].TextureIndex();
}
if (glTFMaterial.values.find("metallicRoughnessTexture") != glTFMaterial.values.end()) {
materials[i].matalicRoughTextureIndex = glTFMaterial.values["metallicRoughnessTexture"].TextureIndex();
}
if (glTFMaterial.additionalValues.find("normalTexture") != glTFMaterial.additionalValues.end())
{
materials[i].normalMapTextureIndex = glTFMaterial.additionalValues["normalTexture"].TextureIndex();
}
if (glTFMaterial.emissiveTexture.index != -1)
{
materials[i].emissiveTextureIndex = glTFMaterial.emissiveTexture.index;
}
if (glTFMaterial.emissiveFactor.size() == 3)
{
materials[i].materialData.values.emissiveFactor = glm::make_vec3(glTFMaterial.emissiveFactor.data());
}
if (glTFMaterial.values.find("baseColorFactor") != glTFMaterial.values.end())
{
materials[i].materialData.values.baseColorFactor = glm::make_vec4(glTFMaterial.values["baseColorFactor"].ColorFactor().data());
}
}
}
*/
/* /*
glTF material glTF material
*/ */
void glTFModel::Model::loadMaterials(tinygltf::Model& gltfModel)
{
for (tinygltf::Material& mat : gltfModel.materials) {
glTFModel::Material material(device);
if (mat.values.find("baseColorTexture") != mat.values.end()) {
material.baseColorTexture = getTexture(gltfModel.textures[mat.values["baseColorTexture"].TextureIndex()].source);
}
// Metallic roughness workflow
if (mat.values.find("metallicRoughnessTexture") != mat.values.end()) {
material.metallicRoughnessTexture = getTexture(gltfModel.textures[mat.values["metallicRoughnessTexture"].TextureIndex()].source);
}
if (mat.values.find("roughnessFactor") != mat.values.end()) {
material.roughnessFactor = static_cast<float>(mat.values["roughnessFactor"].Factor());
}
if (mat.values.find("metallicFactor") != mat.values.end()) {
material.metallicFactor = static_cast<float>(mat.values["metallicFactor"].Factor());
}
if (mat.values.find("baseColorFactor") != mat.values.end()) {
material.baseColorFactor = glm::make_vec4(mat.values["baseColorFactor"].ColorFactor().data());
}
if (mat.additionalValues.find("normalTexture") != mat.additionalValues.end()) {
material.normalTexture = getTexture(gltfModel.textures[mat.additionalValues["normalTexture"].TextureIndex()].source);
}
else {
material.normalTexture = &emptyTexture;
}
if (mat.additionalValues.find("emissiveTexture") != mat.additionalValues.end()) {
material.emissiveTexture = getTexture(gltfModel.textures[mat.additionalValues["emissiveTexture"].TextureIndex()].source);
}
if (mat.additionalValues.find("occlusionTexture") != mat.additionalValues.end()) {
material.occlusionTexture = getTexture(gltfModel.textures[mat.additionalValues["occlusionTexture"].TextureIndex()].source);
}
if (mat.additionalValues.find("alphaMode") != mat.additionalValues.end()) {
tinygltf::Parameter param = mat.additionalValues["alphaMode"];
if (param.string_value == "BLEND") {
material.alphaMode = Material::ALPHAMODE_BLEND;
}
if (param.string_value == "MASK") {
material.alphaMode = Material::ALPHAMODE_MASK;
}
}
if (mat.additionalValues.find("alphaCutoff") != mat.additionalValues.end()) {
material.alphaCutoff = static_cast<float>(mat.additionalValues["alphaCutoff"].Factor());
}
materials.push_back(material);
}
// Push a default material at the end of the list for meshes with no material assigned
materials.push_back(Material(device));
}
void glTFModel::Material::createDescriptorSet(VkDescriptorPool descriptorPool, VkDescriptorSetLayout descriptorSetLayout, uint32_t descriptorBindingFlags) void glTFModel::Material::createDescriptorSet(VkDescriptorPool descriptorPool, VkDescriptorSetLayout descriptorSetLayout, uint32_t descriptorBindingFlags)
{ {
VkDescriptorSetAllocateInfo descriptorSetAllocInfo{}; VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
@ -903,7 +945,6 @@ void glTFModel::Model::loadNode(glTFModel::Node* parent, const tinygltf::Node& n
newNode->matrix = glm::mat4(1.0f); newNode->matrix = glm::mat4(1.0f);
newNode->parent = parent; newNode->parent = parent;
// Get the local node matrix // Get the local node matrix
// It's either made up from translation, rotation, scale or a 4x4 matrix // It's either made up from translation, rotation, scale or a 4x4 matrix
glm::vec3 translation = glm::vec3(0.0f); glm::vec3 translation = glm::vec3(0.0f);
@ -1112,7 +1153,294 @@ void glTFModel::Model::loadNode(glTFModel::Node* parent, const tinygltf::Node& n
linearNodes.push_back(newNode); linearNodes.push_back(newNode);
} }
VulkanglTFModel::Node* VulkanglTFModel::findNode(Node* parent, uint32_t index) // file loader function
void glTFModel::Model::loadFromFile(std::string filename, vks::VulkanDevice* device, VkQueue transferQueue, uint32_t fileLoadingFlags, float scale)
{
tinygltf::Model gltfModel;
tinygltf::TinyGLTF gltfContext;
if (fileLoadingFlags & FileLoadingFlags::DontLoadImages) {
gltfContext.SetImageLoader(loadImageDataFuncEmpty, nullptr);
}
else {
gltfContext.SetImageLoader(loadImageDataFunc, nullptr);
}
#if defined(__ANDROID__)
// On Android all assets are packed with the apk in a compressed form, so we need to open them using the asset manager
// We let tinygltf handle this, by passing the asset manager of our app
tinygltf::asset_manager = androidApp->activity->assetManager;
#endif
size_t pos = filename.find_last_of('/');
path = filename.substr(0, pos);
std::string error, warning;
this->device = device;
#if defined(__ANDROID__)
// On Android all assets are packed with the apk in a compressed form, so we need to open them using the asset manager
// We let tinygltf handle this, by passing the asset manager of our app
tinygltf::asset_manager = androidApp->activity->assetManager;
#endif
bool fileLoaded = gltfContext.LoadASCIIFromFile(&gltfModel, &error, &warning, filename);
std::vector<uint32_t> indexBuffer;
std::vector<Vertex> vertexBuffer;
if (fileLoaded) {
if (!(fileLoadingFlags & FileLoadingFlags::DontLoadImages)) {
loadImages(gltfModel, device, transferQueue);
}
loadMaterials(gltfModel);
const tinygltf::Scene& scene = gltfModel.scenes[gltfModel.defaultScene > -1 ? gltfModel.defaultScene : 0];
for (size_t i = 0; i < scene.nodes.size(); i++) {
const tinygltf::Node node = gltfModel.nodes[scene.nodes[i]];
loadNode(nullptr, node, scene.nodes[i], gltfModel, indexBuffer, vertexBuffer, scale);
}
if (gltfModel.animations.size() > 0) {
loadAnimations(gltfModel);
}
loadSkins(gltfModel);
for (auto node : linearNodes) {
// Assign skins
if (node->skinIndex > -1) {
node->skin = skins[node->skinIndex];
}
// Initial pose
if (node->mesh) {
node->update();
}
}
}
else {
// TODO: throw
vks::tools::exitFatal("Could not load glTF file \"" + filename + "\": " + error, -1);
return;
}
// Pre-Calculations for requested features
if ((fileLoadingFlags & FileLoadingFlags::PreTransformVertices) || (fileLoadingFlags & FileLoadingFlags::PreMultiplyVertexColors) || (fileLoadingFlags & FileLoadingFlags::FlipY)) {
const bool preTransform = fileLoadingFlags & FileLoadingFlags::PreTransformVertices;
const bool preMultiplyColor = fileLoadingFlags & FileLoadingFlags::PreMultiplyVertexColors;
const bool flipY = fileLoadingFlags & FileLoadingFlags::FlipY;
for (Node* node : linearNodes) {
if (node->mesh) {
const glm::mat4 localMatrix = node->getMatrix();
for (Primitive* primitive : node->mesh->primitives) {
for (uint32_t i = 0; i < primitive->vertexCount; i++) {
Vertex& vertex = vertexBuffer[primitive->firstVertex + i];
// Pre-transform vertex positions by node-hierarchy
if (preTransform) {
vertex.pos = glm::vec3(localMatrix * glm::vec4(vertex.pos, 1.0f));
vertex.normal = glm::normalize(glm::mat3(localMatrix) * vertex.normal);
}
// Flip Y-Axis of vertex positions
if (flipY) {
vertex.pos.y *= -1.0f;
vertex.normal.y *= -1.0f;
}
// Pre-Multiply vertex colors with material base color
if (preMultiplyColor) {
vertex.color = primitive->material.baseColorFactor * vertex.color;
}
}
}
}
}
}
for (auto extension : gltfModel.extensionsUsed) {
if (extension == "KHR_materials_pbrSpecularGlossiness") {
std::cout << "Required extension: " << extension;
metallicRoughnessWorkflow = false;
}
}
size_t vertexBufferSize = vertexBuffer.size() * sizeof(Vertex);
size_t indexBufferSize = indexBuffer.size() * sizeof(uint32_t);
indices.count = static_cast<uint32_t>(indexBuffer.size());
vertices.count = static_cast<uint32_t>(vertexBuffer.size());
assert((vertexBufferSize > 0) && (indexBufferSize > 0));
struct StagingBuffer {
VkBuffer buffer;
VkDeviceMemory memory;
} vertexStaging, indexStaging;
// Create staging buffers
// Vertex data
VK_CHECK_RESULT(device->createBuffer(
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
vertexBufferSize,
&vertexStaging.buffer,
&vertexStaging.memory,
vertexBuffer.data()));
// Index data
VK_CHECK_RESULT(device->createBuffer(
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
indexBufferSize,
&indexStaging.buffer,
&indexStaging.memory,
indexBuffer.data()));
// Create device local buffers
// Vertex buffer
VK_CHECK_RESULT(device->createBuffer(
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | memoryPropertyFlags,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
vertexBufferSize,
&vertices.buffer,
&vertices.memory));
// Index buffer
VK_CHECK_RESULT(device->createBuffer(
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | memoryPropertyFlags,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
indexBufferSize,
&indices.buffer,
&indices.memory));
// Copy from staging buffers
VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
VkBufferCopy copyRegion = {};
copyRegion.size = vertexBufferSize;
vkCmdCopyBuffer(copyCmd, vertexStaging.buffer, vertices.buffer, 1, &copyRegion);
copyRegion.size = indexBufferSize;
vkCmdCopyBuffer(copyCmd, indexStaging.buffer, indices.buffer, 1, &copyRegion);
device->flushCommandBuffer(copyCmd, transferQueue, true);
vkDestroyBuffer(device->logicalDevice, vertexStaging.buffer, nullptr);
vkFreeMemory(device->logicalDevice, vertexStaging.memory, nullptr);
vkDestroyBuffer(device->logicalDevice, indexStaging.buffer, nullptr);
vkFreeMemory(device->logicalDevice, indexStaging.memory, nullptr);
getSceneDimensions();
// Setup descriptors
uint32_t uboCount{ 0 };
uint32_t imageCount{ 0 };
for (auto node : linearNodes) {
if (node->mesh) {
uboCount++;
}
}
for (auto material : materials) {
if (material.baseColorTexture != nullptr) {
imageCount++;
}
}
std::vector<VkDescriptorPoolSize> poolSizes = {
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uboCount },
};
if (imageCount > 0) {
if (descriptorBindingFlags & DescriptorBindingFlags::ImageBaseColor) {
poolSizes.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageCount });
}
if (descriptorBindingFlags & DescriptorBindingFlags::ImageNormalMap) {
poolSizes.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageCount });
}
}
VkDescriptorPoolCreateInfo descriptorPoolCI{};
descriptorPoolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
descriptorPoolCI.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
descriptorPoolCI.pPoolSizes = poolSizes.data();
descriptorPoolCI.maxSets = uboCount + imageCount;
VK_CHECK_RESULT(vkCreateDescriptorPool(device->logicalDevice, &descriptorPoolCI, nullptr, &descriptorPool));
// Descriptors for per-node uniform buffers
{
// Layout is global, so only create if it hasn't already been created before
if (descriptorSetLayoutUbo == VK_NULL_HANDLE) {
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0),
};
VkDescriptorSetLayoutCreateInfo descriptorLayoutCI{};
descriptorLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptorLayoutCI.bindingCount = static_cast<uint32_t>(setLayoutBindings.size());
descriptorLayoutCI.pBindings = setLayoutBindings.data();
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device->logicalDevice, &descriptorLayoutCI, nullptr, &descriptorSetLayoutUbo));
}
for (auto node : nodes) {
prepareNodeDescriptor(node, descriptorSetLayoutUbo);
}
}
// Descriptors for per-material images
{
// Layout is global, so only create if it hasn't already been created before
if (descriptorSetLayoutImage == VK_NULL_HANDLE) {
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings{};
if (descriptorBindingFlags & DescriptorBindingFlags::ImageBaseColor) {
setLayoutBindings.push_back(vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, static_cast<uint32_t>(setLayoutBindings.size())));
}
if (descriptorBindingFlags & DescriptorBindingFlags::ImageNormalMap) {
setLayoutBindings.push_back(vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, static_cast<uint32_t>(setLayoutBindings.size())));
}
VkDescriptorSetLayoutCreateInfo descriptorLayoutCI{};
descriptorLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptorLayoutCI.bindingCount = static_cast<uint32_t>(setLayoutBindings.size());
descriptorLayoutCI.pBindings = setLayoutBindings.data();
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device->logicalDevice, &descriptorLayoutCI, nullptr, &descriptorSetLayoutImage));
}
for (auto& material : materials) {
if (material.baseColorTexture != nullptr) {
material.createDescriptorSet(descriptorPool, glTFModel::descriptorSetLayoutImage, descriptorBindingFlags);
}
}
}
}
void glTFModel::Model::bindBuffers(VkCommandBuffer commandBuffer)
{
const VkDeviceSize offsets[1] = { 0 };
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertices.buffer, offsets);
vkCmdBindIndexBuffer(commandBuffer, indices.buffer, 0, VK_INDEX_TYPE_UINT32);
buffersBound = true;
}
/*
helper function
*/
void glTFModel::Model::getNodeDimensions(Node* node, glm::vec3& min, glm::vec3& max)
{
if (node->mesh) {
for (Primitive* primitive : node->mesh->primitives) {
glm::vec4 locMin = glm::vec4(primitive->dimensions.min, 1.0f) * node->getMatrix();
glm::vec4 locMax = glm::vec4(primitive->dimensions.max, 1.0f) * node->getMatrix();
if (locMin.x < min.x) { min.x = locMin.x; }
if (locMin.y < min.y) { min.y = locMin.y; }
if (locMin.z < min.z) { min.z = locMin.z; }
if (locMax.x > max.x) { max.x = locMax.x; }
if (locMax.y > max.y) { max.y = locMax.y; }
if (locMax.z > max.z) { max.z = locMax.z; }
}
}
for (auto child : node->children) {
getNodeDimensions(child, min, max);
}
}
void glTFModel::Model::getSceneDimensions()
{
dimensions.min = glm::vec3(FLT_MAX);
dimensions.max = glm::vec3(-FLT_MAX);
for (auto node : nodes) {
getNodeDimensions(node, dimensions.min, dimensions.max);
}
dimensions.size = dimensions.max - dimensions.min;
dimensions.center = (dimensions.min + dimensions.max) / 2.0f;
dimensions.radius = glm::distance(dimensions.min, dimensions.max) / 2.0f;
}
glTFModel::Node* glTFModel::Model::findNode(Node* parent, uint32_t index)
{ {
Node* nodeFound = nullptr; Node* nodeFound = nullptr;
if (parent->index == index) if (parent->index == index)
@ -1130,7 +1458,7 @@ VulkanglTFModel::Node* VulkanglTFModel::findNode(Node* parent, uint32_t index)
return nodeFound; return nodeFound;
} }
VulkanglTFModel::Node* VulkanglTFModel::nodeFromIndex(uint32_t index) glTFModel::Node* glTFModel::Model::nodeFromIndex(uint32_t index)
{ {
Node* nodeFound = nullptr; Node* nodeFound = nullptr;
for (auto& node : nodes) for (auto& node : nodes)
@ -1144,67 +1472,72 @@ VulkanglTFModel::Node* VulkanglTFModel::nodeFromIndex(uint32_t index)
return nodeFound; return nodeFound;
} }
void VulkanglTFModel::updateAnimation(float deltaTime, vks::Buffer& buffer) /*
{
constexpr uint32_t activeAnimation = 0;
Animation& animation = animations[activeAnimation];
animation.currentTime += deltaTime;
if (animation.currentTime > animation.end)
{
animation.currentTime -= animation.end;
}
for (auto& channel : animation.channels) gltf update function
{
auto& sampler = animation.samplers[channel.samplerIndex]; */
for (size_t i = 0; i < sampler.inputs.size() - 1; ++i)
{
if (sampler.interpolation != "LINEAR") void glTFModel::Model::updateAnimation(uint32_t index, float time)
{ {
std::cout << "This sample only supports linear interpolations\n"; if (index > static_cast<uint32_t>(animations.size()) - 1) {
std::cout << "No animation with index " << index << std::endl;
return;
}
Animation& animation = animations[index];
bool updated = false;
for (auto& channel : animation.channels) {
glTFModel::AnimationSampler& sampler = animation.samplers[channel.samplerIndex];
if (sampler.inputs.size() > sampler.outputsVec4.size()) {
continue; continue;
} }
if ((animation.currentTime >= sampler.inputs[i]) && (animation.currentTime <= sampler.inputs[i + 1]))
{ for (auto i = 0; i < sampler.inputs.size() - 1; i++) {
float ratio = (animation.currentTime - sampler.inputs[i]) / (sampler.inputs[i + 1] - sampler.inputs[i]); if ((time >= sampler.inputs[i]) && (time <= sampler.inputs[i + 1])) {
if (channel.path == "translation") float u = std::max(0.0f, time - sampler.inputs[i]) / (sampler.inputs[i + 1] - sampler.inputs[i]);
{ if (u <= 1.0f) {
channel.node->translation = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], ratio); switch (channel.path) {
channel.node->bAnimateNode = true; case glTFModel::AnimationChannel::PathType::TRANSLATION: {
glm::vec4 trans = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], u);
channel.node->translation = glm::vec3(trans);
break;
} }
if (channel.path == "rotation") case glTFModel::AnimationChannel::PathType::SCALE: {
{ glm::vec4 trans = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], u);
channel.node->scale = glm::vec3(trans);
break;
}
case glTFModel::AnimationChannel::PathType::ROTATION: {
glm::quat q1; glm::quat q1;
q1.x = sampler.outputsVec4[i].x; q1.x = sampler.outputsVec4[i].x;
q1.y = sampler.outputsVec4[i].y; q1.y = sampler.outputsVec4[i].y;
q1.z = sampler.outputsVec4[i].z; q1.z = sampler.outputsVec4[i].z;
q1.w = sampler.outputsVec4[i].w; q1.w = sampler.outputsVec4[i].w;
glm::quat q2; glm::quat q2;
q2.x = sampler.outputsVec4[i + 1].x; q2.x = sampler.outputsVec4[i + 1].x;
q2.y = sampler.outputsVec4[i + 1].y; q2.y = sampler.outputsVec4[i + 1].y;
q2.z = sampler.outputsVec4[i + 1].z; q2.z = sampler.outputsVec4[i + 1].z;
q2.w = sampler.outputsVec4[i + 1].w; q2.w = sampler.outputsVec4[i + 1].w;
channel.node->rotation = glm::normalize(glm::slerp(q1, q2, u));
break;
}
}
updated = true;
}
}
}
}
if (updated) {
for (auto& node : nodes) {
node->update();
}
}
channel.node->rotation = glm::normalize(glm::slerp(q1, q2, ratio));
channel.node->bAnimateNode = true;
}
if (channel.path == "scale")
{
channel.node->scale = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], ratio);
channel.node->bAnimateNode = true;
}
}
}
}
std::vector<glm::mat4> nodeMatrics(nodeCount);
for (auto& node : nodes)
{
updateNodeMatrix(node, nodeMatrics);
}
buffer.copyTo(nodeMatrics.data(), nodeCount * sizeof(glm::mat4));
} }
/*
void VulkanglTFModel::updateNodeMatrix(Node* node, std::vector<glm::mat4>& nodeMatrics) void VulkanglTFModel::updateNodeMatrix(Node* node, std::vector<glm::mat4>& nodeMatrics)
{ {
nodeMatrics[node->index] = getNodeMatrix(node); nodeMatrics[node->index] = getNodeMatrix(node);
@ -1225,79 +1558,73 @@ glm::mat4 VulkanglTFModel::getNodeMatrix(Node* node)
} }
return nodeMatrix; return nodeMatrix;
} }
*/
void glTFModel::Model::prepareNodeDescriptor(glTFModel::Node* node, VkDescriptorSetLayout descriptorSetLayout) {
if (node->mesh) {
VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
descriptorSetAllocInfo.descriptorPool = descriptorPool;
descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayout;
descriptorSetAllocInfo.descriptorSetCount = 1;
VK_CHECK_RESULT(vkAllocateDescriptorSets(device->logicalDevice, &descriptorSetAllocInfo, &node->mesh->uniformBuffer.descriptorSet));
VkWriteDescriptorSet writeDescriptorSet{};
writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
writeDescriptorSet.descriptorCount = 1;
writeDescriptorSet.dstSet = node->mesh->uniformBuffer.descriptorSet;
writeDescriptorSet.dstBinding = 0;
writeDescriptorSet.pBufferInfo = &node->mesh->uniformBuffer.descriptor;
vkUpdateDescriptorSets(device->logicalDevice, 1, &writeDescriptorSet, 0, nullptr);
}
for (auto& child : node->children) {
prepareNodeDescriptor(child, descriptorSetLayout);
}
}
/* /*
glTF rendering functions glTF rendering functions
*/ */
// Draw a single node including child nodes (if present) void glTFModel::Model::drawNode(Node* node, VkCommandBuffer commandBuffer, uint32_t renderFlags, VkPipelineLayout pipelineLayout, uint32_t bindImageSet)
void VulkanglTFModel::drawNode(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, VulkanglTFModel::Node* node, bool bPushConstants)
{ {
if (node->mesh.primitives.size() > 0) { if (node->mesh) {
// Pass the node's matrix via push constants for (Primitive* primitive : node->mesh->primitives) {
// Traverse the node hierarchy to the top-most parent to get the final matrix of the current node bool skip = false;
glm::mat4 nodeMatrix = node->matrix; const glTFModel::Material& material = primitive->material;
VulkanglTFModel::Node* currentParent = node->parent; if (renderFlags & RenderFlags::RenderOpaqueNodes) {
while (currentParent) { skip = (material.alphaMode != Material::ALPHAMODE_OPAQUE);
nodeMatrix = currentParent->matrix * nodeMatrix;
currentParent = currentParent->parent;
} }
if (renderFlags & RenderFlags::RenderAlphaMaskedNodes) {
for (VulkanglTFModel::Primitive& primitive : node->mesh.primitives) { skip = (material.alphaMode != Material::ALPHAMODE_MASK);
if (primitive.indexCount > 0) {
// Get the texture index for this primitive
if (textures.size() > 0)
{
//binding base color texture
if (materials[primitive.materialIndex].baseColorTextureIndex < textures.size())
{
VulkanglTFModel::Texture texture = textures[materials[primitive.materialIndex].baseColorTextureIndex];
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 1, 1, &images[texture.imageIndex].descriptorSet, 0, nullptr);
} }
if (renderFlags & RenderFlags::RenderAlphaBlendedNodes) {
//normal map binding skip = (material.alphaMode != Material::ALPHAMODE_BLEND);
if (materials[primitive.materialIndex].normalMapTextureIndex < textures.size())
{
auto normalMap = textures[materials[primitive.materialIndex].normalMapTextureIndex];
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 2, 1, &images[normalMap.imageIndex].descriptorSet, 0, nullptr);
} }
//rough map binding if (!skip) {
if (materials[primitive.materialIndex].matalicRoughTextureIndex < textures.size()) if (renderFlags & RenderFlags::BindImages) {
{ vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, bindImageSet, 1, &material.descriptorSet, 0, nullptr);
auto roughMetalMap = textures[materials[primitive.materialIndex].matalicRoughTextureIndex];
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 3, 1, &images[roughMetalMap.imageIndex].descriptorSet, 0, nullptr);
} }
vkCmdDrawIndexed(commandBuffer, primitive->indexCount, 1, primitive->firstIndex, 0, 0);
//emissive texture binding
if (materials[primitive.materialIndex].emissiveTextureIndex >= 0 && materials[primitive.materialIndex].emissiveTextureIndex < textures.size())
{
auto emissiveMap = textures[materials[primitive.materialIndex].emissiveTextureIndex];
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 4, 1, &images[emissiveMap.imageIndex].descriptorSet, 0, nullptr);
}
// Bind the descriptor for the current primitive's texture
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 5, 1, &materials[primitive.materialIndex].materialData.descriptorSet, 0, nullptr);
}
vkCmdDrawIndexed(commandBuffer, primitive.indexCount, 1, primitive.firstIndex, 0, 0);
} }
} }
} }
for (auto& child : node->children) { for (auto& child : node->children) {
drawNode(commandBuffer, pipelineLayout, child, bPushConstants); drawNode(child, commandBuffer, renderFlags, pipelineLayout, bindImageSet);
} }
} }
// Draw the glTF scene starting at the top-level-nodes // Draw the glTF scene starting at the top-level-nodes
void VulkanglTFModel::draw(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, bool flag = true) void glTFModel::Model::draw(VkCommandBuffer commandBuffer, uint32_t renderFlags, VkPipelineLayout pipelineLayout, uint32_t bindImageSet)
{ {
// All vertices and indices are stored in single buffers, so we only need to bind once if (!buffersBound) {
VkDeviceSize offsets[1] = { 0 }; const VkDeviceSize offsets[1] = { 0 };
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertices.buffer, offsets); vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertices.buffer, offsets);
vkCmdBindIndexBuffer(commandBuffer, indices.buffer, 0, VK_INDEX_TYPE_UINT32); vkCmdBindIndexBuffer(commandBuffer, indices.buffer, 0, VK_INDEX_TYPE_UINT32);
// Render all nodes at top-level }
for (auto& node : nodes) { for (auto& node : nodes) {
drawNode(commandBuffer, pipelineLayout, node, flag); drawNode(node, commandBuffer, renderFlags, pipelineLayout, bindImageSet);
} }
} }