PeriDyno 1.0.0
Loading...
Searching...
No Matches
VulkanglTFModel.cpp
Go to the documentation of this file.
1
2/*
3* Vulkan glTF model and texture loading class based on tinyglTF (https://github.com/syoyo/tinygltf)
4*
5* Copyright (C) 2018 by Sascha Willems - www.saschawillems.de
6*
7* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
8*/
9
10#define TINYGLTF_IMPLEMENTATION
11#define STB_IMAGE_IMPLEMENTATION
12#define TINYGLTF_NO_STB_IMAGE_WRITE
13
14#include "VulkanglTFModel.h"
15#include "VkSystem.h"
16
17VkDescriptorSetLayout vkglTF::descriptorSetLayoutImage = VK_NULL_HANDLE;
18VkDescriptorSetLayout vkglTF::descriptorSetLayoutUbo = VK_NULL_HANDLE;
19VkMemoryPropertyFlags vkglTF::memoryPropertyFlags = 0;
21
22/*
23 We use a custom image loading function with tinyglTF, so we can do custom stuff loading ktx textures
24*/
25bool 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)
26{
27 // KTX files will be handled by our own code
28 if (image->uri.find_last_of(".") != std::string::npos) {
29 if (image->uri.substr(image->uri.find_last_of(".") + 1) == "ktx") {
30 return true;
31 }
32 }
33
34 return tinygltf::LoadImageData(image, imageIndex, error, warning, req_width, req_height, bytes, size, userData);
35}
36
37bool 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)
38{
39 // This function will be used for samples that don't require images to be loaded
40 return true;
41}
42
43
45{
47
48 if (ctx == VK_NULL_HANDLE) {
49 vks::tools::exitFatal("Vulkan libraray should be initialized first! \n", 0);
50 }
51}
52
53
55{
56 this->destroy();
57}
58
59/*
60 glTF texture loading class
61*/
62
64{
65 descriptor.sampler = sampler;
66 descriptor.imageView = view;
67 descriptor.imageLayout = imageLayout;
68}
69
71{
72 vkDestroyImageView(ctx->deviceHandle(), view, nullptr);
73 vkDestroyImage(ctx->deviceHandle(), image, nullptr);
74 vkFreeMemory(ctx->deviceHandle(), deviceMemory, nullptr);
75 vkDestroySampler(ctx->deviceHandle(), sampler, nullptr);
76}
77
78void vkglTF::Texture::fromglTfImage(tinygltf::Image &gltfimage, std::string path)
79{
80 bool isKtx = false;
81 // Image points to an external ktx file
82 if (gltfimage.uri.find_last_of(".") != std::string::npos) {
83 if (gltfimage.uri.substr(gltfimage.uri.find_last_of(".") + 1) == "ktx") {
84 isKtx = true;
85 }
86 }
87
88 VkFormat format;
89
90 if (!isKtx) {
91 // Texture was loaded using STB_Image
92
93 unsigned char* buffer = nullptr;
94 VkDeviceSize bufferSize = 0;
95 bool deleteBuffer = false;
96 if (gltfimage.component == 3) {
97 // Most devices don't support RGB only on Vulkan so convert if necessary
98 // TODO: Check actual format support and transform only if required
99 bufferSize = gltfimage.width * gltfimage.height * 4;
100 buffer = new unsigned char[bufferSize];
101 unsigned char* rgba = buffer;
102 unsigned char* rgb = &gltfimage.image[0];
103 for (size_t i = 0; i < gltfimage.width * gltfimage.height; ++i) {
104 for (int32_t j = 0; j < 3; ++j) {
105 rgba[j] = rgb[j];
106 }
107 rgba += 4;
108 rgb += 3;
109 }
110 deleteBuffer = true;
111 }
112 else {
113 buffer = &gltfimage.image[0];
114 bufferSize = gltfimage.image.size();
115 }
116
117 format = VK_FORMAT_R8G8B8A8_UNORM;
118
119 VkFormatProperties formatProperties;
120
121 width = gltfimage.width;
122 height = gltfimage.height;
123 mipLevels = static_cast<uint32_t>(floor(log2(std::max(width, height))) + 1.0);
124
125 vkGetPhysicalDeviceFormatProperties(ctx->physicalDeviceHandle(), format, &formatProperties);
126 assert(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT);
127 assert(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT);
128
129 VkMemoryAllocateInfo memAllocInfo{};
130 memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
131 VkMemoryRequirements memReqs{};
132
133 VkBuffer stagingBuffer;
134 VkDeviceMemory stagingMemory;
135
136 VkBufferCreateInfo bufferCreateInfo{};
137 bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
138 bufferCreateInfo.size = bufferSize;
139 bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
140 bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
141 VK_CHECK_RESULT(vkCreateBuffer(ctx->deviceHandle(), &bufferCreateInfo, nullptr, &stagingBuffer));
142 vkGetBufferMemoryRequirements(ctx->deviceHandle(), stagingBuffer, &memReqs);
143 memAllocInfo.allocationSize = memReqs.size;
144 memAllocInfo.memoryTypeIndex = ctx->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
145 VK_CHECK_RESULT(vkAllocateMemory(ctx->deviceHandle(), &memAllocInfo, nullptr, &stagingMemory));
146 VK_CHECK_RESULT(vkBindBufferMemory(ctx->deviceHandle(), stagingBuffer, stagingMemory, 0));
147
148 uint8_t* data;
149 VK_CHECK_RESULT(vkMapMemory(ctx->deviceHandle(), stagingMemory, 0, memReqs.size, 0, (void**)&data));
150 memcpy(data, buffer, bufferSize);
151 vkUnmapMemory(ctx->deviceHandle(), stagingMemory);
152
153 VkImageCreateInfo imageCreateInfo{};
154 imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
155 imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
156 imageCreateInfo.format = format;
157 imageCreateInfo.mipLevels = mipLevels;
158 imageCreateInfo.arrayLayers = 1;
159 imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
160 imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
161 imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
162 imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
163 imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
164 imageCreateInfo.extent = { width, height, 1 };
165 imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
166 VK_CHECK_RESULT(vkCreateImage(ctx->deviceHandle(), &imageCreateInfo, nullptr, &image));
167 vkGetImageMemoryRequirements(ctx->deviceHandle(), image, &memReqs);
168 memAllocInfo.allocationSize = memReqs.size;
169 memAllocInfo.memoryTypeIndex = ctx->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
170 VK_CHECK_RESULT(vkAllocateMemory(ctx->deviceHandle(), &memAllocInfo, nullptr, &deviceMemory));
171 VK_CHECK_RESULT(vkBindImageMemory(ctx->deviceHandle(), image, deviceMemory, 0));
172
173 VkCommandBuffer copyCmd = ctx->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
174
175 VkImageSubresourceRange subresourceRange = {};
176 subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
177 subresourceRange.levelCount = 1;
178 subresourceRange.layerCount = 1;
179
180 {
181 VkImageMemoryBarrier imageMemoryBarrier{};
182 imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
183 imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
184 imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
185 imageMemoryBarrier.srcAccessMask = 0;
186 imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
187 imageMemoryBarrier.image = image;
188 imageMemoryBarrier.subresourceRange = subresourceRange;
189 vkCmdPipelineBarrier(copyCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
190 }
191
192 VkBufferImageCopy bufferCopyRegion = {};
193 bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
194 bufferCopyRegion.imageSubresource.mipLevel = 0;
195 bufferCopyRegion.imageSubresource.baseArrayLayer = 0;
196 bufferCopyRegion.imageSubresource.layerCount = 1;
197 bufferCopyRegion.imageExtent.width = width;
198 bufferCopyRegion.imageExtent.height = height;
199 bufferCopyRegion.imageExtent.depth = 1;
200
201 vkCmdCopyBufferToImage(copyCmd, stagingBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferCopyRegion);
202
203 {
204 VkImageMemoryBarrier imageMemoryBarrier{};
205 imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
206 imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
207 imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
208 imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
209 imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
210 imageMemoryBarrier.image = image;
211 imageMemoryBarrier.subresourceRange = subresourceRange;
212 vkCmdPipelineBarrier(copyCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
213 }
214
215 ctx->flushCommandBuffer(copyCmd, ctx->graphicsQueueHandle(), true);
216
217 vkFreeMemory(ctx->deviceHandle(), stagingMemory, nullptr);
218 vkDestroyBuffer(ctx->deviceHandle(), stagingBuffer, nullptr);
219
220 // Generate the mip chain (glTF uses jpg and png, so we need to create this manually)
221 VkCommandBuffer blitCmd = ctx->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
222 for (uint32_t i = 1; i < mipLevels; i++) {
223 VkImageBlit imageBlit{};
224
225 imageBlit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
226 imageBlit.srcSubresource.layerCount = 1;
227 imageBlit.srcSubresource.mipLevel = i - 1;
228 imageBlit.srcOffsets[1].x = int32_t(width >> (i - 1));
229 imageBlit.srcOffsets[1].y = int32_t(height >> (i - 1));
230 imageBlit.srcOffsets[1].z = 1;
231
232 imageBlit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
233 imageBlit.dstSubresource.layerCount = 1;
234 imageBlit.dstSubresource.mipLevel = i;
235 imageBlit.dstOffsets[1].x = int32_t(width >> i);
236 imageBlit.dstOffsets[1].y = int32_t(height >> i);
237 imageBlit.dstOffsets[1].z = 1;
238
239 VkImageSubresourceRange mipSubRange = {};
240 mipSubRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
241 mipSubRange.baseMipLevel = i;
242 mipSubRange.levelCount = 1;
243 mipSubRange.layerCount = 1;
244
245 {
246 VkImageMemoryBarrier imageMemoryBarrier{};
247 imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
248 imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
249 imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
250 imageMemoryBarrier.srcAccessMask = 0;
251 imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
252 imageMemoryBarrier.image = image;
253 imageMemoryBarrier.subresourceRange = mipSubRange;
254 vkCmdPipelineBarrier(blitCmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
255 }
256
257 vkCmdBlitImage(blitCmd, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageBlit, VK_FILTER_LINEAR);
258
259 {
260 VkImageMemoryBarrier imageMemoryBarrier{};
261 imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
262 imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
263 imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
264 imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
265 imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
266 imageMemoryBarrier.image = image;
267 imageMemoryBarrier.subresourceRange = mipSubRange;
268 vkCmdPipelineBarrier(blitCmd, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
269 }
270 }
271
272 subresourceRange.levelCount = mipLevels;
273 imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
274
275 {
276 VkImageMemoryBarrier imageMemoryBarrier{};
277 imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
278 imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
279 imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
280 imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
281 imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
282 imageMemoryBarrier.image = image;
283 imageMemoryBarrier.subresourceRange = subresourceRange;
284 vkCmdPipelineBarrier(blitCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
285 }
286
287 ctx->flushCommandBuffer(blitCmd, ctx->graphicsQueueHandle(), true);
288 }
289 else {
290 // Texture is stored in an external ktx file
291 std::string filename = path + "/" + gltfimage.uri;
292
293 ktxTexture* ktxTexture;
294
295 ktxResult result = KTX_SUCCESS;
296#if defined(__ANDROID__)
297 AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, filename.c_str(), AASSET_MODE_STREAMING);
298 if (!asset) {
299 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);
300 }
301 size_t size = AAsset_getLength(asset);
302 assert(size > 0);
303 ktx_uint8_t* textureData = new ktx_uint8_t[size];
304 AAsset_read(asset, textureData, size);
305 AAsset_close(asset);
306 result = ktxTexture_CreateFromMemory(textureData, size, KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, &ktxTexture);
307 delete[] textureData;
308#else
309 if (!vks::tools::fileExists(filename)) {
310 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);
311 }
312 result = ktxTexture_CreateFromNamedFile(filename.c_str(), KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, &ktxTexture);
313#endif
314 assert(result == KTX_SUCCESS);
315
316 width = ktxTexture->baseWidth;
317 height = ktxTexture->baseHeight;
318 mipLevels = ktxTexture->numLevels;
319
320 ktx_uint8_t* ktxTextureData = ktxTexture_GetData(ktxTexture);
321 ktx_size_t ktxTextureSize = ktxTexture_GetSize(ktxTexture);
322 // @todo: Use ktxTexture_GetVkFormat(ktxTexture)
323 format = VK_FORMAT_R8G8B8A8_UNORM;
324
325 // Get device properties for the requested texture format
326 VkFormatProperties formatProperties;
327 vkGetPhysicalDeviceFormatProperties(ctx->physicalDeviceHandle(), format, &formatProperties);
328
329 VkCommandBuffer copyCmd = ctx->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
330 VkBuffer stagingBuffer;
331 VkDeviceMemory stagingMemory;
332
333 VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo();
334 bufferCreateInfo.size = ktxTextureSize;
335 // This buffer is used as a transfer source for the buffer copy
336 bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
337 bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
338 VK_CHECK_RESULT(vkCreateBuffer(ctx->deviceHandle(), &bufferCreateInfo, nullptr, &stagingBuffer));
339
340 VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo();
341 VkMemoryRequirements memReqs;
342 vkGetBufferMemoryRequirements(ctx->deviceHandle(), stagingBuffer, &memReqs);
343 memAllocInfo.allocationSize = memReqs.size;
344 memAllocInfo.memoryTypeIndex = ctx->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
345 VK_CHECK_RESULT(vkAllocateMemory(ctx->deviceHandle(), &memAllocInfo, nullptr, &stagingMemory));
346 VK_CHECK_RESULT(vkBindBufferMemory(ctx->deviceHandle(), stagingBuffer, stagingMemory, 0));
347
348 uint8_t* data;
349 VK_CHECK_RESULT(vkMapMemory(ctx->deviceHandle(), stagingMemory, 0, memReqs.size, 0, (void**)&data));
350 memcpy(data, ktxTextureData, ktxTextureSize);
351 vkUnmapMemory(ctx->deviceHandle(), stagingMemory);
352
353 std::vector<VkBufferImageCopy> bufferCopyRegions;
354 for (uint32_t i = 0; i < mipLevels; i++)
355 {
356 ktx_size_t offset;
357 KTX_error_code result = ktxTexture_GetImageOffset(ktxTexture, i, 0, 0, &offset);
358 assert(result == KTX_SUCCESS);
359 VkBufferImageCopy bufferCopyRegion = {};
360 bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
361 bufferCopyRegion.imageSubresource.mipLevel = i;
362 bufferCopyRegion.imageSubresource.baseArrayLayer = 0;
363 bufferCopyRegion.imageSubresource.layerCount = 1;
364 bufferCopyRegion.imageExtent.width = std::max(1u, ktxTexture->baseWidth >> i);
365 bufferCopyRegion.imageExtent.height = std::max(1u, ktxTexture->baseHeight >> i);
366 bufferCopyRegion.imageExtent.depth = 1;
367 bufferCopyRegion.bufferOffset = offset;
368 bufferCopyRegions.push_back(bufferCopyRegion);
369 }
370
371 // Create optimal tiled target image
372 VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo();
373 imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
374 imageCreateInfo.format = format;
375 imageCreateInfo.mipLevels = mipLevels;
376 imageCreateInfo.arrayLayers = 1;
377 imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
378 imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
379 imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
380 imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
381 imageCreateInfo.extent = { width, height, 1 };
382 imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
383 VK_CHECK_RESULT(vkCreateImage(ctx->deviceHandle(), &imageCreateInfo, nullptr, &image));
384
385 vkGetImageMemoryRequirements(ctx->deviceHandle(), image, &memReqs);
386 memAllocInfo.allocationSize = memReqs.size;
387 memAllocInfo.memoryTypeIndex = ctx->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
388 VK_CHECK_RESULT(vkAllocateMemory(ctx->deviceHandle(), &memAllocInfo, nullptr, &deviceMemory));
389 VK_CHECK_RESULT(vkBindImageMemory(ctx->deviceHandle(), image, deviceMemory, 0));
390
391 VkImageSubresourceRange subresourceRange = {};
392 subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
393 subresourceRange.baseMipLevel = 0;
394 subresourceRange.levelCount = mipLevels;
395 subresourceRange.layerCount = 1;
396
397 vks::tools::setImageLayout(copyCmd, image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange);
398 vkCmdCopyBufferToImage(copyCmd, stagingBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, static_cast<uint32_t>(bufferCopyRegions.size()), bufferCopyRegions.data());
399 vks::tools::setImageLayout(copyCmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, subresourceRange);
400 ctx->flushCommandBuffer(copyCmd, ctx->graphicsQueueHandle());
401 this->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
402
403 vkFreeMemory(ctx->deviceHandle(), stagingMemory, nullptr);
404 vkDestroyBuffer(ctx->deviceHandle(), stagingBuffer, nullptr);
405
406 ktxTexture_Destroy(ktxTexture);
407 }
408
409 VkSamplerCreateInfo samplerInfo{};
410 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
411 samplerInfo.magFilter = VK_FILTER_LINEAR;
412 samplerInfo.minFilter = VK_FILTER_LINEAR;
413 samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
414 samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
415 samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
416 samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
417 samplerInfo.compareOp = VK_COMPARE_OP_NEVER;
418 samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
419 samplerInfo.maxAnisotropy = 1.0;
420 samplerInfo.anisotropyEnable = VK_FALSE;
421 samplerInfo.maxLod = (float)mipLevels;
422 samplerInfo.maxAnisotropy = 8.0f;
423 samplerInfo.anisotropyEnable = VK_TRUE;
424 VK_CHECK_RESULT(vkCreateSampler(ctx->deviceHandle(), &samplerInfo, nullptr, &sampler));
425
426 VkImageViewCreateInfo viewInfo{};
427 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
428 viewInfo.image = image;
429 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
430 viewInfo.format = format;
431 viewInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
432 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
433 viewInfo.subresourceRange.layerCount = 1;
434 viewInfo.subresourceRange.levelCount = mipLevels;
435 VK_CHECK_RESULT(vkCreateImageView(ctx->deviceHandle(), &viewInfo, nullptr, &view));
436
437 descriptor.sampler = sampler;
438 descriptor.imageView = view;
439 descriptor.imageLayout = imageLayout;
440}
441
442/*
443 glTF material
444*/
445void vkglTF::Material::createDescriptorSet(VkDescriptorPool descriptorPool, VkDescriptorSetLayout descriptorSetLayout, uint32_t descriptorBindingFlags)
446{
447 VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
448 descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
449 descriptorSetAllocInfo.descriptorPool = descriptorPool;
450 descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayout;
451 descriptorSetAllocInfo.descriptorSetCount = 1;
452 VK_CHECK_RESULT(vkAllocateDescriptorSets(ctx->deviceHandle(), &descriptorSetAllocInfo, &descriptorSet));
453 std::vector<VkDescriptorImageInfo> imageDescriptors{};
454 std::vector<VkWriteDescriptorSet> writeDescriptorSets{};
456 imageDescriptors.push_back(baseColorTexture->descriptor);
457 VkWriteDescriptorSet writeDescriptorSet{};
458 writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
459 writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
460 writeDescriptorSet.descriptorCount = 1;
461 writeDescriptorSet.dstSet = descriptorSet;
462 writeDescriptorSet.dstBinding = static_cast<uint32_t>(writeDescriptorSets.size());
463 writeDescriptorSet.pImageInfo = &baseColorTexture->descriptor;
464 writeDescriptorSets.push_back(writeDescriptorSet);
465 }
467 imageDescriptors.push_back(normalTexture->descriptor);
468 VkWriteDescriptorSet writeDescriptorSet{};
469 writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
470 writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
471 writeDescriptorSet.descriptorCount = 1;
472 writeDescriptorSet.dstSet = descriptorSet;
473 writeDescriptorSet.dstBinding = static_cast<uint32_t>(writeDescriptorSets.size());
474 writeDescriptorSet.pImageInfo = &normalTexture->descriptor;
475 writeDescriptorSets.push_back(writeDescriptorSet);
476 }
477 vkUpdateDescriptorSets(ctx->deviceHandle(), static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
478}
479
480
481/*
482 glTF primitive
483*/
484void vkglTF::Primitive::setDimensions(glm::vec3 min, glm::vec3 max) {
485 dimensions.min = min;
486 dimensions.max = max;
487 dimensions.size = max - min;
488 dimensions.center = (min + max) / 2.0f;
489 dimensions.radius = glm::distance(min, max) / 2.0f;
490}
491
492/*
493 glTF mesh
494*/
495vkglTF::Mesh::Mesh(glm::mat4 matrix) {
497
498 if (ctx == VK_NULL_HANDLE) {
499 vks::tools::exitFatal("Vulkan libraray should be initialized first before mesh is created! \n", 0);
500 }
501
502 this->uniformBlock.matrix = matrix;
503 VK_CHECK_RESULT(ctx->createBuffer(
504 VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
505 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
506 sizeof(uniformBlock),
507 &uniformBuffer.buffer,
508 &uniformBuffer.memory,
509 &uniformBlock));
510 VK_CHECK_RESULT(vkMapMemory(ctx->deviceHandle(), uniformBuffer.memory, 0, sizeof(uniformBlock), 0, &uniformBuffer.mapped));
511 uniformBuffer.descriptor = { uniformBuffer.buffer, 0, sizeof(uniformBlock) };
512};
513
515 vkDestroyBuffer(ctx->deviceHandle(), uniformBuffer.buffer, nullptr);
516 vkFreeMemory(ctx->deviceHandle(), uniformBuffer.memory, nullptr);
517}
518
519/*
520 glTF node
521*/
523 return glm::translate(glm::mat4(1.0f), translation) * glm::mat4(rotation) * glm::scale(glm::mat4(1.0f), scale) * matrix;
524}
525
527 glm::mat4 m = localMatrix();
528 vkglTF::Node *p = parent;
529 while (p) {
530 m = p->localMatrix() * m;
531 p = p->parent;
532 }
533 return m;
534}
535
537 if (mesh) {
538 glm::mat4 m = getMatrix();
539 if (skin) {
540 mesh->uniformBlock.matrix = m;
541 // Update join matrices
542 glm::mat4 inverseTransform = glm::inverse(m);
543 for (size_t i = 0; i < skin->joints.size(); i++) {
544 vkglTF::Node *jointNode = skin->joints[i];
545 glm::mat4 jointMat = jointNode->getMatrix() * skin->inverseBindMatrices[i];
546 jointMat = inverseTransform * jointMat;
547 mesh->uniformBlock.jointMatrix[i] = jointMat;
548 }
549 mesh->uniformBlock.jointcount = (float)skin->joints.size();
550 memcpy(mesh->uniformBuffer.mapped, &mesh->uniformBlock, sizeof(mesh->uniformBlock));
551 } else {
552 memcpy(mesh->uniformBuffer.mapped, &m, sizeof(glm::mat4));
553 }
554 }
555
556 for (auto& child : children) {
557 child->update();
558 }
559}
560
562 if (mesh) {
563 delete mesh;
564 }
565 for (auto& child : children) {
566 delete child;
567 }
568}
569
570/*
571 glTF default vertex layout with easy Vulkan mapping functions
572*/
573
574VkVertexInputBindingDescription vkglTF::Vertex::vertexInputBindingDescription;
575std::vector<VkVertexInputAttributeDescription> vkglTF::Vertex::vertexInputAttributeDescriptions;
576VkPipelineVertexInputStateCreateInfo vkglTF::Vertex::pipelineVertexInputStateCreateInfo;
577
578VkVertexInputBindingDescription vkglTF::Vertex::inputBindingDescription(uint32_t binding) {
579 return VkVertexInputBindingDescription({ binding, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX });
580}
581
582VkVertexInputAttributeDescription vkglTF::Vertex::inputAttributeDescription(uint32_t binding, uint32_t location, VertexComponent component) {
583 switch (component) {
585 return VkVertexInputAttributeDescription({ location, binding, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, pos) });
587 return VkVertexInputAttributeDescription({ location, binding, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal) });
589 return VkVertexInputAttributeDescription({ location, binding, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, uv) });
591 return VkVertexInputAttributeDescription({ location, binding, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(Vertex, color) });
593 return VkVertexInputAttributeDescription({ location, binding, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(Vertex, tangent)} );
595 return VkVertexInputAttributeDescription({ location, binding, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(Vertex, joint0) });
597 return VkVertexInputAttributeDescription({ location, binding, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(Vertex, weight0) });
598 default:
599 return VkVertexInputAttributeDescription({});
600 }
601}
602
603std::vector<VkVertexInputAttributeDescription> vkglTF::Vertex::inputAttributeDescriptions(uint32_t binding, const std::vector<VertexComponent> components) {
604 std::vector<VkVertexInputAttributeDescription> result;
605 uint32_t location = 0;
606 for (VertexComponent component : components) {
607 result.push_back(Vertex::inputAttributeDescription(binding, location, component));
608 location++;
609 }
610 return result;
611}
612
614VkPipelineVertexInputStateCreateInfo* vkglTF::Vertex::getPipelineVertexInputState(const std::vector<VertexComponent> components) {
617 pipelineVertexInputStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
618 pipelineVertexInputStateCreateInfo.vertexBindingDescriptionCount = 1;
620 pipelineVertexInputStateCreateInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(Vertex::vertexInputAttributeDescriptions.size());
623}
624
626{
627
628 if (index < textures.size()) {
629 return &textures[index];
630 }
631 return nullptr;
632}
633
634void vkglTF::Model::createEmptyTexture(VkQueue transferQueue)
635{
636 emptyTexture.width = 1;
637 emptyTexture.height = 1;
638 emptyTexture.layerCount = 1;
639 emptyTexture.mipLevels = 1;
640
641 size_t bufferSize = emptyTexture.width * emptyTexture.height * 4;
642 unsigned char* buffer = new unsigned char[bufferSize];
643 memset(buffer, 0, bufferSize);
644
645 VkBuffer stagingBuffer;
646 VkDeviceMemory stagingMemory;
647 VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo();
648 bufferCreateInfo.size = bufferSize;
649 // This buffer is used as a transfer source for the buffer copy
650 bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
651 bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
652 VK_CHECK_RESULT(vkCreateBuffer(ctx->deviceHandle(), &bufferCreateInfo, nullptr, &stagingBuffer));
653
654 VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo();
655 VkMemoryRequirements memReqs;
656 vkGetBufferMemoryRequirements(ctx->deviceHandle(), stagingBuffer, &memReqs);
657 memAllocInfo.allocationSize = memReqs.size;
658 memAllocInfo.memoryTypeIndex = ctx->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
659 VK_CHECK_RESULT(vkAllocateMemory(ctx->deviceHandle(), &memAllocInfo, nullptr, &stagingMemory));
660 VK_CHECK_RESULT(vkBindBufferMemory(ctx->deviceHandle(), stagingBuffer, stagingMemory, 0));
661
662 // Copy texture data into staging buffer
663 uint8_t* data;
664 VK_CHECK_RESULT(vkMapMemory(ctx->deviceHandle(), stagingMemory, 0, memReqs.size, 0, (void**)&data));
665 memcpy(data, buffer, bufferSize);
666 vkUnmapMemory(ctx->deviceHandle(), stagingMemory);
667
668 VkBufferImageCopy bufferCopyRegion = {};
669 bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
670 bufferCopyRegion.imageSubresource.layerCount = 1;
671 bufferCopyRegion.imageExtent.width = emptyTexture.width;
672 bufferCopyRegion.imageExtent.height = emptyTexture.height;
673 bufferCopyRegion.imageExtent.depth = 1;
674
675 // Create optimal tiled target image
676 VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo();
677 imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
678 imageCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
679 imageCreateInfo.mipLevels = 1;
680 imageCreateInfo.arrayLayers = 1;
681 imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
682 imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
683 imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
684 imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
685 imageCreateInfo.extent = { emptyTexture.width, emptyTexture.height, 1 };
686 imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
687 VK_CHECK_RESULT(vkCreateImage(ctx->deviceHandle(), &imageCreateInfo, nullptr, &emptyTexture.image));
688
689 vkGetImageMemoryRequirements(ctx->deviceHandle(), emptyTexture.image, &memReqs);
690 memAllocInfo.allocationSize = memReqs.size;
691 memAllocInfo.memoryTypeIndex = ctx->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
692 VK_CHECK_RESULT(vkAllocateMemory(ctx->deviceHandle(), &memAllocInfo, nullptr, &emptyTexture.deviceMemory));
693 VK_CHECK_RESULT(vkBindImageMemory(ctx->deviceHandle(), emptyTexture.image, emptyTexture.deviceMemory, 0));
694
695 VkImageSubresourceRange subresourceRange{};
696 subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
697 subresourceRange.baseMipLevel = 0;
698 subresourceRange.levelCount = 1;
699 subresourceRange.layerCount = 1;
700
701 VkCommandBuffer copyCmd = ctx->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
702 vks::tools::setImageLayout(copyCmd, emptyTexture.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange);
703 vkCmdCopyBufferToImage(copyCmd, stagingBuffer, emptyTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferCopyRegion);
704 vks::tools::setImageLayout(copyCmd, emptyTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, subresourceRange);
705 ctx->flushCommandBuffer(copyCmd, ctx->graphicsQueueHandle());
706 emptyTexture.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
707
708 // Clean up staging resources
709 vkFreeMemory(ctx->deviceHandle(), stagingMemory, nullptr);
710 vkDestroyBuffer(ctx->deviceHandle(), stagingBuffer, nullptr);
711
712 VkSamplerCreateInfo samplerCreateInfo = vks::initializers::samplerCreateInfo();
713 samplerCreateInfo.magFilter = VK_FILTER_LINEAR;
714 samplerCreateInfo.minFilter = VK_FILTER_LINEAR;
715 samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
716 samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
717 samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
718 samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
719 samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER;
720 samplerCreateInfo.maxAnisotropy = 1.0f;
721 VK_CHECK_RESULT(vkCreateSampler(ctx->deviceHandle(), &samplerCreateInfo, nullptr, &emptyTexture.sampler));
722
723 VkImageViewCreateInfo viewCreateInfo = vks::initializers::imageViewCreateInfo();
724 viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
725 viewCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
726 viewCreateInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };
727 viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
728 viewCreateInfo.subresourceRange.levelCount = 1;
729 viewCreateInfo.image = emptyTexture.image;
730 VK_CHECK_RESULT(vkCreateImageView(ctx->deviceHandle(), &viewCreateInfo, nullptr, &emptyTexture.view));
731
732 emptyTexture.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
733 emptyTexture.descriptor.imageView = emptyTexture.view;
734 emptyTexture.descriptor.sampler = emptyTexture.sampler;
735}
736
737
739{
741
742 if (ctx == VK_NULL_HANDLE) {
743 vks::tools::exitFatal("Vulkan libraray should be initialized first before Model is created! \n", 0);
744 }
745}
746
747/*
748 glTF model loading and rendering class
749*/
751{
752 vkDestroyBuffer(ctx->deviceHandle(), vertices.buffer, nullptr);
753 vkFreeMemory(ctx->deviceHandle(), vertices.memory, nullptr);
754 vkDestroyBuffer(ctx->deviceHandle(), indices.buffer, nullptr);
755 vkFreeMemory(ctx->deviceHandle(), indices.memory, nullptr);
756 for (auto texture : textures) {
757 texture.destroy();
758 }
759 for (auto node : nodes) {
760 delete node;
761 }
762 if (descriptorSetLayoutUbo != VK_NULL_HANDLE) {
763 vkDestroyDescriptorSetLayout(ctx->deviceHandle(), descriptorSetLayoutUbo, nullptr);
764 descriptorSetLayoutUbo = VK_NULL_HANDLE;
765 }
766 if (descriptorSetLayoutImage != VK_NULL_HANDLE) {
767 vkDestroyDescriptorSetLayout(ctx->deviceHandle(), descriptorSetLayoutImage, nullptr);
768 descriptorSetLayoutImage = VK_NULL_HANDLE;
769 }
770 vkDestroyDescriptorPool(ctx->deviceHandle(), descriptorPool, nullptr);
771 emptyTexture.destroy();
772}
773
774void vkglTF::Model::loadNode(vkglTF::Node *parent, const tinygltf::Node &node, uint32_t nodeIndex, const tinygltf::Model &model, std::vector<uint32_t>& indexBuffer, std::vector<Vertex>& vertexBuffer, float globalscale)
775{
776 vkglTF::Node *newNode = new Node{};
777 newNode->index = nodeIndex;
778 newNode->parent = parent;
779 newNode->name = node.name;
780 newNode->skinIndex = node.skin;
781 newNode->matrix = glm::mat4(1.0f);
782
783 // Generate local node matrix
784 glm::vec3 translation = glm::vec3(0.0f);
785 if (node.translation.size() == 3) {
786 translation = glm::make_vec3(node.translation.data());
787 newNode->translation = translation;
788 }
789 glm::mat4 rotation = glm::mat4(1.0f);
790 if (node.rotation.size() == 4) {
791 glm::quat q = glm::make_quat(node.rotation.data());
792 newNode->rotation = q;
793 }
794 glm::vec3 scale = glm::vec3(1.0f);
795 if (node.scale.size() == 3) {
796 scale = glm::make_vec3(node.scale.data());
797 newNode->scale = scale;
798 }
799 if (node.matrix.size() == 16) {
800 newNode->matrix = glm::make_mat4x4(node.matrix.data());
801 if (globalscale != 1.0f) {
802 //newNode->matrix = glm::scale(newNode->matrix, glm::vec3(globalscale));
803 }
804 };
805
806 // Node with children
807 if (node.children.size() > 0) {
808 for (auto i = 0; i < node.children.size(); i++) {
809 loadNode(newNode, model.nodes[node.children[i]], node.children[i], model, indexBuffer, vertexBuffer, globalscale);
810 }
811 }
812
813 // Node contains mesh data
814 if (node.mesh > -1) {
815 const tinygltf::Mesh mesh = model.meshes[node.mesh];
816 Mesh *newMesh = new Mesh(newNode->matrix);
817 newMesh->name = mesh.name;
818 for (size_t j = 0; j < mesh.primitives.size(); j++) {
819 const tinygltf::Primitive &primitive = mesh.primitives[j];
820 if (primitive.indices < 0) {
821 continue;
822 }
823 uint32_t indexStart = static_cast<uint32_t>(indexBuffer.size());
824 uint32_t vertexStart = static_cast<uint32_t>(vertexBuffer.size());
825 uint32_t indexCount = 0;
826 uint32_t vertexCount = 0;
827 glm::vec3 posMin{};
828 glm::vec3 posMax{};
829 bool hasSkin = false;
830 // Vertices
831 {
832 const float *bufferPos = nullptr;
833 const float *bufferNormals = nullptr;
834 const float *bufferTexCoords = nullptr;
835 const float* bufferColors = nullptr;
836 const float *bufferTangents = nullptr;
837 uint32_t numColorComponents;
838 const uint16_t *bufferJoints = nullptr;
839 const float *bufferWeights = nullptr;
840
841 // Position attribute is required
842 assert(primitive.attributes.find("POSITION") != primitive.attributes.end());
843
844 const tinygltf::Accessor &posAccessor = model.accessors[primitive.attributes.find("POSITION")->second];
845 const tinygltf::BufferView &posView = model.bufferViews[posAccessor.bufferView];
846 bufferPos = reinterpret_cast<const float *>(&(model.buffers[posView.buffer].data[posAccessor.byteOffset + posView.byteOffset]));
847 posMin = glm::vec3(posAccessor.minValues[0], posAccessor.minValues[1], posAccessor.minValues[2]);
848 posMax = glm::vec3(posAccessor.maxValues[0], posAccessor.maxValues[1], posAccessor.maxValues[2]);
849
850 if (primitive.attributes.find("NORMAL") != primitive.attributes.end()) {
851 const tinygltf::Accessor &normAccessor = model.accessors[primitive.attributes.find("NORMAL")->second];
852 const tinygltf::BufferView &normView = model.bufferViews[normAccessor.bufferView];
853 bufferNormals = reinterpret_cast<const float *>(&(model.buffers[normView.buffer].data[normAccessor.byteOffset + normView.byteOffset]));
854 }
855
856 if (primitive.attributes.find("TEXCOORD_0") != primitive.attributes.end()) {
857 const tinygltf::Accessor &uvAccessor = model.accessors[primitive.attributes.find("TEXCOORD_0")->second];
858 const tinygltf::BufferView &uvView = model.bufferViews[uvAccessor.bufferView];
859 bufferTexCoords = reinterpret_cast<const float *>(&(model.buffers[uvView.buffer].data[uvAccessor.byteOffset + uvView.byteOffset]));
860 }
861
862 if (primitive.attributes.find("COLOR_0") != primitive.attributes.end())
863 {
864 const tinygltf::Accessor& colorAccessor = model.accessors[primitive.attributes.find("COLOR_0")->second];
865 const tinygltf::BufferView& colorView = model.bufferViews[colorAccessor.bufferView];
866 // Color buffer are either of type vec3 or vec4
867 numColorComponents = colorAccessor.type == TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 ? 3 : 4;
868 bufferColors = reinterpret_cast<const float*>(&(model.buffers[colorView.buffer].data[colorAccessor.byteOffset + colorView.byteOffset]));
869 }
870
871 if (primitive.attributes.find("TANGENT") != primitive.attributes.end())
872 {
873 const tinygltf::Accessor &tangentAccessor = model.accessors[primitive.attributes.find("TANGENT")->second];
874 const tinygltf::BufferView &tangentView = model.bufferViews[tangentAccessor.bufferView];
875 bufferTangents = reinterpret_cast<const float *>(&(model.buffers[tangentView.buffer].data[tangentAccessor.byteOffset + tangentView.byteOffset]));
876 }
877
878 // Skinning
879 // Joints
880 if (primitive.attributes.find("JOINTS_0") != primitive.attributes.end()) {
881 const tinygltf::Accessor &jointAccessor = model.accessors[primitive.attributes.find("JOINTS_0")->second];
882 const tinygltf::BufferView &jointView = model.bufferViews[jointAccessor.bufferView];
883 bufferJoints = reinterpret_cast<const uint16_t *>(&(model.buffers[jointView.buffer].data[jointAccessor.byteOffset + jointView.byteOffset]));
884 }
885
886 if (primitive.attributes.find("WEIGHTS_0") != primitive.attributes.end()) {
887 const tinygltf::Accessor &uvAccessor = model.accessors[primitive.attributes.find("WEIGHTS_0")->second];
888 const tinygltf::BufferView &uvView = model.bufferViews[uvAccessor.bufferView];
889 bufferWeights = reinterpret_cast<const float *>(&(model.buffers[uvView.buffer].data[uvAccessor.byteOffset + uvView.byteOffset]));
890 }
891
892 hasSkin = (bufferJoints && bufferWeights);
893
894 vertexCount = static_cast<uint32_t>(posAccessor.count);
895
896 for (size_t v = 0; v < posAccessor.count; v++) {
897 Vertex vert{};
898 vert.pos = glm::make_vec3(&bufferPos[v * 3]);
899 vert.normal = glm::normalize(glm::vec3(bufferNormals ? glm::make_vec3(&bufferNormals[v * 3]) : glm::vec3(0.0f)));
900 vert.uv = bufferTexCoords ? glm::make_vec2(&bufferTexCoords[v * 2]) : glm::vec2(0.0f);
901 if (bufferColors) {
902 switch (numColorComponents) {
903 case 3:
904 vert.color = glm::vec4(glm::make_vec3(&bufferColors[v * 3]), 1.0f);
905 case 4:
906 vert.color = glm::make_vec4(&bufferColors[v * 4]);
907 }
908 }
909 else {
910 vert.color = glm::vec4(1.0f);
911 }
912 vert.tangent = bufferTangents ? glm::vec4(glm::make_vec4(&bufferTangents[v * 4])) : glm::vec4(0.0f);
913 vert.joint0 = hasSkin ? glm::vec4(glm::make_vec4(&bufferJoints[v * 4])) : glm::vec4(0.0f);
914 vert.weight0 = hasSkin ? glm::make_vec4(&bufferWeights[v * 4]) : glm::vec4(0.0f);
915 vertexBuffer.push_back(vert);
916 }
917 }
918 // Indices
919 {
920 const tinygltf::Accessor &accessor = model.accessors[primitive.indices];
921 const tinygltf::BufferView &bufferView = model.bufferViews[accessor.bufferView];
922 const tinygltf::Buffer &buffer = model.buffers[bufferView.buffer];
923
924 indexCount = static_cast<uint32_t>(accessor.count);
925
926 switch (accessor.componentType) {
927 case TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT: {
928 uint32_t *buf = new uint32_t[accessor.count];
929 memcpy(buf, &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(uint32_t));
930 for (size_t index = 0; index < accessor.count; index++) {
931 indexBuffer.push_back(buf[index] + vertexStart);
932 }
933 break;
934 }
935 case TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT: {
936 uint16_t *buf = new uint16_t[accessor.count];
937 memcpy(buf, &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(uint16_t));
938 for (size_t index = 0; index < accessor.count; index++) {
939 indexBuffer.push_back(buf[index] + vertexStart);
940 }
941 break;
942 }
943 case TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE: {
944 uint8_t *buf = new uint8_t[accessor.count];
945 memcpy(buf, &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(uint8_t));
946 for (size_t index = 0; index < accessor.count; index++) {
947 indexBuffer.push_back(buf[index] + vertexStart);
948 }
949 break;
950 }
951 default:
952 std::cerr << "Index component type " << accessor.componentType << " not supported!" << std::endl;
953 return;
954 }
955 }
956 Primitive *newPrimitive = new Primitive(indexStart, indexCount, primitive.material > -1 ? materials[primitive.material] : materials.back());
957 newPrimitive->firstVertex = vertexStart;
958 newPrimitive->vertexCount = vertexCount;
959 newPrimitive->setDimensions(posMin, posMax);
960 newMesh->primitives.push_back(newPrimitive);
961 }
962 newNode->mesh = newMesh;
963 }
964 if (parent) {
965 parent->children.push_back(newNode);
966 } else {
967 nodes.push_back(newNode);
968 }
969 linearNodes.push_back(newNode);
970}
971
972void vkglTF::Model::loadSkins(tinygltf::Model &gltfModel)
973{
974 for (tinygltf::Skin &source : gltfModel.skins) {
975 Skin *newSkin = new Skin{};
976 newSkin->name = source.name;
977
978 // Find skeleton root node
979 if (source.skeleton > -1) {
980 newSkin->skeletonRoot = nodeFromIndex(source.skeleton);
981 }
982
983 // Find joint nodes
984 for (int jointIndex : source.joints) {
985 Node* node = nodeFromIndex(jointIndex);
986 if (node) {
987 newSkin->joints.push_back(nodeFromIndex(jointIndex));
988 }
989 }
990
991 // Get inverse bind matrices from buffer
992 if (source.inverseBindMatrices > -1) {
993 const tinygltf::Accessor &accessor = gltfModel.accessors[source.inverseBindMatrices];
994 const tinygltf::BufferView &bufferView = gltfModel.bufferViews[accessor.bufferView];
995 const tinygltf::Buffer &buffer = gltfModel.buffers[bufferView.buffer];
996 newSkin->inverseBindMatrices.resize(accessor.count);
997 memcpy(newSkin->inverseBindMatrices.data(), &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(glm::mat4));
998 }
999
1000 skins.push_back(newSkin);
1001 }
1002}
1003
1004void vkglTF::Model::loadImages(tinygltf::Model &gltfModel)
1005{
1006 for (tinygltf::Image &image : gltfModel.images) {
1007 vkglTF::Texture texture;
1008 texture.fromglTfImage(image, path);
1009 textures.push_back(texture);
1010 }
1011 // Create an empty texture to be used for empty material images
1012 createEmptyTexture(ctx->graphicsQueueHandle());
1013}
1014
1015void vkglTF::Model::loadMaterials(tinygltf::Model &gltfModel)
1016{
1017 for (tinygltf::Material &mat : gltfModel.materials) {
1018 vkglTF::Material material(ctx);
1019 if (mat.values.find("baseColorTexture") != mat.values.end()) {
1020 material.baseColorTexture = getTexture(gltfModel.textures[mat.values["baseColorTexture"].TextureIndex()].source);
1021 }
1022 // Metallic roughness workflow
1023 if (mat.values.find("metallicRoughnessTexture") != mat.values.end()) {
1024 material.metallicRoughnessTexture = getTexture(gltfModel.textures[mat.values["metallicRoughnessTexture"].TextureIndex()].source);
1025 }
1026 if (mat.values.find("roughnessFactor") != mat.values.end()) {
1027 material.roughnessFactor = static_cast<float>(mat.values["roughnessFactor"].Factor());
1028 }
1029 if (mat.values.find("metallicFactor") != mat.values.end()) {
1030 material.metallicFactor = static_cast<float>(mat.values["metallicFactor"].Factor());
1031 }
1032 if (mat.values.find("baseColorFactor") != mat.values.end()) {
1033 material.baseColorFactor = glm::make_vec4(mat.values["baseColorFactor"].ColorFactor().data());
1034 }
1035 if (mat.additionalValues.find("normalTexture") != mat.additionalValues.end()) {
1036 material.normalTexture = getTexture(gltfModel.textures[mat.additionalValues["normalTexture"].TextureIndex()].source);
1037 } else {
1038 material.normalTexture = &emptyTexture;
1039 }
1040 if (mat.additionalValues.find("emissiveTexture") != mat.additionalValues.end()) {
1041 material.emissiveTexture = getTexture(gltfModel.textures[mat.additionalValues["emissiveTexture"].TextureIndex()].source);
1042 }
1043 if (mat.additionalValues.find("occlusionTexture") != mat.additionalValues.end()) {
1044 material.occlusionTexture = getTexture(gltfModel.textures[mat.additionalValues["occlusionTexture"].TextureIndex()].source);
1045 }
1046 if (mat.additionalValues.find("alphaMode") != mat.additionalValues.end()) {
1047 tinygltf::Parameter param = mat.additionalValues["alphaMode"];
1048 if (param.string_value == "BLEND") {
1050 }
1051 if (param.string_value == "MASK") {
1053 }
1054 }
1055 if (mat.additionalValues.find("alphaCutoff") != mat.additionalValues.end()) {
1056 material.alphaCutoff = static_cast<float>(mat.additionalValues["alphaCutoff"].Factor());
1057 }
1058
1059 materials.push_back(material);
1060 }
1061 // Push a default material at the end of the list for meshes with no material assigned
1062 materials.push_back(Material(ctx));
1063}
1064
1065void vkglTF::Model::loadAnimations(tinygltf::Model &gltfModel)
1066{
1067 for (tinygltf::Animation &anim : gltfModel.animations) {
1068 vkglTF::Animation animation{};
1069 animation.name = anim.name;
1070 if (anim.name.empty()) {
1071 animation.name = std::to_string(animations.size());
1072 }
1073
1074 // Samplers
1075 for (auto &samp : anim.samplers) {
1076 vkglTF::AnimationSampler sampler{};
1077
1078 if (samp.interpolation == "LINEAR") {
1079 sampler.interpolation = AnimationSampler::InterpolationType::LINEAR;
1080 }
1081 if (samp.interpolation == "STEP") {
1082 sampler.interpolation = AnimationSampler::InterpolationType::STEP;
1083 }
1084 if (samp.interpolation == "CUBICSPLINE") {
1086 }
1087
1088 // Read sampler input time values
1089 {
1090 const tinygltf::Accessor &accessor = gltfModel.accessors[samp.input];
1091 const tinygltf::BufferView &bufferView = gltfModel.bufferViews[accessor.bufferView];
1092 const tinygltf::Buffer &buffer = gltfModel.buffers[bufferView.buffer];
1093
1094 assert(accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT);
1095
1096 float *buf = new float[accessor.count];
1097 memcpy(buf, &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(float));
1098 for (size_t index = 0; index < accessor.count; index++) {
1099 sampler.inputs.push_back(buf[index]);
1100 }
1101
1102 for (auto input : sampler.inputs) {
1103 if (input < animation.start) {
1104 animation.start = input;
1105 };
1106 if (input > animation.end) {
1107 animation.end = input;
1108 }
1109 }
1110 }
1111
1112 // Read sampler output T/R/S values
1113 {
1114 const tinygltf::Accessor &accessor = gltfModel.accessors[samp.output];
1115 const tinygltf::BufferView &bufferView = gltfModel.bufferViews[accessor.bufferView];
1116 const tinygltf::Buffer &buffer = gltfModel.buffers[bufferView.buffer];
1117
1118 assert(accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT);
1119
1120 switch (accessor.type) {
1121 case TINYGLTF_TYPE_VEC3: {
1122 glm::vec3 *buf = new glm::vec3[accessor.count];
1123 memcpy(buf, &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(glm::vec3));
1124 for (size_t index = 0; index < accessor.count; index++) {
1125 sampler.outputsVec4.push_back(glm::vec4(buf[index], 0.0f));
1126 }
1127 break;
1128 }
1129 case TINYGLTF_TYPE_VEC4: {
1130 glm::vec4 *buf = new glm::vec4[accessor.count];
1131 memcpy(buf, &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(glm::vec4));
1132 for (size_t index = 0; index < accessor.count; index++) {
1133 sampler.outputsVec4.push_back(buf[index]);
1134 }
1135 break;
1136 }
1137 default: {
1138 std::cout << "unknown type" << std::endl;
1139 break;
1140 }
1141 }
1142 }
1143
1144 animation.samplers.push_back(sampler);
1145 }
1146
1147 // Channels
1148 for (auto &source: anim.channels) {
1149 vkglTF::AnimationChannel channel{};
1150
1151 if (source.target_path == "rotation") {
1153 }
1154 if (source.target_path == "translation") {
1156 }
1157 if (source.target_path == "scale") {
1158 channel.path = AnimationChannel::PathType::SCALE;
1159 }
1160 if (source.target_path == "weights") {
1161 std::cout << "weights not yet supported, skipping channel" << std::endl;
1162 continue;
1163 }
1164 channel.samplerIndex = source.sampler;
1165 channel.node = nodeFromIndex(source.target_node);
1166 if (!channel.node) {
1167 continue;
1168 }
1169
1170 animation.channels.push_back(channel);
1171 }
1172
1173 animations.push_back(animation);
1174 }
1175}
1176
1177void vkglTF::Model::loadFromFile(std::string filename, uint32_t fileLoadingFlags, float scale)
1178{
1179 tinygltf::Model gltfModel;
1180 tinygltf::TinyGLTF gltfContext;
1181 if (fileLoadingFlags & FileLoadingFlags::DontLoadImages) {
1182 gltfContext.SetImageLoader(loadImageDataFuncEmpty, nullptr);
1183 } else {
1184 gltfContext.SetImageLoader(loadImageDataFunc, nullptr);
1185 }
1186#if defined(__ANDROID__)
1187 // On Android all assets are packed with the apk in a compressed form, so we need to open them using the asset manager
1188 // We let tinygltf handle this, by passing the asset manager of our app
1189 tinygltf::asset_manager = androidApp->activity->assetManager;
1190#endif
1191 size_t pos = filename.find_last_of('/');
1192 path = filename.substr(0, pos);
1193
1194 std::string error, warning;
1195
1196#if defined(__ANDROID__)
1197 // On Android all assets are packed with the apk in a compressed form, so we need to open them using the asset manager
1198 // We let tinygltf handle this, by passing the asset manager of our app
1199 tinygltf::asset_manager = androidApp->activity->assetManager;
1200#endif
1201 bool fileLoaded = gltfContext.LoadASCIIFromFile(&gltfModel, &error, &warning, filename);
1202
1203 std::vector<uint32_t> indexBuffer;
1204 std::vector<Vertex> vertexBuffer;
1205
1206 if (fileLoaded) {
1207 if (!(fileLoadingFlags & FileLoadingFlags::DontLoadImages)) {
1208 loadImages(gltfModel);
1209 }
1210 loadMaterials(gltfModel);
1211 const tinygltf::Scene &scene = gltfModel.scenes[gltfModel.defaultScene > -1 ? gltfModel.defaultScene : 0];
1212 for (size_t i = 0; i < scene.nodes.size(); i++) {
1213 const tinygltf::Node node = gltfModel.nodes[scene.nodes[i]];
1214 loadNode(nullptr, node, scene.nodes[i], gltfModel, indexBuffer, vertexBuffer, scale);
1215 }
1216 if (gltfModel.animations.size() > 0) {
1217 loadAnimations(gltfModel);
1218 }
1219 loadSkins(gltfModel);
1220
1221 for (auto node : linearNodes) {
1222 // Assign skins
1223 if (node->skinIndex > -1) {
1224 node->skin = skins[node->skinIndex];
1225 }
1226 // Initial pose
1227 if (node->mesh) {
1228 node->update();
1229 }
1230 }
1231 }
1232 else {
1233 // TODO: throw
1234 vks::tools::exitFatal("Could not load glTF file \"" + filename + "\": " + error, -1);
1235 return;
1236 }
1237
1238 // Pre-Calculations for requested features
1239 if ((fileLoadingFlags & FileLoadingFlags::PreTransformVertices) || (fileLoadingFlags & FileLoadingFlags::PreMultiplyVertexColors) || (fileLoadingFlags & FileLoadingFlags::FlipY)) {
1240 const bool preTransform = fileLoadingFlags & FileLoadingFlags::PreTransformVertices;
1241 const bool preMultiplyColor = fileLoadingFlags & FileLoadingFlags::PreMultiplyVertexColors;
1242 const bool flipY = fileLoadingFlags & FileLoadingFlags::FlipY;
1243 for (Node* node : linearNodes) {
1244 if (node->mesh) {
1245 const glm::mat4 localMatrix = node->getMatrix();
1246 for (Primitive* primitive : node->mesh->primitives) {
1247 for (uint32_t i = 0; i < primitive->vertexCount; i++) {
1248 Vertex& vertex = vertexBuffer[primitive->firstVertex + i];
1249 // Pre-transform vertex positions by node-hierarchy
1250 if (preTransform) {
1251 vertex.pos = glm::vec3(localMatrix * glm::vec4(vertex.pos, 1.0f));
1252 vertex.normal = glm::normalize(glm::mat3(localMatrix) * vertex.normal);
1253 }
1254 // Flip Y-Axis of vertex positions
1255 if (flipY) {
1256 vertex.pos.y *= -1.0f;
1257 vertex.normal.y *= -1.0f;
1258 }
1259 // Pre-Multiply vertex colors with material base color
1260 if (preMultiplyColor) {
1261 vertex.color = primitive->material.baseColorFactor * vertex.color;
1262 }
1263 }
1264 }
1265 }
1266 }
1267 }
1268
1269 for (auto extension : gltfModel.extensionsUsed) {
1270 if (extension == "KHR_materials_pbrSpecularGlossiness") {
1271 std::cout << "Required extension: " << extension;
1273 }
1274 }
1275
1276 size_t vertexBufferSize = vertexBuffer.size() * sizeof(Vertex);
1277 size_t indexBufferSize = indexBuffer.size() * sizeof(uint32_t);
1278 indices.count = static_cast<uint32_t>(indexBuffer.size());
1279 vertices.count = static_cast<uint32_t>(vertexBuffer.size());
1280
1281 assert((vertexBufferSize > 0) && (indexBufferSize > 0));
1282
1283 struct StagingBuffer {
1284 VkBuffer buffer;
1285 VkDeviceMemory memory;
1286 } vertexStaging, indexStaging;
1287
1288 // Create staging buffers
1289 // Vertex data
1290 VK_CHECK_RESULT(ctx->createBuffer(
1291 VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
1292 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
1293 vertexBufferSize,
1294 &vertexStaging.buffer,
1295 &vertexStaging.memory,
1296 vertexBuffer.data()));
1297 // Index data
1298 VK_CHECK_RESULT(ctx->createBuffer(
1299 VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
1300 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
1301 indexBufferSize,
1302 &indexStaging.buffer,
1303 &indexStaging.memory,
1304 indexBuffer.data()));
1305
1306 // Create device local buffers
1307 // Vertex buffer
1308 VK_CHECK_RESULT(ctx->createBuffer(
1309 VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | memoryPropertyFlags,
1310 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
1311 vertexBufferSize,
1312 &vertices.buffer,
1313 &vertices.memory));
1314 // Index buffer
1315 VK_CHECK_RESULT(ctx->createBuffer(
1316 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | memoryPropertyFlags,
1317 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
1318 indexBufferSize,
1319 &indices.buffer,
1320 &indices.memory));
1321
1322 // Copy from staging buffers
1323 VkCommandBuffer copyCmd = ctx->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
1324
1325 VkBufferCopy copyRegion = {};
1326
1327 copyRegion.size = vertexBufferSize;
1328 vkCmdCopyBuffer(copyCmd, vertexStaging.buffer, vertices.buffer, 1, &copyRegion);
1329
1330 copyRegion.size = indexBufferSize;
1331 vkCmdCopyBuffer(copyCmd, indexStaging.buffer, indices.buffer, 1, &copyRegion);
1332
1333 ctx->flushCommandBuffer(copyCmd, ctx->graphicsQueueHandle(), true);
1334
1335 vkDestroyBuffer(ctx->deviceHandle(), vertexStaging.buffer, nullptr);
1336 vkFreeMemory(ctx->deviceHandle(), vertexStaging.memory, nullptr);
1337 vkDestroyBuffer(ctx->deviceHandle(), indexStaging.buffer, nullptr);
1338 vkFreeMemory(ctx->deviceHandle(), indexStaging.memory, nullptr);
1339
1341
1342 // Setup descriptors
1343 uint32_t uboCount{ 0 };
1344 uint32_t imageCount{ 0 };
1345 for (auto node : linearNodes) {
1346 if (node->mesh) {
1347 uboCount++;
1348 }
1349 }
1350 for (auto material : materials) {
1351 if (material.baseColorTexture != nullptr) {
1352 imageCount++;
1353 }
1354 }
1355 std::vector<VkDescriptorPoolSize> poolSizes = {
1356 { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uboCount },
1357 };
1358 if (imageCount > 0) {
1360 poolSizes.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageCount });
1361 }
1363 poolSizes.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageCount });
1364 }
1365 }
1366 VkDescriptorPoolCreateInfo descriptorPoolCI{};
1367 descriptorPoolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
1368 descriptorPoolCI.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
1369 descriptorPoolCI.pPoolSizes = poolSizes.data();
1370 descriptorPoolCI.maxSets = uboCount + imageCount;
1371 VK_CHECK_RESULT(vkCreateDescriptorPool(ctx->deviceHandle(), &descriptorPoolCI, nullptr, &descriptorPool));
1372
1373 // Descriptors for per-node uniform buffers
1374 {
1375 // Layout is global, so only create if it hasn't already been created before
1376 if (descriptorSetLayoutUbo == VK_NULL_HANDLE) {
1377 std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
1378 vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0),
1379 };
1380 VkDescriptorSetLayoutCreateInfo descriptorLayoutCI{};
1381 descriptorLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
1382 descriptorLayoutCI.bindingCount = static_cast<uint32_t>(setLayoutBindings.size());
1383 descriptorLayoutCI.pBindings = setLayoutBindings.data();
1384 VK_CHECK_RESULT(vkCreateDescriptorSetLayout(ctx->deviceHandle(), &descriptorLayoutCI, nullptr, &descriptorSetLayoutUbo));
1385 }
1386 for (auto node : nodes) {
1388 }
1389 }
1390
1391 // Descriptors for per-material images
1392 {
1393 // Layout is global, so only create if it hasn't already been created before
1394 if (descriptorSetLayoutImage == VK_NULL_HANDLE) {
1395 std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings{};
1397 setLayoutBindings.push_back(vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, static_cast<uint32_t>(setLayoutBindings.size())));
1398 }
1400 setLayoutBindings.push_back(vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, static_cast<uint32_t>(setLayoutBindings.size())));
1401 }
1402 VkDescriptorSetLayoutCreateInfo descriptorLayoutCI{};
1403 descriptorLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
1404 descriptorLayoutCI.bindingCount = static_cast<uint32_t>(setLayoutBindings.size());
1405 descriptorLayoutCI.pBindings = setLayoutBindings.data();
1406 VK_CHECK_RESULT(vkCreateDescriptorSetLayout(ctx->deviceHandle(), &descriptorLayoutCI, nullptr, &descriptorSetLayoutImage));
1407 }
1408 for (auto& material : materials) {
1409 if (material.baseColorTexture != nullptr) {
1411 }
1412 }
1413 }
1414}
1415
1416void vkglTF::Model::bindBuffers(VkCommandBuffer commandBuffer)
1417{
1418 const VkDeviceSize offsets[1] = {0};
1419 vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertices.buffer, offsets);
1420 vkCmdBindIndexBuffer(commandBuffer, indices.buffer, 0, VK_INDEX_TYPE_UINT32);
1421 buffersBound = true;
1422}
1423
1424void vkglTF::Model::drawNode(Node *node, VkCommandBuffer commandBuffer, uint32_t renderFlags, VkPipelineLayout pipelineLayout, uint32_t bindImageSet)
1425{
1426 if (node->mesh) {
1427 for (Primitive* primitive : node->mesh->primitives) {
1428 bool skip = false;
1429 const vkglTF::Material& material = primitive->material;
1430 if (renderFlags & RenderFlags::RenderOpaqueNodes) {
1431 skip = (material.alphaMode != Material::ALPHAMODE_OPAQUE);
1432 }
1433 if (renderFlags & RenderFlags::RenderAlphaMaskedNodes) {
1434 skip = (material.alphaMode != Material::ALPHAMODE_MASK);
1435 }
1436 if (renderFlags & RenderFlags::RenderAlphaBlendedNodes) {
1437 skip = (material.alphaMode != Material::ALPHAMODE_BLEND);
1438 }
1439 if (!skip) {
1440 if (renderFlags & RenderFlags::BindImages) {
1441 vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, bindImageSet, 1, &material.descriptorSet, 0, nullptr);
1442 }
1443 vkCmdDrawIndexed(commandBuffer, primitive->indexCount, 1, primitive->firstIndex, 0, 0);
1444 }
1445 }
1446 }
1447 for (auto& child : node->children) {
1448 drawNode(child, commandBuffer, renderFlags);
1449 }
1450}
1451
1452void vkglTF::Model::draw(VkCommandBuffer commandBuffer, uint32_t renderFlags, VkPipelineLayout pipelineLayout, uint32_t bindImageSet)
1453{
1454 if (!buffersBound) {
1455 const VkDeviceSize offsets[1] = {0};
1456 vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertices.buffer, offsets);
1457 vkCmdBindIndexBuffer(commandBuffer, indices.buffer, 0, VK_INDEX_TYPE_UINT32);
1458 }
1459 for (auto& node : nodes) {
1460 drawNode(node, commandBuffer, renderFlags, pipelineLayout, bindImageSet);
1461 }
1462}
1463
1464void vkglTF::Model::getNodeDimensions(Node *node, glm::vec3 &min, glm::vec3 &max)
1465{
1466 if (node->mesh) {
1467 for (Primitive *primitive : node->mesh->primitives) {
1468 glm::vec4 locMin = glm::vec4(primitive->dimensions.min, 1.0f) * node->getMatrix();
1469 glm::vec4 locMax = glm::vec4(primitive->dimensions.max, 1.0f) * node->getMatrix();
1470 if (locMin.x < min.x) { min.x = locMin.x; }
1471 if (locMin.y < min.y) { min.y = locMin.y; }
1472 if (locMin.z < min.z) { min.z = locMin.z; }
1473 if (locMax.x > max.x) { max.x = locMax.x; }
1474 if (locMax.y > max.y) { max.y = locMax.y; }
1475 if (locMax.z > max.z) { max.z = locMax.z; }
1476 }
1477 }
1478 for (auto child : node->children) {
1479 getNodeDimensions(child, min, max);
1480 }
1481}
1482
1484{
1485 dimensions.min = glm::vec3(FLT_MAX);
1486 dimensions.max = glm::vec3(-FLT_MAX);
1487 for (auto node : nodes) {
1488 getNodeDimensions(node, dimensions.min, dimensions.max);
1489 }
1490 dimensions.size = dimensions.max - dimensions.min;
1491 dimensions.center = (dimensions.min + dimensions.max) / 2.0f;
1492 dimensions.radius = glm::distance(dimensions.min, dimensions.max) / 2.0f;
1493}
1494
1495void vkglTF::Model::updateAnimation(uint32_t index, float time)
1496{
1497 if (index > static_cast<uint32_t>(animations.size()) - 1) {
1498 std::cout << "No animation with index " << index << std::endl;
1499 return;
1500 }
1501 Animation &animation = animations[index];
1502
1503 bool updated = false;
1504 for (auto& channel : animation.channels) {
1505 vkglTF::AnimationSampler &sampler = animation.samplers[channel.samplerIndex];
1506 if (sampler.inputs.size() > sampler.outputsVec4.size()) {
1507 continue;
1508 }
1509
1510 for (auto i = 0; i < sampler.inputs.size() - 1; i++) {
1511 if ((time >= sampler.inputs[i]) && (time <= sampler.inputs[i + 1])) {
1512 float u = std::max(0.0f, time - sampler.inputs[i]) / (sampler.inputs[i + 1] - sampler.inputs[i]);
1513 if (u <= 1.0f) {
1514 switch (channel.path) {
1516 glm::vec4 trans = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], u);
1517 channel.node->translation = glm::vec3(trans);
1518 break;
1519 }
1521 glm::vec4 trans = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], u);
1522 channel.node->scale = glm::vec3(trans);
1523 break;
1524 }
1526 glm::quat q1;
1527 q1.x = sampler.outputsVec4[i].x;
1528 q1.y = sampler.outputsVec4[i].y;
1529 q1.z = sampler.outputsVec4[i].z;
1530 q1.w = sampler.outputsVec4[i].w;
1531 glm::quat q2;
1532 q2.x = sampler.outputsVec4[i + 1].x;
1533 q2.y = sampler.outputsVec4[i + 1].y;
1534 q2.z = sampler.outputsVec4[i + 1].z;
1535 q2.w = sampler.outputsVec4[i + 1].w;
1536 channel.node->rotation = glm::normalize(glm::slerp(q1, q2, u));
1537 break;
1538 }
1539 }
1540 updated = true;
1541 }
1542 }
1543 }
1544 }
1545 if (updated) {
1546 for (auto &node : nodes) {
1547 node->update();
1548 }
1549 }
1550}
1551
1552/*
1553 Helper functions
1554*/
1555vkglTF::Node* vkglTF::Model::findNode(Node *parent, uint32_t index) {
1556 Node* nodeFound = nullptr;
1557 if (parent->index == index) {
1558 return parent;
1559 }
1560 for (auto& child : parent->children) {
1561 nodeFound = findNode(child, index);
1562 if (nodeFound) {
1563 break;
1564 }
1565 }
1566 return nodeFound;
1567}
1568
1570 Node* nodeFound = nullptr;
1571 for (auto &node : nodes) {
1572 nodeFound = findNode(node, index);
1573 if (nodeFound) {
1574 break;
1575 }
1576 }
1577 return nodeFound;
1578}
1579
1580void vkglTF::Model::prepareNodeDescriptor(vkglTF::Node* node, VkDescriptorSetLayout descriptorSetLayout) {
1581 if (node->mesh) {
1582 VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
1583 descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
1584 descriptorSetAllocInfo.descriptorPool = descriptorPool;
1585 descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayout;
1586 descriptorSetAllocInfo.descriptorSetCount = 1;
1587 VK_CHECK_RESULT(vkAllocateDescriptorSets(ctx->deviceHandle(), &descriptorSetAllocInfo, &node->mesh->uniformBuffer.descriptorSet));
1588
1589 VkWriteDescriptorSet writeDescriptorSet{};
1590 writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1591 writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
1592 writeDescriptorSet.descriptorCount = 1;
1593 writeDescriptorSet.dstSet = node->mesh->uniformBuffer.descriptorSet;
1594 writeDescriptorSet.dstBinding = 0;
1595 writeDescriptorSet.pBufferInfo = &node->mesh->uniformBuffer.descriptor;
1596
1597 vkUpdateDescriptorSets(ctx->deviceHandle(), 1, &writeDescriptorSet, 0, nullptr);
1598 }
1599 for (auto& child : node->children) {
1600 prepareNodeDescriptor(child, descriptorSetLayout);
1601 }
1602}
assert(queueCount >=1)
#define VK_CHECK_RESULT(f)
Definition VulkanTools.h:55
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)
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)
VkContext * currentContext()
Definition VkSystem.h:21
static VkSystem * instance()
Definition VkSystem.cpp:10
vkglTF::Texture * metallicRoughnessTexture
dyno::VkContext * ctx
VkDescriptorSet descriptorSet
void createDescriptorSet(VkDescriptorPool descriptorPool, VkDescriptorSetLayout descriptorSetLayout, uint32_t descriptorBindingFlags)
vkglTF::Texture * normalTexture
glm::vec4 baseColorFactor
vkglTF::Texture * occlusionTexture
vkglTF::Texture * emissiveTexture
vkglTF::Texture * baseColorTexture
void loadFromFile(std::string filename, uint32_t fileLoadingFlags=vkglTF::FileLoadingFlags::None, float scale=1.0f)
void drawNode(Node *node, VkCommandBuffer commandBuffer, uint32_t renderFlags=0, VkPipelineLayout pipelineLayout=VK_NULL_HANDLE, uint32_t bindImageSet=1)
struct vkglTF::Model::Indices indices
void loadNode(vkglTF::Node *parent, const tinygltf::Node &node, uint32_t nodeIndex, const tinygltf::Model &model, std::vector< uint32_t > &indexBuffer, std::vector< Vertex > &vertexBuffer, float globalscale)
std::vector< Node * > nodes
struct vkglTF::Model::Vertices vertices
void bindBuffers(VkCommandBuffer commandBuffer)
vkglTF::Texture * getTexture(uint32_t index)
std::string path
std::vector< Texture > textures
void loadMaterials(tinygltf::Model &gltfModel)
void createEmptyTexture(VkQueue transferQueue)
void prepareNodeDescriptor(vkglTF::Node *node, VkDescriptorSetLayout descriptorSetLayout)
void updateAnimation(uint32_t index, float time)
VkDescriptorPool descriptorPool
Node * findNode(Node *parent, uint32_t index)
void getNodeDimensions(Node *node, glm::vec3 &min, glm::vec3 &max)
std::vector< Material > materials
void loadAnimations(tinygltf::Model &gltfModel)
std::vector< Animation > animations
std::vector< Skin * > skins
bool metallicRoughnessWorkflow
std::vector< Node * > linearNodes
void draw(VkCommandBuffer commandBuffer, uint32_t renderFlags=0, VkPipelineLayout pipelineLayout=VK_NULL_HANDLE, uint32_t bindImageSet=1)
Node * nodeFromIndex(uint32_t index)
void loadImages(tinygltf::Model &gltfModel)
vkglTF::Texture emptyTexture
dyno::VkContext * ctx
struct vkglTF::Model::Dimensions dimensions
void loadSkins(tinygltf::Model &gltfModel)
void fromglTfImage(tinygltf::Image &gltfimage, std::string path)
VkDescriptorImageInfo descriptor
dyno::VkContext * ctx
VkImageLayout imageLayout
VkDeviceMemory deviceMemory
VkDescriptorSetLayout descriptorSetLayoutImage
VkMemoryPropertyFlags memoryPropertyFlags
VkDescriptorSetLayout descriptorSetLayoutUbo
uint32_t descriptorBindingFlags
@ PreTransformVertices
@ PreMultiplyVertexColors
@ RenderAlphaBlendedNodes
@ RenderAlphaMaskedNodes
VkDescriptorSetLayoutBinding descriptorSetLayoutBinding(VkDescriptorType type, VkShaderStageFlags stageFlags, uint32_t binding, uint32_t descriptorCount=1)
VkMemoryAllocateInfo memoryAllocateInfo()
VkImageCreateInfo imageCreateInfo()
VkSamplerCreateInfo samplerCreateInfo()
VkImageViewCreateInfo imageViewCreateInfo()
VkBufferCreateInfo bufferCreateInfo()
bool fileExists(const std::string &filename)
Checks if a file exists.
void setImageLayout(VkCommandBuffer cmdbuffer, VkImage image, VkImageLayout oldImageLayout, VkImageLayout newImageLayout, VkImageSubresourceRange subresourceRange, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask)
void exitFatal(const std::string &message, int32_t exitCode)
std::vector< AnimationSampler > samplers
std::vector< AnimationChannel > channels
std::vector< glm::vec4 > outputsVec4
std::vector< float > inputs
VkDescriptorBufferInfo descriptor
dyno::VkContext * ctx
std::vector< Primitive * > primitives
struct vkglTF::Mesh::UniformBuffer uniformBuffer
Mesh(glm::mat4 matrix)
std::string name
struct vkglTF::Mesh::UniformBlock uniformBlock
std::vector< Node * > children
std::string name
glm::quat rotation
glm::vec3 translation
glm::mat4 getMatrix()
glm::mat4 localMatrix()
struct vkglTF::Primitive::Dimensions dimensions
void setDimensions(glm::vec3 min, glm::vec3 max)
std::string name
std::vector< glm::mat4 > inverseBindMatrices
std::vector< Node * > joints
static std::vector< VkVertexInputAttributeDescription > vertexInputAttributeDescriptions
static VkVertexInputAttributeDescription inputAttributeDescription(uint32_t binding, uint32_t location, VertexComponent component)
static VkPipelineVertexInputStateCreateInfo * getPipelineVertexInputState(const std::vector< VertexComponent > components)
Returns the default pipeline vertex input state create info structure for the requested vertex compon...
static VkVertexInputBindingDescription inputBindingDescription(uint32_t binding)
static std::vector< VkVertexInputAttributeDescription > inputAttributeDescriptions(uint32_t binding, const std::vector< VertexComponent > components)
static VkPipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo
static VkVertexInputBindingDescription vertexInputBindingDescription
#define max(x, y)
Definition svd3_cuda.h:41