reconstruct gltf model loader (no complete yet)
parent
293e5a6d28
commit
e7d9f78252
|
@ -1,8 +1,16 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
|
|
||||||
|
#define TINYGLTF_IMPLEMENTATION
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#define TINYGLTF_NO_STB_IMAGE_WRITE
|
||||||
|
|
||||||
#include "glTFModel.h"
|
#include "glTFModel.h"
|
||||||
|
|
||||||
|
VkDescriptorSetLayout glTFModel::descriptorSetLayoutImage = VK_NULL_HANDLE;
|
||||||
|
VkDescriptorSetLayout glTFModel::descriptorSetLayoutUbo = VK_NULL_HANDLE;
|
||||||
|
VkMemoryPropertyFlags glTFModel::memoryPropertyFlags = 0;
|
||||||
|
uint32_t glTFModel::descriptorBindingFlags = glTFModel::DescriptorBindingFlags::ImageBaseColor;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
glTF loading functions
|
glTF loading functions
|
||||||
|
@ -10,24 +18,81 @@
|
||||||
The following functions take a glTF input model loaded via tinyglTF and convert all required data into our own structure
|
The following functions take a glTF input model loaded via tinyglTF and convert all required data into our own structure
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void VulkanglTFModel::loadImages(tinygltf::Model& input)
|
// custom image loading function with tinyglTF
|
||||||
|
|
||||||
|
|
||||||
|
// ktx image as texture
|
||||||
|
bool loadImageDataFunc(tinygltf::Image* image, const int imageIndex, std::string* error, std::string* warning, int req_width, int req_height, const unsigned char* bytes, int size, void* userData)
|
||||||
{
|
{
|
||||||
// Images can be stored inside the glTF (which is the case for the sample model), so instead of directly
|
// KTX files will be handled by our own code
|
||||||
// loading them from disk, we fetch them from the glTF loader and upload the buffers
|
if (image->uri.find_last_of(".") != std::string::npos) {
|
||||||
images.resize(input.images.size());
|
if (image->uri.substr(image->uri.find_last_of(".") + 1) == "ktx") {
|
||||||
for (size_t i = 0; i < input.images.size(); i++) {
|
return true;
|
||||||
tinygltf::Image& glTFImage = input.images[i];
|
}
|
||||||
// Get the image data from the glTF loader
|
}
|
||||||
|
|
||||||
|
return tinygltf::LoadImageData(image, imageIndex, error, warning, req_width, req_height, bytes, size, userData);
|
||||||
|
}
|
||||||
|
|
||||||
|
//will be used for samples that don't require images to be loaded
|
||||||
|
bool loadImageDataFuncEmpty(tinygltf::Image* image, const int imageIndex, std::string* error, std::string* warning, int req_width, int req_height, const unsigned char* bytes, int size, void* userData)
|
||||||
|
{
|
||||||
|
// This function will be used for samples that don't require images to be loaded
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
glTF texture loader
|
||||||
|
*/
|
||||||
|
|
||||||
|
void glTFModel::Texture::updateDescriptor()
|
||||||
|
{
|
||||||
|
descriptor.sampler = sampler;
|
||||||
|
descriptor.imageView = view;
|
||||||
|
descriptor.imageLayout = imageLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
void glTFModel::Texture::destroy()
|
||||||
|
{
|
||||||
|
if (device)
|
||||||
|
{
|
||||||
|
vkDestroyImageView(device->logicalDevice, view, nullptr);
|
||||||
|
vkDestroyImage(device->logicalDevice, image, nullptr);
|
||||||
|
vkFreeMemory(device->logicalDevice, deviceMemory, nullptr);
|
||||||
|
vkDestroySampler(device->logicalDevice, sampler, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void glTFModel::Texture::fromglTfImage(tinygltf::Image& gltfImage, std::string path, vks::VulkanDevice* device, VkQueue copyQueue)
|
||||||
|
{
|
||||||
|
this->device = device;
|
||||||
|
|
||||||
|
bool isKtx = false;
|
||||||
|
// Image points to an external ktx file
|
||||||
|
if (gltfImage.uri.find_last_of(".") != std::string::npos) {
|
||||||
|
if (gltfImage.uri.substr(gltfImage.uri.find_last_of(".") + 1) == "ktx") {
|
||||||
|
isKtx = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VkFormat format;
|
||||||
|
|
||||||
|
if (!isKtx)
|
||||||
|
{
|
||||||
|
// loaded using STB_Image
|
||||||
|
// Images can be stored inside the glTF (which is the case for the sample model), so instead of directly
|
||||||
|
// loading them from disk, we fetch them from the glTF loader and upload the buffers
|
||||||
|
|
||||||
unsigned char* buffer = nullptr;
|
unsigned char* buffer = nullptr;
|
||||||
VkDeviceSize bufferSize = 0;
|
VkDeviceSize bufferSize = 0;
|
||||||
bool deleteBuffer = false;
|
bool deleteBuffer = false;
|
||||||
// We convert RGB-only images to RGBA, as most devices don't support RGB-formats in Vulkan
|
// We convert RGB-only images to RGBA, as most devices don't support RGB-formats in Vulkan
|
||||||
if (glTFImage.component == 3) {
|
if (gltfImage.component == 3) {
|
||||||
bufferSize = glTFImage.width * glTFImage.height * 4;
|
bufferSize = gltfImage.width * gltfImage.height * 4;
|
||||||
buffer = new unsigned char[bufferSize];
|
buffer = new unsigned char[bufferSize];
|
||||||
unsigned char* rgba = buffer;
|
unsigned char* rgba = buffer;
|
||||||
unsigned char* rgb = &glTFImage.image[0];
|
unsigned char* rgb = &gltfImage.image[0];
|
||||||
for (size_t i = 0; i < glTFImage.width * glTFImage.height; ++i) {
|
for (size_t i = 0; i < gltfImage.width * gltfImage.height; ++i) {
|
||||||
memcpy(rgba, rgb, sizeof(unsigned char) * 3);
|
memcpy(rgba, rgb, sizeof(unsigned char) * 3);
|
||||||
rgba += 4;
|
rgba += 4;
|
||||||
rgb += 3;
|
rgb += 3;
|
||||||
|
@ -35,17 +100,340 @@ void VulkanglTFModel::loadImages(tinygltf::Model& input)
|
||||||
deleteBuffer = true;
|
deleteBuffer = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
buffer = &glTFImage.image[0];
|
buffer = &gltfImage.image[0];
|
||||||
bufferSize = glTFImage.image.size();
|
bufferSize = gltfImage.image.size();
|
||||||
}
|
}
|
||||||
// Load texture from image buffer
|
|
||||||
images[i].texture.fromBuffer(buffer, bufferSize, VK_FORMAT_R8G8B8A8_UNORM, glTFImage.width, glTFImage.height, vulkanDevice, copyQueue);
|
format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||||
|
|
||||||
|
VkFormatProperties formatProperties;
|
||||||
|
|
||||||
|
width = gltfImage.width;
|
||||||
|
height = gltfImage.height;
|
||||||
|
mipLevels = static_cast<uint32_t>(floor(log2(std::max(width, height))) + 1.0);
|
||||||
|
|
||||||
|
vkGetPhysicalDeviceFormatProperties(device->physicalDevice, format, &formatProperties);
|
||||||
|
assert(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT);
|
||||||
|
assert(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT);
|
||||||
|
// allocate memry for texture
|
||||||
|
VkMemoryAllocateInfo memAllocInfo{};
|
||||||
|
memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||||
|
VkMemoryRequirements memReqs{};
|
||||||
|
VkBuffer stagingBuffer;
|
||||||
|
VkDeviceMemory stagingMemory;
|
||||||
|
|
||||||
|
VkBufferCreateInfo bufferCreateInfo{};
|
||||||
|
bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||||
|
bufferCreateInfo.size = bufferSize;
|
||||||
|
bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||||
|
bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
VK_CHECK_RESULT(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer));
|
||||||
|
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));
|
||||||
|
|
||||||
|
uint8_t* data;
|
||||||
|
VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void**)&data));
|
||||||
|
memcpy(data, buffer, bufferSize);
|
||||||
|
vkUnmapMemory(device->logicalDevice, stagingMemory);
|
||||||
|
|
||||||
|
VkImageCreateInfo imageCreateInfo{};
|
||||||
|
imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||||
|
imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||||
|
imageCreateInfo.format = format;
|
||||||
|
imageCreateInfo.mipLevels = mipLevels;
|
||||||
|
imageCreateInfo.arrayLayers = 1;
|
||||||
|
imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||||
|
imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||||
|
imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
imageCreateInfo.extent = { width, height, 1 };
|
||||||
|
imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||||
|
VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &image));
|
||||||
|
vkGetImageMemoryRequirements(device->logicalDevice, 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, &deviceMemory));
|
||||||
|
VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, image, deviceMemory, 0));
|
||||||
|
|
||||||
|
VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
|
||||||
|
|
||||||
|
VkImageSubresourceRange subresourceRange = {};
|
||||||
|
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
subresourceRange.levelCount = 1;
|
||||||
|
subresourceRange.layerCount = 1;
|
||||||
|
|
||||||
|
{
|
||||||
|
VkImageMemoryBarrier imageMemoryBarrier{};
|
||||||
|
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||||
|
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||||
|
imageMemoryBarrier.srcAccessMask = 0;
|
||||||
|
imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||||
|
imageMemoryBarrier.image = image;
|
||||||
|
imageMemoryBarrier.subresourceRange = subresourceRange;
|
||||||
|
vkCmdPipelineBarrier(copyCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkBufferImageCopy bufferCopyRegion = {};
|
||||||
|
bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
bufferCopyRegion.imageSubresource.mipLevel = 0;
|
||||||
|
bufferCopyRegion.imageSubresource.baseArrayLayer = 0;
|
||||||
|
bufferCopyRegion.imageSubresource.layerCount = 1;
|
||||||
|
bufferCopyRegion.imageExtent.width = width;
|
||||||
|
bufferCopyRegion.imageExtent.height = height;
|
||||||
|
bufferCopyRegion.imageExtent.depth = 1;
|
||||||
|
|
||||||
|
vkCmdCopyBufferToImage(copyCmd, stagingBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferCopyRegion);
|
||||||
|
|
||||||
|
{
|
||||||
|
VkImageMemoryBarrier imageMemoryBarrier{};
|
||||||
|
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||||
|
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||||
|
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||||
|
imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||||
|
imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||||
|
imageMemoryBarrier.image = image;
|
||||||
|
imageMemoryBarrier.subresourceRange = subresourceRange;
|
||||||
|
vkCmdPipelineBarrier(copyCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
|
||||||
|
}
|
||||||
|
device->flushCommandBuffer(copyCmd, copyQueue, true);
|
||||||
|
|
||||||
|
vkFreeMemory(device->logicalDevice, stagingMemory, nullptr);
|
||||||
|
vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr);
|
||||||
|
|
||||||
|
// Generate the mip chain (glTF uses jpg and png, so we need to create this manually)
|
||||||
|
VkCommandBuffer blitCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
|
||||||
|
for (uint32_t i = 1; i < mipLevels; i++) {
|
||||||
|
VkImageBlit imageBlit{};
|
||||||
|
|
||||||
|
imageBlit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
imageBlit.srcSubresource.layerCount = 1;
|
||||||
|
imageBlit.srcSubresource.mipLevel = i - 1;
|
||||||
|
imageBlit.srcOffsets[1].x = int32_t(width >> (i - 1));
|
||||||
|
imageBlit.srcOffsets[1].y = int32_t(height >> (i - 1));
|
||||||
|
imageBlit.srcOffsets[1].z = 1;
|
||||||
|
|
||||||
|
imageBlit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
imageBlit.dstSubresource.layerCount = 1;
|
||||||
|
imageBlit.dstSubresource.mipLevel = i;
|
||||||
|
imageBlit.dstOffsets[1].x = int32_t(width >> i);
|
||||||
|
imageBlit.dstOffsets[1].y = int32_t(height >> i);
|
||||||
|
imageBlit.dstOffsets[1].z = 1;
|
||||||
|
|
||||||
|
VkImageSubresourceRange mipSubRange = {};
|
||||||
|
mipSubRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
mipSubRange.baseMipLevel = i;
|
||||||
|
mipSubRange.levelCount = 1;
|
||||||
|
mipSubRange.layerCount = 1;
|
||||||
|
|
||||||
|
{
|
||||||
|
VkImageMemoryBarrier imageMemoryBarrier{};
|
||||||
|
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||||
|
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||||
|
imageMemoryBarrier.srcAccessMask = 0;
|
||||||
|
imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||||
|
imageMemoryBarrier.image = image;
|
||||||
|
imageMemoryBarrier.subresourceRange = mipSubRange;
|
||||||
|
vkCmdPipelineBarrier(blitCmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
|
||||||
|
}
|
||||||
|
|
||||||
|
vkCmdBlitImage(blitCmd, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageBlit, VK_FILTER_LINEAR);
|
||||||
|
|
||||||
|
{
|
||||||
|
VkImageMemoryBarrier imageMemoryBarrier{};
|
||||||
|
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||||
|
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||||
|
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||||
|
imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||||
|
imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||||
|
imageMemoryBarrier.image = image;
|
||||||
|
imageMemoryBarrier.subresourceRange = mipSubRange;
|
||||||
|
vkCmdPipelineBarrier(blitCmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subresourceRange.levelCount = mipLevels;
|
||||||
|
imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||||
|
|
||||||
|
{
|
||||||
|
VkImageMemoryBarrier imageMemoryBarrier{};
|
||||||
|
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||||
|
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||||
|
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||||
|
imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||||
|
imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||||
|
imageMemoryBarrier.image = image;
|
||||||
|
imageMemoryBarrier.subresourceRange = subresourceRange;
|
||||||
|
vkCmdPipelineBarrier(blitCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
|
||||||
|
}
|
||||||
|
|
||||||
if (deleteBuffer) {
|
if (deleteBuffer) {
|
||||||
delete[] buffer;
|
delete[] buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
device->flushCommandBuffer(blitCmd, copyQueue, true);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
// Texture is stored in an external ktx file
|
||||||
|
std::string filename = path + "/" + gltfImage.uri;
|
||||||
|
|
||||||
|
ktxTexture* ktxTexture;
|
||||||
|
|
||||||
|
ktxResult result = KTX_SUCCESS;
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, filename.c_str(), AASSET_MODE_STREAMING);
|
||||||
|
if (!asset) {
|
||||||
|
vks::tools::exitFatal("Could not load texture from " + filename + "\n\nThe file may be part of the additional asset pack.\n\nRun \"download_assets.py\" in the repository root to download the latest version.", -1);
|
||||||
|
}
|
||||||
|
size_t size = AAsset_getLength(asset);
|
||||||
|
assert(size > 0);
|
||||||
|
ktx_uint8_t* textureData = new ktx_uint8_t[size];
|
||||||
|
AAsset_read(asset, textureData, size);
|
||||||
|
AAsset_close(asset);
|
||||||
|
result = ktxTexture_CreateFromMemory(textureData, size, KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, &ktxTexture);
|
||||||
|
delete[] textureData;
|
||||||
|
#else
|
||||||
|
if (!vks::tools::fileExists(filename)) {
|
||||||
|
vks::tools::exitFatal("Could not load texture from " + filename + "\n\nThe file may be part of the additional asset pack.\n\nRun \"download_assets.py\" in the repository root to download the latest version.", -1);
|
||||||
|
}
|
||||||
|
result = ktxTexture_CreateFromNamedFile(filename.c_str(), KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, &ktxTexture);
|
||||||
|
#endif
|
||||||
|
assert(result == KTX_SUCCESS);
|
||||||
|
|
||||||
|
this->device = device;
|
||||||
|
width = ktxTexture->baseWidth;
|
||||||
|
height = ktxTexture->baseHeight;
|
||||||
|
mipLevels = ktxTexture->numLevels;
|
||||||
|
|
||||||
|
ktx_uint8_t* ktxTextureData = ktxTexture_GetData(ktxTexture);
|
||||||
|
ktx_size_t ktxTextureSize = ktxTexture_GetSize(ktxTexture);
|
||||||
|
// @todo: Use ktxTexture_GetVkFormat(ktxTexture)
|
||||||
|
format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||||
|
|
||||||
|
// Get device properties for the requested texture format
|
||||||
|
VkFormatProperties formatProperties;
|
||||||
|
vkGetPhysicalDeviceFormatProperties(device->physicalDevice, format, &formatProperties);
|
||||||
|
|
||||||
|
VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
|
||||||
|
VkBuffer stagingBuffer;
|
||||||
|
VkDeviceMemory stagingMemory;
|
||||||
|
|
||||||
|
VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo();
|
||||||
|
bufferCreateInfo.size = ktxTextureSize;
|
||||||
|
// 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));
|
||||||
|
|
||||||
|
uint8_t* data;
|
||||||
|
VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void**)&data));
|
||||||
|
memcpy(data, ktxTextureData, ktxTextureSize);
|
||||||
|
vkUnmapMemory(device->logicalDevice, stagingMemory);
|
||||||
|
|
||||||
|
std::vector<VkBufferImageCopy> bufferCopyRegions;
|
||||||
|
for (uint32_t i = 0; i < mipLevels; i++)
|
||||||
|
{
|
||||||
|
ktx_size_t offset;
|
||||||
|
KTX_error_code result = ktxTexture_GetImageOffset(ktxTexture, i, 0, 0, &offset);
|
||||||
|
assert(result == KTX_SUCCESS);
|
||||||
|
VkBufferImageCopy bufferCopyRegion = {};
|
||||||
|
bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
bufferCopyRegion.imageSubresource.mipLevel = i;
|
||||||
|
bufferCopyRegion.imageSubresource.baseArrayLayer = 0;
|
||||||
|
bufferCopyRegion.imageSubresource.layerCount = 1;
|
||||||
|
bufferCopyRegion.imageExtent.width = std::max(1u, ktxTexture->baseWidth >> i);
|
||||||
|
bufferCopyRegion.imageExtent.height = std::max(1u, ktxTexture->baseHeight >> i);
|
||||||
|
bufferCopyRegion.imageExtent.depth = 1;
|
||||||
|
bufferCopyRegion.bufferOffset = offset;
|
||||||
|
bufferCopyRegions.push_back(bufferCopyRegion);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create optimal tiled target image
|
||||||
|
VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo();
|
||||||
|
imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||||
|
imageCreateInfo.format = format;
|
||||||
|
imageCreateInfo.mipLevels = mipLevels;
|
||||||
|
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 = { width, height, 1 };
|
||||||
|
imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||||
|
VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &image));
|
||||||
|
|
||||||
|
vkGetImageMemoryRequirements(device->logicalDevice, 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, &deviceMemory));
|
||||||
|
VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, image, deviceMemory, 0));
|
||||||
|
|
||||||
|
VkImageSubresourceRange subresourceRange = {};
|
||||||
|
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
subresourceRange.baseMipLevel = 0;
|
||||||
|
subresourceRange.levelCount = mipLevels;
|
||||||
|
subresourceRange.layerCount = 1;
|
||||||
|
|
||||||
|
vks::tools::setImageLayout(copyCmd, image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange);
|
||||||
|
vkCmdCopyBufferToImage(copyCmd, stagingBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, static_cast<uint32_t>(bufferCopyRegions.size()), bufferCopyRegions.data());
|
||||||
|
vks::tools::setImageLayout(copyCmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, subresourceRange);
|
||||||
|
device->flushCommandBuffer(copyCmd, copyQueue);
|
||||||
|
this->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||||
|
|
||||||
|
vkFreeMemory(device->logicalDevice, stagingMemory, nullptr);
|
||||||
|
vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr);
|
||||||
|
|
||||||
|
ktxTexture_Destroy(ktxTexture);
|
||||||
|
}
|
||||||
|
VkSamplerCreateInfo samplerInfo{};
|
||||||
|
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
||||||
|
samplerInfo.magFilter = VK_FILTER_LINEAR;
|
||||||
|
samplerInfo.minFilter = VK_FILTER_LINEAR;
|
||||||
|
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
||||||
|
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
|
||||||
|
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
|
||||||
|
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
|
||||||
|
samplerInfo.compareOp = VK_COMPARE_OP_NEVER;
|
||||||
|
samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
|
||||||
|
samplerInfo.maxAnisotropy = 1.0;
|
||||||
|
samplerInfo.anisotropyEnable = VK_FALSE;
|
||||||
|
samplerInfo.maxLod = (float)mipLevels;
|
||||||
|
samplerInfo.maxAnisotropy = 8.0f;
|
||||||
|
samplerInfo.anisotropyEnable = VK_TRUE;
|
||||||
|
VK_CHECK_RESULT(vkCreateSampler(device->logicalDevice, &samplerInfo, nullptr, &sampler));
|
||||||
|
|
||||||
|
VkImageViewCreateInfo viewInfo{};
|
||||||
|
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||||
|
viewInfo.image = image;
|
||||||
|
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||||
|
viewInfo.format = format;
|
||||||
|
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
viewInfo.subresourceRange.layerCount = 1;
|
||||||
|
viewInfo.subresourceRange.levelCount = mipLevels;
|
||||||
|
VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewInfo, nullptr, &view));
|
||||||
|
|
||||||
|
descriptor.sampler = sampler;
|
||||||
|
descriptor.imageView = view;
|
||||||
|
descriptor.imageLayout = imageLayout;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
void VulkanglTFModel::loadTextures(tinygltf::Model& input)
|
void VulkanglTFModel::loadTextures(tinygltf::Model& input)
|
||||||
{
|
{
|
||||||
textures.resize(input.textures.size());
|
textures.resize(input.textures.size());
|
||||||
|
@ -142,7 +530,49 @@ 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)
|
||||||
{
|
{
|
||||||
materials.resize(input.materials.size());
|
materials.resize(input.materials.size());
|
||||||
|
@ -179,7 +609,7 @@ 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)
|
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)
|
||||||
{
|
{
|
||||||
VulkanglTFModel::Node* node = new VulkanglTFModel::Node{};
|
VulkanglTFModel::Node* node = new VulkanglTFModel::Node{};
|
||||||
|
|
|
@ -23,148 +23,200 @@
|
||||||
|
|
||||||
|
|
||||||
#include "tiny_gltf.h"
|
#include "tiny_gltf.h"
|
||||||
|
#include "VulkanDevice.h"
|
||||||
#include "vulkanexamplebase.h"
|
#include "vulkan/vulkan.h"
|
||||||
|
|
||||||
#define ENABLE_VALIDATION false
|
#define ENABLE_VALIDATION false
|
||||||
|
|
||||||
|
|
||||||
// Contains everything required to render a glTF model in Vulkan
|
// Contains everything required to render a glTF model in Vulkan
|
||||||
// This class is heavily simplified (compared to glTF's feature set) but retains the basic glTF structure
|
// This class is heavily simplified (compared to glTF's feature set) but retains the basic glTF structure
|
||||||
namespace VulkanglTFModel
|
namespace glTFModel
|
||||||
{
|
{
|
||||||
|
|
||||||
// The class requires some Vulkan objects so it can create it's own resources
|
|
||||||
vks::VulkanDevice* vulkanDevice;
|
|
||||||
VkQueue copyQueue;
|
|
||||||
uint32_t nodeCount;
|
|
||||||
|
|
||||||
enum DescriptorBindingFlags {
|
enum DescriptorBindingFlags {
|
||||||
ImageBaseColor = 0x00000001,
|
ImageBaseColor = 0x00000001,
|
||||||
ImageNormalMap = 0x00000002
|
ImageNormalMap = 0x00000002
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum FileLoadingFlags {
|
||||||
|
None = 0x00000000,
|
||||||
|
PreTransformVertices = 0x00000001,
|
||||||
|
PreMultiplyVertexColors = 0x00000002,
|
||||||
|
FlipY = 0x00000004,
|
||||||
|
DontLoadImages = 0x00000008
|
||||||
|
};
|
||||||
|
|
||||||
|
enum RenderFlags {
|
||||||
|
BindImages = 0x00000001,
|
||||||
|
RenderOpaqueNodes = 0x00000002,
|
||||||
|
RenderAlphaMaskedNodes = 0x00000004,
|
||||||
|
RenderAlphaBlendedNodes = 0x00000008
|
||||||
|
};
|
||||||
|
|
||||||
extern VkDescriptorSetLayout descriptorSetLayoutImage;
|
extern VkDescriptorSetLayout descriptorSetLayoutImage;
|
||||||
extern VkDescriptorSetLayout descriptorSetLayoutUbo;
|
extern VkDescriptorSetLayout descriptorSetLayoutUbo;
|
||||||
extern VkMemoryPropertyFlags memoryPropertyFlags;
|
extern VkMemoryPropertyFlags memoryPropertyFlags;
|
||||||
extern uint32_t descriptorBindingFlags;
|
extern uint32_t descriptorBindingFlags;
|
||||||
|
|
||||||
// The vertex layout for the samples' model
|
// A glTF texture stores a reference to the image and a sampler
|
||||||
struct Vertex {
|
struct Texture {
|
||||||
glm::vec3 pos;
|
int32_t imageIndex;
|
||||||
glm::vec3 normal;
|
vks::VulkanDevice* device = nullptr;
|
||||||
glm::vec2 uv;
|
VkImage image;
|
||||||
glm::vec3 color;
|
VkImageLayout imageLayout;
|
||||||
glm::vec3 tangent;
|
VkDeviceMemory deviceMemory;
|
||||||
glm::vec3 jointIndices;
|
VkImageView view;
|
||||||
glm::vec3 jointWeights;
|
uint32_t width, height;
|
||||||
|
uint32_t mipLevels;
|
||||||
|
uint32_t layerCount;
|
||||||
|
VkDescriptorImageInfo descriptor;
|
||||||
|
VkSampler sampler;
|
||||||
|
void updateDescriptor();
|
||||||
|
void destroy();
|
||||||
|
void fromglTfImage(tinygltf::Image& gltfimage, std::string path, vks::VulkanDevice* device, VkQueue copyQueue);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Single vertex buffer for all primitives
|
// A glTF material stores information in e.g. the texture that is attached to it and colors
|
||||||
struct Vertices {
|
struct Material {
|
||||||
VkBuffer buffer;
|
vks::VulkanDevice* device = nullptr;
|
||||||
VkDeviceMemory memory;
|
enum AlphaMode { ALPHAMODE_OPAQUE, ALPHAMODE_MASK, ALPHAMODE_BLEND };
|
||||||
} vertices;
|
AlphaMode alphaMode = ALPHAMODE_OPAQUE;
|
||||||
|
float alphaCutoff = 1.0f;
|
||||||
|
float metallicFactor = 1.0f;
|
||||||
|
float roughnessFactor = 1.0f;
|
||||||
|
glm::vec4 baseColorFactor = glm::vec4(1.0f);
|
||||||
|
glTFModel::Texture* baseColorTexture = nullptr;
|
||||||
|
glTFModel::Texture* metallicRoughnessTexture = nullptr;
|
||||||
|
glTFModel::Texture* normalTexture = nullptr;
|
||||||
|
glTFModel::Texture* occlusionTexture = nullptr;
|
||||||
|
glTFModel::Texture* emissiveTexture = nullptr;
|
||||||
|
|
||||||
// Single index buffer for all primitives
|
glTFModel::Texture* specularGlossinessTexture;
|
||||||
struct Indices {
|
glTFModel::Texture* diffuseTexture;
|
||||||
int count;
|
|
||||||
VkBuffer buffer;
|
|
||||||
VkDeviceMemory memory;
|
|
||||||
} indices;
|
|
||||||
|
|
||||||
// The following structures roughly represent the glTF scene structure
|
VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
|
||||||
// To keep things simple, they only contain those properties that are required for this sample
|
|
||||||
struct Node;
|
Material(vks::VulkanDevice* device) : device(device) {};
|
||||||
|
void createDescriptorSet(VkDescriptorPool descriptorPool, VkDescriptorSetLayout descriptorSetLayout, uint32_t descriptorBindingFlags);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
// A primitive contains the data for a single draw call
|
// A primitive contains the data for a single draw call
|
||||||
struct Primitive {
|
struct Primitive {
|
||||||
uint32_t firstIndex;
|
uint32_t firstIndex;
|
||||||
uint32_t indexCount;
|
uint32_t indexCount;
|
||||||
int32_t materialIndex;
|
uint32_t firstVertex;
|
||||||
|
uint32_t vertexCount;
|
||||||
|
Material& material;
|
||||||
|
|
||||||
|
struct Dimensions {
|
||||||
|
glm::vec3 min = glm::vec3(FLT_MAX);
|
||||||
|
glm::vec3 max = glm::vec3(-FLT_MAX);
|
||||||
|
glm::vec3 size;
|
||||||
|
glm::vec3 center;
|
||||||
|
float radius;
|
||||||
|
} dimensions;
|
||||||
|
|
||||||
|
void setDimensions(glm::vec3 min, glm::vec3 max);
|
||||||
|
Primitive(uint32_t firstIndex, uint32_t indexCount, Material& material) : firstIndex(firstIndex), indexCount(indexCount), material(material) {};
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Contains the node's (optional) geometry and can be made up of an arbitrary number of primitives
|
// Contains the node's (optional) geometry and can be made up of an arbitrary number of primitives
|
||||||
struct Mesh {
|
struct Mesh {
|
||||||
std::vector<Primitive> primitives;
|
vks::VulkanDevice* device;
|
||||||
};
|
|
||||||
|
|
||||||
// A node represents an object in the glTF scene graph
|
std::vector<Primitive*> primitives;
|
||||||
struct Node {
|
std::string name;
|
||||||
Node* parent;
|
|
||||||
uint32_t index;
|
|
||||||
std::vector<Node*> children;
|
|
||||||
Mesh mesh;
|
|
||||||
glm::vec3 translation{};
|
|
||||||
glm::vec3 scale{ 1.0f };
|
|
||||||
glm::quat rotation{};
|
|
||||||
int32_t skin = -1;
|
|
||||||
glm::mat4 getLocalMatrix()
|
|
||||||
{
|
|
||||||
return bAnimateNode ? glm::translate(glm::mat4(1.0f), translation) * glm::mat4(rotation) * glm::scale(glm::mat4(1.0f), scale) : matrix;
|
|
||||||
}
|
|
||||||
glm::mat4 matrix;
|
|
||||||
bool bAnimateNode = false;
|
|
||||||
|
|
||||||
~Node() {
|
struct UniformBuffer {
|
||||||
for (auto& child : children) {
|
VkBuffer buffer;
|
||||||
delete child;
|
VkDeviceMemory memory;
|
||||||
};
|
VkDescriptorBufferInfo descriptor;
|
||||||
}
|
VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
|
||||||
|
void* mapped;
|
||||||
|
} uniformBuffer;
|
||||||
|
|
||||||
|
struct UniformBlock {
|
||||||
|
glm::mat4 matrix;
|
||||||
|
glm::mat4 jointMatrix[64]{};
|
||||||
|
float jointCount{ 0 };
|
||||||
|
} uniformBlock;
|
||||||
|
|
||||||
|
Mesh(vks::VulkanDevice* device, glm::mat4 matrix);
|
||||||
|
~Mesh();
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
// material data for pbr
|
|
||||||
struct MaterialData
|
struct Node;
|
||||||
{
|
|
||||||
vks::Buffer buffer;
|
|
||||||
VkDescriptorSet descriptorSet;
|
|
||||||
struct Values
|
|
||||||
{
|
|
||||||
glm::vec3 emissiveFactor;
|
|
||||||
glm::vec4 baseColorFactor;
|
|
||||||
}values;
|
|
||||||
|
|
||||||
};
|
/*
|
||||||
// A glTF material stores information in e.g. the texture that is attached to it and colors
|
glTF skin
|
||||||
struct Material {
|
*/
|
||||||
glm::vec4 baseColorFactor = glm::vec4(1.0f);
|
|
||||||
uint32_t baseColorTextureIndex;
|
|
||||||
uint32_t normalMapTextureIndex;
|
|
||||||
uint32_t matalicRoughTextureIndex;
|
|
||||||
int32_t emissiveTextureIndex = -1;
|
|
||||||
MaterialData materialData;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Contains the texture for a single glTF image
|
|
||||||
// Images may be reused by texture objects and are as such separated
|
|
||||||
struct Image {
|
|
||||||
vks::Texture2D texture;
|
|
||||||
// We also store (and create) a descriptor set that's used to access this texture from the fragment shader
|
|
||||||
VkDescriptorSet descriptorSet;
|
|
||||||
};
|
|
||||||
|
|
||||||
// A glTF texture stores a reference to the image and a sampler
|
|
||||||
// In this sample, we are only interested in the image
|
|
||||||
struct Texture {
|
|
||||||
int32_t imageIndex;
|
|
||||||
};
|
|
||||||
|
|
||||||
// structure of skin
|
|
||||||
struct Skin {
|
struct Skin {
|
||||||
std::string name;
|
std::string name;
|
||||||
Node* skeletonRoot = nullptr;
|
Node* skeletonRoot = nullptr;
|
||||||
std::vector<glm::mat4> inverseBindMatrices;
|
std::vector<glm::mat4> inverseBindMatrices;
|
||||||
std::vector<Node*> joints;
|
std::vector<Node*> joints;
|
||||||
vks::Buffer ssbo;
|
};
|
||||||
VkDescriptorSet descriptorSet;
|
// A node represents an object in the glTF scene graph
|
||||||
|
struct Node {
|
||||||
|
Node* parent;
|
||||||
|
uint32_t index;
|
||||||
|
std::vector<Node*> children;
|
||||||
|
glm::mat4 matrix;
|
||||||
|
std::string name;
|
||||||
|
Mesh* mesh;
|
||||||
|
Skin* skin;
|
||||||
|
int32_t skinIndex = -1;
|
||||||
|
glm::vec3 translation{};
|
||||||
|
glm::vec3 scale{ 1.0f };
|
||||||
|
glm::quat rotation{};
|
||||||
|
glm::mat4 localMatrix();
|
||||||
|
glm::mat4 getMatrix();
|
||||||
|
void update();
|
||||||
|
~Node();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
glTF default vertex layout with easy Vulkan mapping functions
|
||||||
|
*/
|
||||||
|
enum class VertexComponent { Position, Normal, UV, Color, Tangent, Joint0, Weight0 };
|
||||||
|
|
||||||
|
// The vertex layout for the samples' model
|
||||||
|
struct Vertex {
|
||||||
|
glm::vec3 pos;
|
||||||
|
glm::vec3 normal;
|
||||||
|
glm::vec2 uv;
|
||||||
|
glm::vec4 color;
|
||||||
|
glm::vec4 joint0;
|
||||||
|
glm::vec4 weight0;
|
||||||
|
glm::vec4 tangent;
|
||||||
|
static VkVertexInputBindingDescription vertexInputBindingDescription;
|
||||||
|
static std::vector<VkVertexInputAttributeDescription> vertexInputAttributeDescriptions;
|
||||||
|
static VkPipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo;
|
||||||
|
static VkVertexInputBindingDescription inputBindingDescription(uint32_t binding);
|
||||||
|
static VkVertexInputAttributeDescription inputAttributeDescription(uint32_t binding, uint32_t location, VertexComponent component);
|
||||||
|
static std::vector<VkVertexInputAttributeDescription> inputAttributeDescriptions(uint32_t binding, const std::vector<VertexComponent> components);
|
||||||
|
/** @brief Returns the default pipeline vertex input state create info structure for the requested vertex components */
|
||||||
|
static VkPipelineVertexInputStateCreateInfo* getPipelineVertexInputState(const std::vector<VertexComponent> components);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct AnimationSampler
|
struct AnimationSampler
|
||||||
{
|
{
|
||||||
std::string interpolation;
|
enum InterpolationType { LINEAR, STEP, CUBICSPLINE };
|
||||||
|
InterpolationType interpolation;
|
||||||
std::vector<float> inputs;
|
std::vector<float> inputs;
|
||||||
std::vector<glm::vec4> outputsVec4;
|
std::vector<glm::vec4> outputsVec4;
|
||||||
|
|
||||||
|
@ -172,7 +224,8 @@ namespace VulkanglTFModel
|
||||||
|
|
||||||
struct AnimationChannel
|
struct AnimationChannel
|
||||||
{
|
{
|
||||||
std::string path;
|
enum PathType { TRANSLATION, ROTATION, SCALE };
|
||||||
|
PathType path;
|
||||||
Node* node;
|
Node* node;
|
||||||
uint32_t samplerIndex;
|
uint32_t samplerIndex;
|
||||||
|
|
||||||
|
@ -190,53 +243,68 @@ namespace VulkanglTFModel
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Model data
|
glTF model loading and rendering class
|
||||||
*/
|
*/
|
||||||
std::vector<Image> images;
|
class Model {
|
||||||
std::vector<Texture> textures;
|
private:
|
||||||
std::vector<Material> materials;
|
glTFModel::Texture* getTexture(uint32_t index);
|
||||||
std::vector<Node*> nodes;
|
glTFModel::Texture emptyTexture;
|
||||||
std::vector<Skin> skins;
|
void createEmptyTexture(VkQueue transferQueue);
|
||||||
std::vector<Animation> animations;
|
public:
|
||||||
|
vks::VulkanDevice* device;
|
||||||
|
VkDescriptorPool descriptorPool;
|
||||||
|
|
||||||
uint32_t activeAnimation = 0;
|
struct Vertices {
|
||||||
|
int count;
|
||||||
|
VkBuffer buffer;
|
||||||
|
VkDeviceMemory memory;
|
||||||
|
} vertices;
|
||||||
|
struct Indices {
|
||||||
|
int count;
|
||||||
|
VkBuffer buffer;
|
||||||
|
VkDeviceMemory memory;
|
||||||
|
} indices;
|
||||||
|
|
||||||
|
std::vector<Node*> nodes;
|
||||||
|
std::vector<Node*> linearNodes;
|
||||||
|
|
||||||
|
std::vector<Skin*> skins;
|
||||||
|
|
||||||
|
std::vector<Texture> textures;
|
||||||
|
std::vector<Material> materials;
|
||||||
|
std::vector<Animation> animations;
|
||||||
|
|
||||||
|
struct Dimensions {
|
||||||
|
glm::vec3 min = glm::vec3(FLT_MAX);
|
||||||
|
glm::vec3 max = glm::vec3(-FLT_MAX);
|
||||||
|
glm::vec3 size;
|
||||||
|
glm::vec3 center;
|
||||||
|
float radius;
|
||||||
|
} dimensions;
|
||||||
|
|
||||||
|
bool metallicRoughnessWorkflow = true;
|
||||||
|
bool buffersBound = false;
|
||||||
|
std::string path;
|
||||||
|
|
||||||
|
Model() {};
|
||||||
|
~Model();
|
||||||
|
void 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);
|
||||||
|
void loadSkins(tinygltf::Model& gltfModel);
|
||||||
|
void loadImages(tinygltf::Model& gltfModel, vks::VulkanDevice* device, VkQueue transferQueue);
|
||||||
|
void loadMaterials(tinygltf::Model& gltfModel);
|
||||||
|
void loadAnimations(tinygltf::Model& gltfModel);
|
||||||
|
void loadFromFile(std::string filename, vks::VulkanDevice* device, VkQueue transferQueue, uint32_t fileLoadingFlags = glTFModel::FileLoadingFlags::None, float scale = 1.0f);
|
||||||
|
void bindBuffers(VkCommandBuffer commandBuffer);
|
||||||
|
void drawNode(Node* node, VkCommandBuffer commandBuffer, uint32_t renderFlags = 0, VkPipelineLayout pipelineLayout = VK_NULL_HANDLE, uint32_t bindImageSet = 1);
|
||||||
|
void draw(VkCommandBuffer commandBuffer, uint32_t renderFlags = 0, VkPipelineLayout pipelineLayout = VK_NULL_HANDLE, uint32_t bindImageSet = 1);
|
||||||
|
void getNodeDimensions(Node* node, glm::vec3& min, glm::vec3& max);
|
||||||
|
void getSceneDimensions();
|
||||||
|
void updateAnimation(uint32_t index, float time);
|
||||||
|
Node* findNode(Node* parent, uint32_t index);
|
||||||
|
Node* nodeFromIndex(uint32_t index);
|
||||||
|
void prepareNodeDescriptor(glTFModel::Node* node, VkDescriptorSetLayout descriptorSetLayout);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
//VulkanglTFModel();
|
|
||||||
~VulkanglTFModel()
|
|
||||||
{
|
|
||||||
for (auto node : nodes) {
|
|
||||||
delete node;
|
|
||||||
}
|
|
||||||
// Release all Vulkan resources allocated for the model
|
|
||||||
vkDestroyBuffer(vulkanDevice->logicalDevice, vertices.buffer, nullptr);
|
|
||||||
vkFreeMemory(vulkanDevice->logicalDevice, vertices.memory, nullptr);
|
|
||||||
vkDestroyBuffer(vulkanDevice->logicalDevice, indices.buffer, nullptr);
|
|
||||||
vkFreeMemory(vulkanDevice->logicalDevice, indices.memory, nullptr);
|
|
||||||
for (auto& material : materials)
|
|
||||||
{
|
|
||||||
material.materialData.buffer.destroy();
|
|
||||||
}
|
|
||||||
for (Image image : images) {
|
|
||||||
vkDestroyImageView(vulkanDevice->logicalDevice, image.texture.view, nullptr);
|
|
||||||
vkDestroyImage(vulkanDevice->logicalDevice, image.texture.image, nullptr);
|
|
||||||
vkDestroySampler(vulkanDevice->logicalDevice, image.texture.sampler, nullptr);
|
|
||||||
vkFreeMemory(vulkanDevice->logicalDevice, image.texture.deviceMemory, nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void loadImages(tinygltf::Model& input);
|
|
||||||
void loadTextures(tinygltf::Model& input);
|
|
||||||
void loadMaterials(tinygltf::Model& input);
|
|
||||||
Node* findNode(Node* parent, uint32_t index);
|
|
||||||
Node* nodeFromIndex(uint32_t index);
|
|
||||||
//void loadSkins(tinygltf::Model& input);
|
|
||||||
void loadAnimations(tinygltf::Model& input);
|
|
||||||
void 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);
|
|
||||||
glm::mat4 getNodeMatrix(VulkanglTFModel::Node* node);
|
|
||||||
void updateNodeMatrix(Node* node, std::vector<glm::mat4>& nodeMatrics);
|
|
||||||
//void updateJoints(VulkanglTFModel::Node* node);
|
|
||||||
void updateAnimation(float deltaTime, vks::Buffer& buffer);
|
|
||||||
void drawNode(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, VulkanglTFModel::Node* node, bool bPushConstants);
|
|
||||||
void draw(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, bool flag);
|
|
||||||
};
|
|
Loading…
Reference in New Issue