diff --git a/src/render/glTFModel.cpp b/src/render/glTFModel.cpp index 6c558b0..342816d 100644 --- a/src/render/glTFModel.cpp +++ b/src/render/glTFModel.cpp @@ -532,46 +532,6 @@ void VulkanglTFModel::loadAnimations(tinygltf::Model& input) } */ -/* - glTF material -*/ -void glTFModel::Material::createDescriptorSet(VkDescriptorPool descriptorPool, VkDescriptorSetLayout descriptorSetLayout, uint32_t descriptorBindingFlags) -{ - 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, &descriptorSet)); - std::vector imageDescriptors{}; - std::vector writeDescriptorSets{}; - if (descriptorBindingFlags & DescriptorBindingFlags::ImageBaseColor) { - imageDescriptors.push_back(baseColorTexture->descriptor); - VkWriteDescriptorSet writeDescriptorSet{}; - writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - writeDescriptorSet.descriptorCount = 1; - writeDescriptorSet.dstSet = descriptorSet; - writeDescriptorSet.dstBinding = static_cast(writeDescriptorSets.size()); - writeDescriptorSet.pImageInfo = &baseColorTexture->descriptor; - writeDescriptorSets.push_back(writeDescriptorSet); - } - if (normalTexture && descriptorBindingFlags & DescriptorBindingFlags::ImageNormalMap) { - imageDescriptors.push_back(normalTexture->descriptor); - VkWriteDescriptorSet writeDescriptorSet{}; - writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - writeDescriptorSet.descriptorCount = 1; - writeDescriptorSet.dstSet = descriptorSet; - writeDescriptorSet.dstBinding = static_cast(writeDescriptorSets.size()); - writeDescriptorSet.pImageInfo = &normalTexture->descriptor; - writeDescriptorSets.push_back(writeDescriptorSet); - } - vkUpdateDescriptorSets(device->logicalDevice, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); -} - - - /* void VulkanglTFModel::loadMaterials(tinygltf::Model& input) { @@ -610,81 +570,459 @@ void VulkanglTFModel::loadMaterials(tinygltf::Model& input) } } */ -void VulkanglTFModel::loadNode(const tinygltf::Node& inputNode, const tinygltf::Model& input, VulkanglTFModel::Node* parent, uint32_t nodeIndex, std::vector& indexBuffer, std::vector& vertexBuffer) + +/* + glTF material +*/ +void glTFModel::Material::createDescriptorSet(VkDescriptorPool descriptorPool, VkDescriptorSetLayout descriptorSetLayout, uint32_t descriptorBindingFlags) { - VulkanglTFModel::Node* node = new VulkanglTFModel::Node{}; - node->matrix = glm::mat4(1.0f); - node->parent = parent; - node->index = nodeIndex; + 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, &descriptorSet)); + std::vector imageDescriptors{}; + std::vector writeDescriptorSets{}; + if (descriptorBindingFlags & DescriptorBindingFlags::ImageBaseColor) { + imageDescriptors.push_back(baseColorTexture->descriptor); + VkWriteDescriptorSet writeDescriptorSet{}; + writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + writeDescriptorSet.descriptorCount = 1; + writeDescriptorSet.dstSet = descriptorSet; + writeDescriptorSet.dstBinding = static_cast(writeDescriptorSets.size()); + writeDescriptorSet.pImageInfo = &baseColorTexture->descriptor; + writeDescriptorSets.push_back(writeDescriptorSet); + } + if (normalTexture && descriptorBindingFlags & DescriptorBindingFlags::ImageNormalMap) { + imageDescriptors.push_back(normalTexture->descriptor); + VkWriteDescriptorSet writeDescriptorSet{}; + writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + writeDescriptorSet.descriptorCount = 1; + writeDescriptorSet.dstSet = descriptorSet; + writeDescriptorSet.dstBinding = static_cast(writeDescriptorSets.size()); + writeDescriptorSet.pImageInfo = &normalTexture->descriptor; + writeDescriptorSets.push_back(writeDescriptorSet); + } + vkUpdateDescriptorSets(device->logicalDevice, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); +} + +/* + glTF primitive +*/ +void glTFModel::Primitive::setDimensions(glm::vec3 min, glm::vec3 max) { + dimensions.min = min; + dimensions.max = max; + dimensions.size = max - min; + dimensions.center = (min + max) / 2.0f; + dimensions.radius = glm::distance(min, max) / 2.0f; +} + +/* + glTF mesh +*/ +glTFModel::Mesh::Mesh(vks::VulkanDevice* device, glm::mat4 matrix) { + this->device = device; + this->uniformBlock.matrix = matrix; + VK_CHECK_RESULT(device->createBuffer( + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + sizeof(uniformBlock), + &uniformBuffer.buffer, + &uniformBuffer.memory, + &uniformBlock)); + VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, uniformBuffer.memory, 0, sizeof(uniformBlock), 0, &uniformBuffer.mapped)); + uniformBuffer.descriptor = { uniformBuffer.buffer, 0, sizeof(uniformBlock) }; +}; + +glTFModel::Mesh::~Mesh() { + vkDestroyBuffer(device->logicalDevice, uniformBuffer.buffer, nullptr); + vkFreeMemory(device->logicalDevice, uniformBuffer.memory, nullptr); + for (auto primitive : primitives) + { + delete primitive; + } +} +/* + glTF node +*/ +glm::mat4 glTFModel::Node::localMatrix() { + return glm::translate(glm::mat4(1.0f), translation) * glm::mat4(rotation) * glm::scale(glm::mat4(1.0f), scale) * matrix; +} + +glm::mat4 glTFModel::Node::getMatrix() { + glm::mat4 m = localMatrix(); + glTFModel::Node* p = parent; + while (p) { + m = p->localMatrix() * m; + p = p->parent; + } + return m; +} + +void glTFModel::Node::update() { + if (mesh) { + glm::mat4 m = getMatrix(); + if (skin) { + mesh->uniformBlock.matrix = m; + // Update join matrices + glm::mat4 inverseTransform = glm::inverse(m); + for (size_t i = 0; i < skin->joints.size(); i++) { + glTFModel::Node* jointNode = skin->joints[i]; + glm::mat4 jointMat = jointNode->getMatrix() * skin->inverseBindMatrices[i]; + jointMat = inverseTransform * jointMat; + mesh->uniformBlock.jointMatrix[i] = jointMat; + } + mesh->uniformBlock.jointCount = (float)skin->joints.size(); + memcpy(mesh->uniformBuffer.mapped, &mesh->uniformBlock, sizeof(mesh->uniformBlock)); + } + else { + memcpy(mesh->uniformBuffer.mapped, &m, sizeof(glm::mat4)); + } + } + + for (auto& child : children) { + child->update(); + } +} + +glTFModel::Node::~Node() { + if (mesh) { + delete mesh; + } + for (auto& child : children) { + delete child; + } +} + +/* + glTF default vertex layout with easy Vulkan mapping functions +*/ + +VkVertexInputBindingDescription glTFModel::Vertex::vertexInputBindingDescription; +std::vector glTFModel::Vertex::vertexInputAttributeDescriptions; +VkPipelineVertexInputStateCreateInfo glTFModel::Vertex::pipelineVertexInputStateCreateInfo; + +VkVertexInputBindingDescription glTFModel::Vertex::inputBindingDescription(uint32_t binding) { + return VkVertexInputBindingDescription({ binding, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX }); +} + +VkVertexInputAttributeDescription glTFModel::Vertex::inputAttributeDescription(uint32_t binding, uint32_t location, VertexComponent component) { + switch (component) { + case VertexComponent::Position: + return VkVertexInputAttributeDescription({ location, binding, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, pos) }); + case VertexComponent::Normal: + return VkVertexInputAttributeDescription({ location, binding, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal) }); + case VertexComponent::UV: + return VkVertexInputAttributeDescription({ location, binding, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, uv) }); + case VertexComponent::Color: + return VkVertexInputAttributeDescription({ location, binding, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(Vertex, color) }); + case VertexComponent::Tangent: + return VkVertexInputAttributeDescription({ location, binding, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(Vertex, tangent) }); + case VertexComponent::Joint0: + return VkVertexInputAttributeDescription({ location, binding, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(Vertex, joint0) }); + case VertexComponent::Weight0: + return VkVertexInputAttributeDescription({ location, binding, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(Vertex, weight0) }); + default: + return VkVertexInputAttributeDescription({}); + } +} + +std::vector glTFModel::Vertex::inputAttributeDescriptions(uint32_t binding, const std::vector components) { + std::vector result; + uint32_t location = 0; + for (VertexComponent component : components) { + result.push_back(Vertex::inputAttributeDescription(binding, location, component)); + location++; + } + return result; +} + +/* @brief Returns the default pipeline vertex input state create info structure for the requested vertex components */ +VkPipelineVertexInputStateCreateInfo* glTFModel::Vertex::getPipelineVertexInputState(const std::vector components) { + vertexInputBindingDescription = Vertex::inputBindingDescription(0); + Vertex::vertexInputAttributeDescriptions = Vertex::inputAttributeDescriptions(0, components); + pipelineVertexInputStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + pipelineVertexInputStateCreateInfo.vertexBindingDescriptionCount = 1; + pipelineVertexInputStateCreateInfo.pVertexBindingDescriptions = &Vertex::vertexInputBindingDescription; + pipelineVertexInputStateCreateInfo.vertexAttributeDescriptionCount = static_cast(Vertex::vertexInputAttributeDescriptions.size()); + pipelineVertexInputStateCreateInfo.pVertexAttributeDescriptions = Vertex::vertexInputAttributeDescriptions.data(); + return &pipelineVertexInputStateCreateInfo; +} + +glTFModel::Texture* glTFModel::Model::getTexture(uint32_t index) +{ + + if (index < textures.size()) { + return &textures[index]; + } + return nullptr; +} + +void glTFModel::Model::createEmptyTexture(VkQueue transferQueue) +{ + emptyTexture.device = device; + emptyTexture.width = 1; + emptyTexture.height = 1; + emptyTexture.layerCount = 1; + emptyTexture.mipLevels = 1; + + size_t bufferSize = emptyTexture.width * emptyTexture.height * 4; + unsigned char* buffer = new unsigned char[bufferSize]; + memset(buffer, 0, bufferSize); + + VkBuffer stagingBuffer; + VkDeviceMemory stagingMemory; + VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); + bufferCreateInfo.size = bufferSize; + // This buffer is used as a transfer source for the buffer copy + bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + VK_CHECK_RESULT(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer)); + + VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); + VkMemoryRequirements memReqs; + vkGetBufferMemoryRequirements(device->logicalDevice, stagingBuffer, &memReqs); + memAllocInfo.allocationSize = memReqs.size; + memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &stagingMemory)); + VK_CHECK_RESULT(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0)); + + // Copy texture data into staging buffer + uint8_t* data; + VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void**)&data)); + memcpy(data, buffer, bufferSize); + vkUnmapMemory(device->logicalDevice, stagingMemory); + + VkBufferImageCopy bufferCopyRegion = {}; + bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + bufferCopyRegion.imageSubresource.layerCount = 1; + bufferCopyRegion.imageExtent.width = emptyTexture.width; + bufferCopyRegion.imageExtent.height = emptyTexture.height; + bufferCopyRegion.imageExtent.depth = 1; + + // Create optimal tiled target image + VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; + imageCreateInfo.mipLevels = 1; + imageCreateInfo.arrayLayers = 1; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageCreateInfo.extent = { emptyTexture.width, emptyTexture.height, 1 }; + imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &emptyTexture.image)); + + vkGetImageMemoryRequirements(device->logicalDevice, emptyTexture.image, &memReqs); + memAllocInfo.allocationSize = memReqs.size; + memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &emptyTexture.deviceMemory)); + VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, emptyTexture.image, emptyTexture.deviceMemory, 0)); + + VkImageSubresourceRange subresourceRange{}; + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresourceRange.baseMipLevel = 0; + subresourceRange.levelCount = 1; + subresourceRange.layerCount = 1; + + VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + vks::tools::setImageLayout(copyCmd, emptyTexture.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange); + vkCmdCopyBufferToImage(copyCmd, stagingBuffer, emptyTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferCopyRegion); + vks::tools::setImageLayout(copyCmd, emptyTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, subresourceRange); + device->flushCommandBuffer(copyCmd, transferQueue); + emptyTexture.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + // Clean up staging resources + vkFreeMemory(device->logicalDevice, stagingMemory, nullptr); + vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr); + + VkSamplerCreateInfo samplerCreateInfo = vks::initializers::samplerCreateInfo(); + samplerCreateInfo.magFilter = VK_FILTER_LINEAR; + samplerCreateInfo.minFilter = VK_FILTER_LINEAR; + samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER; + samplerCreateInfo.maxAnisotropy = 1.0f; + VK_CHECK_RESULT(vkCreateSampler(device->logicalDevice, &samplerCreateInfo, nullptr, &emptyTexture.sampler)); + + VkImageViewCreateInfo viewCreateInfo = vks::initializers::imageViewCreateInfo(); + viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; + viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + viewCreateInfo.subresourceRange.levelCount = 1; + viewCreateInfo.image = emptyTexture.image; + VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &emptyTexture.view)); + + emptyTexture.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + emptyTexture.descriptor.imageView = emptyTexture.view; + emptyTexture.descriptor.sampler = emptyTexture.sampler; +} + +/* + glTF model loading and rendering class +*/ +glTFModel::Model::~Model() +{ + vkDestroyBuffer(device->logicalDevice, vertices.buffer, nullptr); + vkFreeMemory(device->logicalDevice, vertices.memory, nullptr); + vkDestroyBuffer(device->logicalDevice, indices.buffer, nullptr); + vkFreeMemory(device->logicalDevice, indices.memory, nullptr); + for (auto texture : textures) { + texture.destroy(); + } + for (auto node : nodes) { + delete node; + } + for (auto skin : skins) { + delete skin; + } + if (descriptorSetLayoutUbo != VK_NULL_HANDLE) { + vkDestroyDescriptorSetLayout(device->logicalDevice, descriptorSetLayoutUbo, nullptr); + descriptorSetLayoutUbo = VK_NULL_HANDLE; + } + if (descriptorSetLayoutImage != VK_NULL_HANDLE) { + vkDestroyDescriptorSetLayout(device->logicalDevice, descriptorSetLayoutImage, nullptr); + descriptorSetLayoutImage = VK_NULL_HANDLE; + } + vkDestroyDescriptorPool(device->logicalDevice, descriptorPool, nullptr); + emptyTexture.destroy(); +} + +void glTFModel::Model::loadNode(glTFModel::Node* parent, const tinygltf::Node& node, uint32_t nodeIndex, const tinygltf::Model& model, std::vector& indexBuffer, std::vector& vertexBuffer, float globalscale) +{ + glTFModel::Node* newNode = new Node{}; + newNode->index = nodeIndex; + newNode->name = node.name; + newNode->skinIndex = node.skin; + newNode->matrix = glm::mat4(1.0f); + newNode->parent = parent; + // Get the local node matrix // It's either made up from translation, rotation, scale or a 4x4 matrix - if (inputNode.translation.size() == 3) { - node->matrix = glm::translate(node->matrix, glm::vec3(glm::make_vec3(inputNode.translation.data()))); + glm::vec3 translation = glm::vec3(0.0f); + if (node.translation.size() == 3) { + translation = glm::make_vec3(node.translation.data()); + newNode->translation = translation; } - if (inputNode.rotation.size() == 4) { - glm::quat q = glm::make_quat(inputNode.rotation.data()); - node->matrix *= glm::mat4(q); + glm::mat4 rotation = glm::mat4(1.0f); + if (node.rotation.size() == 4) { + glm::quat q = glm::make_quat(node.rotation.data()); + newNode->rotation = glm::mat4(q); } - if (inputNode.scale.size() == 3) { - node->matrix = glm::scale(node->matrix, glm::vec3(glm::make_vec3(inputNode.scale.data()))); + glm::vec3 scale = glm::vec3(1.0f); + if (node.scale.size() == 3) { + scale = glm::make_vec3(node.scale.data()); + newNode->scale = scale; } - if (inputNode.matrix.size() == 16) { - node->matrix = glm::make_mat4x4(inputNode.matrix.data()); + if (node.matrix.size() == 16) { + newNode->matrix = glm::make_mat4x4(node.matrix.data()); + if (globalscale != 1.0f) { + //newNode->matrix = glm::scale(newNode->matrix, glm::vec3(globalscale)); + } }; // Load node's children - if (inputNode.children.size() > 0) { - for (size_t i = 0; i < inputNode.children.size(); i++) { - loadNode(input.nodes[inputNode.children[i]], input, node, inputNode.children[i], indexBuffer, vertexBuffer); + if (node.children.size() > 0) { + for (size_t i = 0; i < node.children.size(); i++) { + loadNode(newNode, model.nodes[node.children[i]],node.children[i],model,indexBuffer,vertexBuffer,globalscale); } } // If the node contains mesh data, we load vertices and indices from the buffers // In glTF this is done via accessors and buffer views - if (inputNode.mesh > -1) { - const tinygltf::Mesh mesh = input.meshes[inputNode.mesh]; + if (node.mesh > -1) { + const tinygltf::Mesh mesh = model.meshes[node.mesh]; + + Mesh* newMesh = new Mesh(device, newNode->matrix); + newMesh->name = mesh.name; // Iterate through all primitives of this node's mesh for (size_t i = 0; i < mesh.primitives.size(); i++) { - const tinygltf::Primitive& glTFPrimitive = mesh.primitives[i]; + const tinygltf::Primitive& primitive = mesh.primitives[i]; + if (primitive.indices < 0) + { + continue; + } uint32_t firstIndex = static_cast(indexBuffer.size()); uint32_t vertexStart = static_cast(vertexBuffer.size()); uint32_t indexCount = 0; + uint32_t vertexCount = 0; + glm::vec3 posMin{}; + glm::vec3 posMax{}; + bool hasSkin = false; // Vertices { const float* positionBuffer = nullptr; const float* normalsBuffer = nullptr; const float* texCoordsBuffer = nullptr; + const float* colorBuffer = nullptr; const float* tangentsBuffer = nullptr; - size_t vertexCount = 0; + uint32_t colorComponentsNum; + const uint16_t* jointsBuffer = nullptr; + const float* weightsBuffer = nullptr; + + // Get buffer data for vertex positions - if (glTFPrimitive.attributes.find("POSITION") != glTFPrimitive.attributes.end()) { - const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("POSITION")->second]; - const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView]; - positionBuffer = reinterpret_cast(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset])); - vertexCount = accessor.count; + if (primitive.attributes.find("POSITION") != primitive.attributes.end()) { + const tinygltf::Accessor& positionAccessor = model.accessors[primitive.attributes.find("POSITION")->second]; + const tinygltf::BufferView& view = model.bufferViews[positionAccessor.bufferView]; + positionBuffer = reinterpret_cast(&(model.buffers[view.buffer].data[positionAccessor.byteOffset + view.byteOffset])); + posMin = glm::vec3(positionAccessor.minValues[0], positionAccessor.minValues[1], positionAccessor.minValues[2]); + posMax = glm::vec3(positionAccessor.maxValues[0], positionAccessor.maxValues[1], positionAccessor.maxValues[2]); + vertexCount = static_cast(positionAccessor.count); } // Get buffer data for vertex normals - if (glTFPrimitive.attributes.find("NORMAL") != glTFPrimitive.attributes.end()) { - const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("NORMAL")->second]; - const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView]; - normalsBuffer = reinterpret_cast(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset])); + if (primitive.attributes.find("NORMAL") != primitive.attributes.end()) { + const tinygltf::Accessor& normalAccessor = model.accessors[primitive.attributes.find("NORMAL")->second]; + const tinygltf::BufferView& view = model.bufferViews[normalAccessor.bufferView]; + normalsBuffer = reinterpret_cast(&(model.buffers[view.buffer].data[normalAccessor.byteOffset + view.byteOffset])); } // Get buffer data for vertex texture coordinates // glTF supports multiple sets, we only load the first one - if (glTFPrimitive.attributes.find("TEXCOORD_0") != glTFPrimitive.attributes.end()) { - const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("TEXCOORD_0")->second]; - const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView]; - texCoordsBuffer = reinterpret_cast(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset])); + if (primitive.attributes.find("TEXCOORD_0") != primitive.attributes.end()) { + const tinygltf::Accessor& texcoordAccessor = model.accessors[primitive.attributes.find("TEXCOORD_0")->second]; + const tinygltf::BufferView& view = model.bufferViews[texcoordAccessor.bufferView]; + texCoordsBuffer = reinterpret_cast(&(model.buffers[view.buffer].data[texcoordAccessor.byteOffset + view.byteOffset])); + } + // material tangent + if (primitive.attributes.find("TANGENT") != primitive.attributes.end()) + { + const tinygltf::Accessor& tangentAccessor = model.accessors[primitive.attributes.find("TANGENT")->second]; + const tinygltf::BufferView& view = model.bufferViews[tangentAccessor.bufferView]; + tangentsBuffer = reinterpret_cast(&(model.buffers[view.buffer].data[tangentAccessor.byteOffset + view.byteOffset])); + } + //color + if (primitive.attributes.find("COLOR_0") != primitive.attributes.end()) + { + const tinygltf::Accessor& colorAccessor = model.accessors[primitive.attributes.find("COLOR_0")->second]; + const tinygltf::BufferView& view = model.bufferViews[colorAccessor.bufferView]; + colorBuffer = reinterpret_cast(&(model.buffers[view.buffer].data[colorAccessor.byteOffset + view.byteOffset])); + } + // skin joints + if (primitive.attributes.find("JOINTS_0") != primitive.attributes.end()) + { + const tinygltf::Accessor& jointsAccessor = model.accessors[primitive.attributes.find("JOINTS_0")->second]; + const tinygltf::BufferView& view = model.bufferViews[jointsAccessor.bufferView]; + jointsBuffer = reinterpret_cast(&(model.buffers[view.buffer].data[jointsAccessor.byteOffset + view.byteOffset])); + } + // skin weights + if (primitive.attributes.find("WEIGHTS_0") != primitive.attributes.end()) + { + const tinygltf::Accessor& weightsAccessor = model.accessors[primitive.attributes.find("WEIGHTSS_0")->second]; + const tinygltf::BufferView& view = model.bufferViews[weightsAccessor.bufferView]; + weightsBuffer = reinterpret_cast(&(model.buffers[view.buffer].data[weightsAccessor.byteOffset + view.byteOffset])); } - if (glTFPrimitive.attributes.find("TANGENT") != glTFPrimitive.attributes.end()) - { - const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("TANGENT")->second]; - const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView]; - tangentsBuffer = reinterpret_cast(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset])); - } + hasSkin = (jointsBuffer && weightsBuffer); + + // Append data to model's vertex buffer for (size_t v = 0; v < vertexCount; v++) { @@ -692,40 +1030,63 @@ void VulkanglTFModel::loadNode(const tinygltf::Node& inputNode, const tinygltf:: vert.pos = glm::vec4(glm::make_vec3(&positionBuffer[v * 3]), 1.0f); vert.normal = glm::normalize(glm::vec3(normalsBuffer ? glm::make_vec3(&normalsBuffer[v * 3]) : glm::vec3(0.0f))); vert.uv = texCoordsBuffer ? glm::make_vec2(&texCoordsBuffer[v * 2]) : glm::vec3(0.0f); - vert.tangent = tangentsBuffer ? glm::normalize(glm::make_vec3(&tangentsBuffer[v * 4])) : glm::vec3(0.0f); - vert.color = glm::vec3(1.0f, 1.0f, nodeIndex);//Temp set index in color attribute + if (colorBuffer) + { + switch (colorComponentsNum) + { + case 3 : + vert.color = glm::vec4(glm::make_vec3(&colorBuffer[v * 3]), 1.0f); + case 4 : + vert.color = glm::make_vec4(&colorBuffer[v * 4]); + + } + } + else + { + vert.color = glm::vec4(1.0f); + } + vert.tangent = tangentsBuffer ? glm::vec4(glm::make_vec4(&tangentsBuffer[v * 4])) : glm::vec4(0.0f); + vert.joint0 = hasSkin ? glm::vec4(glm::make_vec4(&jointsBuffer[v * 4])) : glm::vec4(0.0f); + vert.weight0 = hasSkin ? glm::make_vec4(&weightsBuffer[v * 4]) : glm::vec4(0.0f); + vertexBuffer.push_back(vert); } } // Indices { - const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.indices]; - const tinygltf::BufferView& bufferView = input.bufferViews[accessor.bufferView]; - const tinygltf::Buffer& buffer = input.buffers[bufferView.buffer]; + const tinygltf::Accessor& accessor = model.accessors[primitive.indices]; + const tinygltf::BufferView& bufferView = model.bufferViews[accessor.bufferView]; + const tinygltf::Buffer& buffer = model.buffers[bufferView.buffer]; indexCount += static_cast(accessor.count); // glTF supports different component types of indices switch (accessor.componentType) { case TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT: { - const uint32_t* buf = reinterpret_cast(&buffer.data[accessor.byteOffset + bufferView.byteOffset]); + uint32_t* buf = new uint32_t[accessor.count]; + memcpy(buf, &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(uint32_t)); for (size_t index = 0; index < accessor.count; index++) { indexBuffer.push_back(buf[index] + vertexStart); } + delete[] buf; break; } case TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT: { - const uint16_t* buf = reinterpret_cast(&buffer.data[accessor.byteOffset + bufferView.byteOffset]); + uint16_t* buf = new uint16_t[accessor.count]; + memcpy(buf, &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(uint16_t)); for (size_t index = 0; index < accessor.count; index++) { indexBuffer.push_back(buf[index] + vertexStart); } + delete[] buf; break; } case TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE: { - const uint8_t* buf = reinterpret_cast(&buffer.data[accessor.byteOffset + bufferView.byteOffset]); + uint8_t* buf = new uint8_t[accessor.count]; + memcpy(buf, &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(uint8_t)); for (size_t index = 0; index < accessor.count; index++) { indexBuffer.push_back(buf[index] + vertexStart); } + delete[] buf; break; } default: @@ -733,20 +1094,22 @@ void VulkanglTFModel::loadNode(const tinygltf::Node& inputNode, const tinygltf:: return; } } - Primitive primitive{}; - primitive.firstIndex = firstIndex; - primitive.indexCount = indexCount; - primitive.materialIndex = glTFPrimitive.material; - node->mesh.primitives.push_back(primitive); + Primitive* newPrimitive = new Primitive(firstIndex, indexCount, primitive.material > -1 ? materials[primitive.material] : materials.back()); + newPrimitive->firstVertex = vertexStart; + newPrimitive->vertexCount = vertexCount; + newPrimitive->setDimensions(posMin, posMax); + newMesh->primitives.push_back(newPrimitive); } + newNode -> mesh = newMesh; } if (parent) { - parent->children.push_back(node); + parent->children.push_back(newNode); } else { - nodes.push_back(node); + nodes.push_back(newNode); } + linearNodes.push_back(newNode); } VulkanglTFModel::Node* VulkanglTFModel::findNode(Node* parent, uint32_t index)