reconstruct gltf loader (not complete yet)
parent
e7d9f78252
commit
f5096914ae
|
@ -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<VkDescriptorImageInfo> imageDescriptors{};
|
|
||||||
std::vector<VkWriteDescriptorSet> 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<uint32_t>(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<uint32_t>(writeDescriptorSets.size());
|
|
||||||
writeDescriptorSet.pImageInfo = &normalTexture->descriptor;
|
|
||||||
writeDescriptorSets.push_back(writeDescriptorSet);
|
|
||||||
}
|
|
||||||
vkUpdateDescriptorSets(device->logicalDevice, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
void VulkanglTFModel::loadMaterials(tinygltf::Model& input)
|
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<uint32_t>& indexBuffer, std::vector<VulkanglTFModel::Vertex>& vertexBuffer)
|
|
||||||
|
/*
|
||||||
|
glTF material
|
||||||
|
*/
|
||||||
|
void glTFModel::Material::createDescriptorSet(VkDescriptorPool descriptorPool, VkDescriptorSetLayout descriptorSetLayout, uint32_t descriptorBindingFlags)
|
||||||
{
|
{
|
||||||
VulkanglTFModel::Node* node = new VulkanglTFModel::Node{};
|
VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
|
||||||
node->matrix = glm::mat4(1.0f);
|
descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||||
node->parent = parent;
|
descriptorSetAllocInfo.descriptorPool = descriptorPool;
|
||||||
node->index = nodeIndex;
|
descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayout;
|
||||||
|
descriptorSetAllocInfo.descriptorSetCount = 1;
|
||||||
|
VK_CHECK_RESULT(vkAllocateDescriptorSets(device->logicalDevice, &descriptorSetAllocInfo, &descriptorSet));
|
||||||
|
std::vector<VkDescriptorImageInfo> imageDescriptors{};
|
||||||
|
std::vector<VkWriteDescriptorSet> 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<uint32_t>(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<uint32_t>(writeDescriptorSets.size());
|
||||||
|
writeDescriptorSet.pImageInfo = &normalTexture->descriptor;
|
||||||
|
writeDescriptorSets.push_back(writeDescriptorSet);
|
||||||
|
}
|
||||||
|
vkUpdateDescriptorSets(device->logicalDevice, static_cast<uint32_t>(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<VkVertexInputAttributeDescription> 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<VkVertexInputAttributeDescription> glTFModel::Vertex::inputAttributeDescriptions(uint32_t binding, const std::vector<VertexComponent> components) {
|
||||||
|
std::vector<VkVertexInputAttributeDescription> 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<VertexComponent> 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<uint32_t>(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<uint32_t>& indexBuffer, std::vector<Vertex>& 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
|
// 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
|
||||||
if (inputNode.translation.size() == 3) {
|
glm::vec3 translation = glm::vec3(0.0f);
|
||||||
node->matrix = glm::translate(node->matrix, glm::vec3(glm::make_vec3(inputNode.translation.data())));
|
if (node.translation.size() == 3) {
|
||||||
|
translation = glm::make_vec3(node.translation.data());
|
||||||
|
newNode->translation = translation;
|
||||||
}
|
}
|
||||||
if (inputNode.rotation.size() == 4) {
|
glm::mat4 rotation = glm::mat4(1.0f);
|
||||||
glm::quat q = glm::make_quat(inputNode.rotation.data());
|
if (node.rotation.size() == 4) {
|
||||||
node->matrix *= glm::mat4(q);
|
glm::quat q = glm::make_quat(node.rotation.data());
|
||||||
|
newNode->rotation = glm::mat4(q);
|
||||||
}
|
}
|
||||||
if (inputNode.scale.size() == 3) {
|
glm::vec3 scale = glm::vec3(1.0f);
|
||||||
node->matrix = glm::scale(node->matrix, glm::vec3(glm::make_vec3(inputNode.scale.data())));
|
if (node.scale.size() == 3) {
|
||||||
|
scale = glm::make_vec3(node.scale.data());
|
||||||
|
newNode->scale = scale;
|
||||||
}
|
}
|
||||||
if (inputNode.matrix.size() == 16) {
|
if (node.matrix.size() == 16) {
|
||||||
node->matrix = glm::make_mat4x4(inputNode.matrix.data());
|
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
|
// Load node's children
|
||||||
if (inputNode.children.size() > 0) {
|
if (node.children.size() > 0) {
|
||||||
for (size_t i = 0; i < inputNode.children.size(); i++) {
|
for (size_t i = 0; i < node.children.size(); i++) {
|
||||||
loadNode(input.nodes[inputNode.children[i]], input, node, inputNode.children[i], indexBuffer, vertexBuffer);
|
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
|
// If the node contains mesh data, we load vertices and indices from the buffers
|
||||||
// In glTF this is done via accessors and buffer views
|
// In glTF this is done via accessors and buffer views
|
||||||
if (inputNode.mesh > -1) {
|
if (node.mesh > -1) {
|
||||||
const tinygltf::Mesh mesh = input.meshes[inputNode.mesh];
|
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
|
// Iterate through all primitives of this node's mesh
|
||||||
for (size_t i = 0; i < mesh.primitives.size(); i++) {
|
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<uint32_t>(indexBuffer.size());
|
uint32_t firstIndex = static_cast<uint32_t>(indexBuffer.size());
|
||||||
uint32_t vertexStart = static_cast<uint32_t>(vertexBuffer.size());
|
uint32_t vertexStart = static_cast<uint32_t>(vertexBuffer.size());
|
||||||
uint32_t indexCount = 0;
|
uint32_t indexCount = 0;
|
||||||
|
uint32_t vertexCount = 0;
|
||||||
|
glm::vec3 posMin{};
|
||||||
|
glm::vec3 posMax{};
|
||||||
|
bool hasSkin = false;
|
||||||
// Vertices
|
// Vertices
|
||||||
{
|
{
|
||||||
const float* positionBuffer = nullptr;
|
const float* positionBuffer = nullptr;
|
||||||
const float* normalsBuffer = nullptr;
|
const float* normalsBuffer = nullptr;
|
||||||
const float* texCoordsBuffer = nullptr;
|
const float* texCoordsBuffer = nullptr;
|
||||||
|
const float* colorBuffer = nullptr;
|
||||||
const float* tangentsBuffer = 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
|
// Get buffer data for vertex positions
|
||||||
if (glTFPrimitive.attributes.find("POSITION") != glTFPrimitive.attributes.end()) {
|
if (primitive.attributes.find("POSITION") != primitive.attributes.end()) {
|
||||||
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("POSITION")->second];
|
const tinygltf::Accessor& positionAccessor = model.accessors[primitive.attributes.find("POSITION")->second];
|
||||||
const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView];
|
const tinygltf::BufferView& view = model.bufferViews[positionAccessor.bufferView];
|
||||||
positionBuffer = reinterpret_cast<const float*>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
positionBuffer = reinterpret_cast<const float*>(&(model.buffers[view.buffer].data[positionAccessor.byteOffset + view.byteOffset]));
|
||||||
vertexCount = accessor.count;
|
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<uint32_t>(positionAccessor.count);
|
||||||
}
|
}
|
||||||
// Get buffer data for vertex normals
|
// Get buffer data for vertex normals
|
||||||
if (glTFPrimitive.attributes.find("NORMAL") != glTFPrimitive.attributes.end()) {
|
if (primitive.attributes.find("NORMAL") != primitive.attributes.end()) {
|
||||||
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("NORMAL")->second];
|
const tinygltf::Accessor& normalAccessor = model.accessors[primitive.attributes.find("NORMAL")->second];
|
||||||
const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView];
|
const tinygltf::BufferView& view = model.bufferViews[normalAccessor.bufferView];
|
||||||
normalsBuffer = reinterpret_cast<const float*>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
normalsBuffer = reinterpret_cast<const float*>(&(model.buffers[view.buffer].data[normalAccessor.byteOffset + view.byteOffset]));
|
||||||
}
|
}
|
||||||
// Get buffer data for vertex texture coordinates
|
// Get buffer data for vertex texture coordinates
|
||||||
// glTF supports multiple sets, we only load the first one
|
// glTF supports multiple sets, we only load the first one
|
||||||
if (glTFPrimitive.attributes.find("TEXCOORD_0") != glTFPrimitive.attributes.end()) {
|
if (primitive.attributes.find("TEXCOORD_0") != primitive.attributes.end()) {
|
||||||
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("TEXCOORD_0")->second];
|
const tinygltf::Accessor& texcoordAccessor = model.accessors[primitive.attributes.find("TEXCOORD_0")->second];
|
||||||
const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView];
|
const tinygltf::BufferView& view = model.bufferViews[texcoordAccessor.bufferView];
|
||||||
texCoordsBuffer = reinterpret_cast<const float*>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
texCoordsBuffer = reinterpret_cast<const float*>(&(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<const float*>(&(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<const float*>(&(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<const uint16_t*>(&(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<const float*>(&(model.buffers[view.buffer].data[weightsAccessor.byteOffset + view.byteOffset]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (glTFPrimitive.attributes.find("TANGENT") != glTFPrimitive.attributes.end())
|
hasSkin = (jointsBuffer && weightsBuffer);
|
||||||
{
|
|
||||||
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("TANGENT")->second];
|
|
||||||
const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView];
|
|
||||||
tangentsBuffer = reinterpret_cast<const float*>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append data to model's vertex buffer
|
// Append data to model's vertex buffer
|
||||||
for (size_t v = 0; v < vertexCount; v++) {
|
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.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.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.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);
|
if (colorBuffer)
|
||||||
vert.color = glm::vec3(1.0f, 1.0f, nodeIndex);//Temp set index in color attribute
|
{
|
||||||
|
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);
|
vertexBuffer.push_back(vert);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Indices
|
// Indices
|
||||||
{
|
{
|
||||||
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.indices];
|
const tinygltf::Accessor& accessor = model.accessors[primitive.indices];
|
||||||
const tinygltf::BufferView& bufferView = input.bufferViews[accessor.bufferView];
|
const tinygltf::BufferView& bufferView = model.bufferViews[accessor.bufferView];
|
||||||
const tinygltf::Buffer& buffer = input.buffers[bufferView.buffer];
|
const tinygltf::Buffer& buffer = model.buffers[bufferView.buffer];
|
||||||
|
|
||||||
indexCount += static_cast<uint32_t>(accessor.count);
|
indexCount += static_cast<uint32_t>(accessor.count);
|
||||||
|
|
||||||
// glTF supports different component types of indices
|
// glTF supports different component types of indices
|
||||||
switch (accessor.componentType) {
|
switch (accessor.componentType) {
|
||||||
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT: {
|
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT: {
|
||||||
const uint32_t* buf = reinterpret_cast<const uint32_t*>(&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++) {
|
for (size_t index = 0; index < accessor.count; index++) {
|
||||||
indexBuffer.push_back(buf[index] + vertexStart);
|
indexBuffer.push_back(buf[index] + vertexStart);
|
||||||
}
|
}
|
||||||
|
delete[] buf;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT: {
|
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT: {
|
||||||
const uint16_t* buf = reinterpret_cast<const uint16_t*>(&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++) {
|
for (size_t index = 0; index < accessor.count; index++) {
|
||||||
indexBuffer.push_back(buf[index] + vertexStart);
|
indexBuffer.push_back(buf[index] + vertexStart);
|
||||||
}
|
}
|
||||||
|
delete[] buf;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE: {
|
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE: {
|
||||||
const uint8_t* buf = reinterpret_cast<const uint8_t*>(&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++) {
|
for (size_t index = 0; index < accessor.count; index++) {
|
||||||
indexBuffer.push_back(buf[index] + vertexStart);
|
indexBuffer.push_back(buf[index] + vertexStart);
|
||||||
}
|
}
|
||||||
|
delete[] buf;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -733,20 +1094,22 @@ void VulkanglTFModel::loadNode(const tinygltf::Node& inputNode, const tinygltf::
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Primitive primitive{};
|
Primitive* newPrimitive = new Primitive(firstIndex, indexCount, primitive.material > -1 ? materials[primitive.material] : materials.back());
|
||||||
primitive.firstIndex = firstIndex;
|
newPrimitive->firstVertex = vertexStart;
|
||||||
primitive.indexCount = indexCount;
|
newPrimitive->vertexCount = vertexCount;
|
||||||
primitive.materialIndex = glTFPrimitive.material;
|
newPrimitive->setDimensions(posMin, posMax);
|
||||||
node->mesh.primitives.push_back(primitive);
|
newMesh->primitives.push_back(newPrimitive);
|
||||||
}
|
}
|
||||||
|
newNode -> mesh = newMesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parent) {
|
if (parent) {
|
||||||
parent->children.push_back(node);
|
parent->children.push_back(newNode);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
nodes.push_back(node);
|
nodes.push_back(newNode);
|
||||||
}
|
}
|
||||||
|
linearNodes.push_back(newNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanglTFModel::Node* VulkanglTFModel::findNode(Node* parent, uint32_t index)
|
VulkanglTFModel::Node* VulkanglTFModel::findNode(Node* parent, uint32_t index)
|
||||||
|
|
Loading…
Reference in New Issue