diff --git a/base/VulkanDevice.hpp b/base/VulkanDevice.hpp new file mode 100644 index 0000000..038742e --- /dev/null +++ b/base/VulkanDevice.hpp @@ -0,0 +1,389 @@ +/* +* Vulkan device class +* +* Encapsulates a physical Vulkan device and it's logical representation +* +* Copyright (C) 2016-2018 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include "vulkan/vulkan.h" + +#if defined(VK_USE_PLATFORM_MACOS_MVK) && (VK_HEADER_VERSION >= 216) +#include +#endif + +#if defined(VK_USE_PLATFORM_ANDROID_KHR) +#include "VulkanAndroid.h" +#endif + +#include "macros.h" + +namespace vks +{ + struct VulkanDevice + { + VkPhysicalDevice physicalDevice; + VkDevice logicalDevice; + VkPhysicalDeviceProperties properties; + VkPhysicalDeviceFeatures features; + VkPhysicalDeviceFeatures enabledFeatures; + VkPhysicalDeviceMemoryProperties memoryProperties; + std::vector queueFamilyProperties; + VkCommandPool commandPool = VK_NULL_HANDLE; + + struct { + uint32_t graphics; + uint32_t compute; + } queueFamilyIndices; + + operator VkDevice() { return logicalDevice; }; + + /** + * Default constructor + * + * @param physicalDevice Physical device that is to be used + */ + VulkanDevice(VkPhysicalDevice physicalDevice) + { + assert(physicalDevice); + this->physicalDevice = physicalDevice; + + // Store Properties features, limits and properties of the physical device for later use + // Device properties also contain limits and sparse properties + vkGetPhysicalDeviceProperties(physicalDevice, &properties); + // Features should be checked by the examples before using them + vkGetPhysicalDeviceFeatures(physicalDevice, &features); + // Memory properties are used regularly for creating all kinds of buffers + vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties); + // Queue family properties, used for setting up requested queues upon device creation + uint32_t queueFamilyCount; + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); + assert(queueFamilyCount > 0); + queueFamilyProperties.resize(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilyProperties.data()); + } + + /** + * Default destructor + * + * @note Frees the logical device + */ + ~VulkanDevice() + { + if (commandPool) { + vkDestroyCommandPool(logicalDevice, commandPool, nullptr); + } + if (logicalDevice) { + vkDestroyDevice(logicalDevice, nullptr); + } + } + + /** + * Get the index of a memory type that has all the requested property bits set + * + * @param typeBits Bitmask with bits set for each memory type supported by the resource to request for (from VkMemoryRequirements) + * @param properties Bitmask of properties for the memory type to request + * @param (Optional) memTypeFound Pointer to a bool that is set to true if a matching memory type has been found + * + * @return Index of the requested memory type + * + * @throw Throws an exception if memTypeFound is null and no memory type could be found that supports the requested properties + */ + uint32_t getMemoryType(uint32_t typeBits, VkMemoryPropertyFlags properties, VkBool32 *memTypeFound = nullptr) + { + for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) { + if ((typeBits & 1) == 1) { + if ((memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) { + if (memTypeFound) { + *memTypeFound = true; + } + return i; + } + } + typeBits >>= 1; + } + + if (memTypeFound) { + *memTypeFound = false; + return 0; + } else { + throw std::runtime_error("Could not find a matching memory type"); + } + } + + /** + * Get the index of a queue family that supports the requested queue flags + * + * @param queueFlags Queue flags to find a queue family index for + * + * @return Index of the queue family index that matches the flags + * + * @throw Throws an exception if no queue family index could be found that supports the requested flags + */ + uint32_t getQueueFamilyIndex(VkQueueFlagBits queueFlags) + { + // Dedicated queue for compute + // Try to find a queue family index that supports compute but not graphics + if (queueFlags & VK_QUEUE_COMPUTE_BIT) + { + for (uint32_t i = 0; i < static_cast(queueFamilyProperties.size()); i++) { + if ((queueFamilyProperties[i].queueFlags & queueFlags) && ((queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) == 0)) { + return i; + break; + } + } + } + + // For other queue types or if no separate compute queue is present, return the first one to support the requested flags + for (uint32_t i = 0; i < static_cast(queueFamilyProperties.size()); i++) { + if (queueFamilyProperties[i].queueFlags & queueFlags) { + return i; + break; + } + } + + throw std::runtime_error("Could not find a matching queue family index"); + } + + /** + * Create the logical device based on the assigned physical device, also gets default queue family indices + * + * @param enabledFeatures Can be used to enable certain features upon device creation + * @param requestedQueueTypes Bit flags specifying the queue types to be requested from the device + * + * @return VkResult of the device creation call + */ + VkResult createLogicalDevice(VkPhysicalDeviceFeatures enabledFeatures, std::vector enabledExtensions, VkQueueFlags requestedQueueTypes = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT) + { + // Desired queues need to be requested upon logical device creation + // Due to differing queue family configurations of Vulkan implementations this can be a bit tricky, especially if the application + // requests different queue types + + std::vector queueCreateInfos{}; + + // Get queue family indices for the requested queue family types + // Note that the indices may overlap depending on the implementation + + const float defaultQueuePriority(0.0f); + + // Graphics queue + if (requestedQueueTypes & VK_QUEUE_GRAPHICS_BIT) { + queueFamilyIndices.graphics = getQueueFamilyIndex(VK_QUEUE_GRAPHICS_BIT); + VkDeviceQueueCreateInfo queueInfo{}; + queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueInfo.queueFamilyIndex = queueFamilyIndices.graphics; + queueInfo.queueCount = 1; + queueInfo.pQueuePriorities = &defaultQueuePriority; + queueCreateInfos.push_back(queueInfo); + } else { + queueFamilyIndices.graphics = 0; + } + + // Dedicated compute queue + if (requestedQueueTypes & VK_QUEUE_COMPUTE_BIT) { + queueFamilyIndices.compute = getQueueFamilyIndex(VK_QUEUE_COMPUTE_BIT); + if (queueFamilyIndices.compute != queueFamilyIndices.graphics) { + // If compute family index differs, we need an additional queue create info for the compute queue + VkDeviceQueueCreateInfo queueInfo{}; + queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueInfo.queueFamilyIndex = queueFamilyIndices.compute; + queueInfo.queueCount = 1; + queueInfo.pQueuePriorities = &defaultQueuePriority; + queueCreateInfos.push_back(queueInfo); + } + } else { + // Else we use the same queue + queueFamilyIndices.compute = queueFamilyIndices.graphics; + } + + // Create the logical device representation + std::vector deviceExtensions(enabledExtensions); + deviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + +#if defined(VK_USE_PLATFORM_MACOS_MVK) && (VK_HEADER_VERSION >= 216) + deviceExtensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); +#endif + + VkDeviceCreateInfo deviceCreateInfo = {}; + deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + deviceCreateInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size());; + deviceCreateInfo.pQueueCreateInfos = queueCreateInfos.data(); + deviceCreateInfo.pEnabledFeatures = &enabledFeatures; + + if (deviceExtensions.size() > 0) { + deviceCreateInfo.enabledExtensionCount = (uint32_t)deviceExtensions.size(); + deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.data(); + } + + VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, nullptr, &logicalDevice); + + if (result == VK_SUCCESS) { + commandPool = createCommandPool(queueFamilyIndices.graphics); + } + + this->enabledFeatures = enabledFeatures; + + return result; + } + + /** + * Create a buffer on the device + * + * @param usageFlags Usage flag bitmask for the buffer (i.e. index, vertex, uniform buffer) + * @param memoryPropertyFlags Memory properties for this buffer (i.e. device local, host visible, coherent) + * @param size Size of the buffer in byes + * @param buffer Pointer to the buffer handle acquired by the function + * @param memory Pointer to the memory handle acquired by the function + * @param data Pointer to the data that should be copied to the buffer after creation (optional, if not set, no data is copied over) + * + * @return VK_SUCCESS if buffer handle and memory have been created and (optionally passed) data has been copied + */ + VkResult createBuffer(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, VkBuffer *buffer, VkDeviceMemory *memory, void *data = nullptr) + { + // Create the buffer handle + VkBufferCreateInfo bufferCreateInfo{}; + bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferCreateInfo.usage = usageFlags; + bufferCreateInfo.size = size; + bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + VK_CHECK_RESULT(vkCreateBuffer(logicalDevice, &bufferCreateInfo, nullptr, buffer)); + + // Create the memory backing up the buffer handle + VkMemoryRequirements memReqs; + VkMemoryAllocateInfo memAlloc{}; + memAlloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + vkGetBufferMemoryRequirements(logicalDevice, *buffer, &memReqs); + memAlloc.allocationSize = memReqs.size; + // Find a memory type index that fits the properties of the buffer + memAlloc.memoryTypeIndex = getMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags); + VK_CHECK_RESULT(vkAllocateMemory(logicalDevice, &memAlloc, nullptr, memory)); + + // If a pointer to the buffer data has been passed, map the buffer and copy over the data + if (data != nullptr) + { + void *mapped; + VK_CHECK_RESULT(vkMapMemory(logicalDevice, *memory, 0, size, 0, &mapped)); + memcpy(mapped, data, size); + // If host coherency hasn't been requested, do a manual flush to make writes visible + if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0) + { + VkMappedMemoryRange mappedRange{}; + mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + mappedRange.memory = *memory; + mappedRange.offset = 0; + mappedRange.size = size; + vkFlushMappedMemoryRanges(logicalDevice, 1, &mappedRange); + } + vkUnmapMemory(logicalDevice, *memory); + } + + // Attach the memory to the buffer object + VK_CHECK_RESULT(vkBindBufferMemory(logicalDevice, *buffer, *memory, 0)); + + return VK_SUCCESS; + } + + /** + * Create a command pool for allocation command buffers from + * + * @param queueFamilyIndex Family index of the queue to create the command pool for + * @param createFlags (Optional) Command pool creation flags (Defaults to VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT) + * + * @note Command buffers allocated from the created pool can only be submitted to a queue with the same family index + * + * @return A handle to the created command buffer + */ + VkCommandPool createCommandPool(uint32_t queueFamilyIndex, VkCommandPoolCreateFlags createFlags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT) + { + VkCommandPoolCreateInfo cmdPoolInfo = {}; + cmdPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmdPoolInfo.queueFamilyIndex = queueFamilyIndex; + cmdPoolInfo.flags = createFlags; + VkCommandPool cmdPool; + VK_CHECK_RESULT(vkCreateCommandPool(logicalDevice, &cmdPoolInfo, nullptr, &cmdPool)); + return cmdPool; + } + + /** + * Allocate a command buffer from the command pool + * + * @param level Level of the new command buffer (primary or secondary) + * @param (Optional) begin If true, recording on the new command buffer will be started (vkBeginCommandBuffer) (Defaults to false) + * + * @return A handle to the allocated command buffer + */ + VkCommandBuffer createCommandBuffer(VkCommandBufferLevel level, bool begin = false) + { + VkCommandBufferAllocateInfo cmdBufAllocateInfo{}; + cmdBufAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmdBufAllocateInfo.commandPool = commandPool; + cmdBufAllocateInfo.level = level; + cmdBufAllocateInfo.commandBufferCount = 1; + + VkCommandBuffer cmdBuffer; + VK_CHECK_RESULT(vkAllocateCommandBuffers(logicalDevice, &cmdBufAllocateInfo, &cmdBuffer)); + + // If requested, also start recording for the new command buffer + if (begin) { + VkCommandBufferBeginInfo commandBufferBI{}; + commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + VK_CHECK_RESULT(vkBeginCommandBuffer(cmdBuffer, &commandBufferBI)); + } + + return cmdBuffer; + } + + void beginCommandBuffer(VkCommandBuffer commandBuffer) + { + VkCommandBufferBeginInfo commandBufferBI{}; + commandBufferBI.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + VK_CHECK_RESULT(vkBeginCommandBuffer(commandBuffer, &commandBufferBI)); + } + + /** + * Finish command buffer recording and submit it to a queue + * + * @param commandBuffer Command buffer to flush + * @param queue Queue to submit the command buffer to + * @param free (Optional) Free the command buffer once it has been submitted (Defaults to true) + * + * @note The queue that the command buffer is submitted to must be from the same family index as the pool it was allocated from + * @note Uses a fence to ensure command buffer has finished executing + */ + void flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, bool free = true) + { + VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer)); + + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &commandBuffer; + + // Create fence to ensure that the command buffer has finished executing + VkFenceCreateInfo fenceInfo{}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + VkFence fence; + VK_CHECK_RESULT(vkCreateFence(logicalDevice, &fenceInfo, nullptr, &fence)); + + // Submit to the queue + VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, fence)); + // Wait for the fence to signal that command buffer has finished executing + VK_CHECK_RESULT(vkWaitForFences(logicalDevice, 1, &fence, VK_TRUE, 100000000000)); + + vkDestroyFence(logicalDevice, fence, nullptr); + + if (free) { + vkFreeCommandBuffers(logicalDevice, commandPool, 1, &commandBuffer); + } + } + }; +} diff --git a/base/VulkanUtils.hpp b/base/VulkanUtils.hpp new file mode 100644 index 0000000..92b4b5f --- /dev/null +++ b/base/VulkanUtils.hpp @@ -0,0 +1,187 @@ +/* +* Vulkan utilities +* +* Copyright(C) 2018 by Sascha Willems - www.saschawillems.de +* +* This code is licensed under the MIT license(MIT) (http://opensource.org/licenses/MIT) +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "vulkan/vulkan.h" +#include "VulkanDevice.hpp" +#if defined(__ANDROID__) +#include +#elif defined(__linux__) +#include +#endif + +/* + Vulkan buffer object +*/ +struct Buffer { + VkDevice device; + VkBuffer buffer = VK_NULL_HANDLE; + VkDeviceMemory memory = VK_NULL_HANDLE; + VkDescriptorBufferInfo descriptor; + int32_t count = 0; + void *mapped = nullptr; + void create(vks::VulkanDevice *device, VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, VkDeviceSize size, bool map = true) { + this->device = device->logicalDevice; + device->createBuffer(usageFlags, memoryPropertyFlags, size, &buffer, &memory); + descriptor = { buffer, 0, size }; + if (map) { + VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, memory, 0, size, 0, &mapped)); + } + } + void destroy() { + if (mapped) { + unmap(); + } + vkDestroyBuffer(device, buffer, nullptr); + vkFreeMemory(device, memory, nullptr); + buffer = VK_NULL_HANDLE; + memory = VK_NULL_HANDLE; + } + void map() { + VK_CHECK_RESULT(vkMapMemory(device, memory, 0, VK_WHOLE_SIZE, 0, &mapped)); + } + void unmap() { + if (mapped) { + vkUnmapMemory(device, memory); + mapped = nullptr; + } + } + void flush(VkDeviceSize size = VK_WHOLE_SIZE) { + VkMappedMemoryRange mappedRange{}; + mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + mappedRange.memory = memory; + mappedRange.size = size; + VK_CHECK_RESULT(vkFlushMappedMemoryRanges(device, 1, &mappedRange)); + } +}; + +VkPipelineShaderStageCreateInfo loadShader(VkDevice device, std::string filename, VkShaderStageFlagBits stage) +{ + VkPipelineShaderStageCreateInfo shaderStage{}; + shaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStage.stage = stage; + shaderStage.pName = "main"; +#if defined(VK_USE_PLATFORM_ANDROID_KHR) + std::string assetpath = "shaders/" + filename; + AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, assetpath.c_str(), AASSET_MODE_STREAMING); + assert(asset); + size_t size = AAsset_getLength(asset); + assert(size > 0); + char *shaderCode = new char[size]; + AAsset_read(asset, shaderCode, size); + AAsset_close(asset); + VkShaderModule shaderModule; + VkShaderModuleCreateInfo moduleCreateInfo; + moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + moduleCreateInfo.pNext = NULL; + moduleCreateInfo.codeSize = size; + moduleCreateInfo.pCode = (uint32_t*)shaderCode; + moduleCreateInfo.flags = 0; + VK_CHECK_RESULT(vkCreateShaderModule(device, &moduleCreateInfo, NULL, &shaderStage.module)); + delete[] shaderCode; +#else + std::ifstream is("./../data/shaders/" + filename, std::ios::binary | std::ios::in | std::ios::ate); + + if (is.is_open()) { + size_t size = is.tellg(); + is.seekg(0, std::ios::beg); + char* shaderCode = new char[size]; + is.read(shaderCode, size); + is.close(); + assert(size > 0); + VkShaderModuleCreateInfo moduleCreateInfo{}; + moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + moduleCreateInfo.codeSize = size; + moduleCreateInfo.pCode = (uint32_t*)shaderCode; + vkCreateShaderModule(device, &moduleCreateInfo, NULL, &shaderStage.module); + delete[] shaderCode; + } + else { + std::cerr << "Error: Could not open shader file \"" << filename << "\"" << std::endl; + shaderStage.module = VK_NULL_HANDLE; + } + +#endif + assert(shaderStage.module != VK_NULL_HANDLE); + return shaderStage; +} + +void readDirectory(const std::string& directory, const std::string &pattern, std::map &filelist, bool recursive) +{ +#if defined(VK_USE_PLATFORM_ANDROID_KHR) + AAssetDir* assetDir = AAssetManager_openDir(androidApp->activity->assetManager, directory.c_str()); + AAssetDir_rewind(assetDir); + const char* assetName; + while ((assetName = AAssetDir_getNextFileName(assetDir)) != 0) { + std::string filename(assetName); + filename.erase(filename.find_last_of("."), std::string::npos); + filelist[filename] = directory + "/" + assetName; + } + AAssetDir_close(assetDir); +#elif defined(VK_USE_PLATFORM_WIN32_KHR) + std::string searchpattern(directory + "/" + pattern); + WIN32_FIND_DATA data; + HANDLE hFind; + if ((hFind = FindFirstFile(searchpattern.c_str(), &data)) != INVALID_HANDLE_VALUE) { + do { + std::string filename(data.cFileName); + filename.erase(filename.find_last_of("."), std::string::npos); + filelist[filename] = directory + "/" + data.cFileName; + } while (FindNextFile(hFind, &data) != 0); + FindClose(hFind); + } + if (recursive) { + std::string dirpattern = directory + "/*"; + if ((hFind = FindFirstFile(dirpattern.c_str(), &data)) != INVALID_HANDLE_VALUE) { + do { + if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + char subdir[MAX_PATH]; + strcpy(subdir, directory.c_str()); + strcat(subdir, "/"); + strcat(subdir, data.cFileName); + if ((strcmp(data.cFileName, ".") != 0) && (strcmp(data.cFileName, "..") != 0)) { + readDirectory(subdir, pattern, filelist, recursive); + } + } + } while (FindNextFile(hFind, &data) != 0); + FindClose(hFind); + } + } +#elif defined(__linux__) + std::string patternExt = pattern; + patternExt.erase(0, pattern.find_last_of(".")); + struct dirent *entry; + DIR *dir = opendir(directory.c_str()); + if (dir == NULL) { + return; + } + while ((entry = readdir(dir)) != NULL) { + if (entry->d_type == DT_REG) { + std::string filename(entry->d_name); + if (filename.find(patternExt) != std::string::npos) { + filename.erase(filename.find_last_of("."), std::string::npos); + filelist[filename] = directory + "/" + entry->d_name; + } + } + if (recursive && (entry->d_type == DT_DIR)) { + std::string subdir = directory + "/" + entry->d_name; + if ((strcmp(entry->d_name, ".") != 0) && (strcmp(entry->d_name, "..") != 0)) { + readDirectory(subdir, pattern, filelist, recursive); + } + } + } + closedir(dir); +#endif +} \ No newline at end of file diff --git a/src/render/glTFModel.cpp b/src/render/glTFModel.cpp index 838ddd5..6af2913 100644 --- a/src/render/glTFModel.cpp +++ b/src/render/glTFModel.cpp @@ -5,19 +5,20 @@ #define STB_IMAGE_IMPLEMENTATION #define STBI_MSC_SECURE_CRT -#include "VulkanglTFModel.h" -namespace glTFModel -{ +#include "VulkanglTFModel.h" +#include "glTFModel.h" + + // Bounding box - BoundingBox::BoundingBox() { + glTFModel::BoundingBox::BoundingBox() { }; - BoundingBox::BoundingBox(glm::vec3 min, glm::vec3 max) : min(min), max(max) { + glTFModel::BoundingBox::BoundingBox(glm::vec3 min, glm::vec3 max) : min(min), max(max) { }; - BoundingBox BoundingBox::getAABB(glm::mat4 m) { + glTFModel::BoundingBox glTFModel::BoundingBox::getAABB(glm::mat4 m) { glm::vec3 min = glm::vec3(m[3]); glm::vec3 max = min; glm::vec3 v0, v1; @@ -44,14 +45,14 @@ namespace glTFModel } // Texture - void Texture::updateDescriptor() + void glTFModel::Texture::updateDescriptor() { descriptor.sampler = sampler; descriptor.imageView = view; descriptor.imageLayout = imageLayout; } - void Texture::destroy() + void glTFModel::Texture::destroy() { vkDestroyImageView(device->logicalDevice, view, nullptr); vkDestroyImage(device->logicalDevice, image, nullptr); @@ -59,7 +60,7 @@ namespace glTFModel vkDestroySampler(device->logicalDevice, sampler, nullptr); } - void Texture::fromglTfImage(tinygltf::Image& gltfimage, TextureSampler textureSampler, vks::VulkanDevice* device, VkQueue copyQueue) + void glTFModel::Texture::fromglTfImage(tinygltf::Image& gltfimage, glTFModel::TextureSampler textureSampler, vks::VulkanDevice* device, VkQueue copyQueue) { this->device = device; @@ -297,18 +298,18 @@ namespace glTFModel } // Primitive - Primitive::Primitive(uint32_t firstIndex, uint32_t indexCount, uint32_t vertexCount, Material& material) : firstIndex(firstIndex), indexCount(indexCount), vertexCount(vertexCount), material(material) { + glTFModel::Primitive::Primitive(uint32_t firstIndex, uint32_t indexCount, uint32_t vertexCount, Material& material) : firstIndex(firstIndex), indexCount(indexCount), vertexCount(vertexCount), material(material) { hasIndices = indexCount > 0; }; - void Primitive::setBoundingBox(glm::vec3 min, glm::vec3 max) { + void glTFModel::Primitive::setBoundingBox(glm::vec3 min, glm::vec3 max) { bb.min = min; bb.max = max; bb.valid = true; } // Mesh - Mesh::Mesh(vks::VulkanDevice* device, glm::mat4 matrix) { + glTFModel::Mesh::Mesh(vks::VulkanDevice* device, glm::mat4 matrix) { this->device = device; this->uniformBlock.matrix = matrix; VK_CHECK_RESULT(device->createBuffer( @@ -322,27 +323,27 @@ namespace glTFModel uniformBuffer.descriptor = { uniformBuffer.buffer, 0, sizeof(uniformBlock) }; }; - Mesh::~Mesh() { + glTFModel::Mesh::~Mesh() { vkDestroyBuffer(device->logicalDevice, uniformBuffer.buffer, nullptr); vkFreeMemory(device->logicalDevice, uniformBuffer.memory, nullptr); for (Primitive* p : primitives) delete p; } - void Mesh::setBoundingBox(glm::vec3 min, glm::vec3 max) { + void glTFModel::Mesh::setBoundingBox(glm::vec3 min, glm::vec3 max) { bb.min = min; bb.max = max; bb.valid = true; } // Node - glm::mat4 Node::localMatrix() { + glm::mat4 glTFModel::Node::localMatrix() { return glm::translate(glm::mat4(1.0f), translation) * glm::mat4(rotation) * glm::scale(glm::mat4(1.0f), scale) * matrix; } - glm::mat4 Node::getMatrix() { + glm::mat4 glTFModel::Node::getMatrix() { glm::mat4 m = localMatrix(); - vkglTF::Node* p = parent; + glTFModel::Node* p = parent; while (p) { m = p->localMatrix() * m; p = p->parent; @@ -350,16 +351,16 @@ namespace glTFModel return m; } - void Node::update() { + void glTFModel::Node::update() { if (mesh) { glm::mat4 m = getMatrix(); if (skin) { mesh->uniformBlock.matrix = m; // Update join matrices glm::mat4 inverseTransform = glm::inverse(m); - size_t numJoints = std::min((uint32_t)skin->joints.size(), MAX_NUM_JOINTS); + size_t numJoints = std::min((uint32_t)skin->joints.size(),MAX_NUM_JOINTS); for (size_t i = 0; i < numJoints; i++) { - vkglTF::Node* jointNode = skin->joints[i]; + glTFModel::Node* jointNode = skin->joints[i]; glm::mat4 jointMat = jointNode->getMatrix() * skin->inverseBindMatrices[i]; jointMat = inverseTransform * jointMat; mesh->uniformBlock.jointMatrix[i] = jointMat; @@ -377,7 +378,7 @@ namespace glTFModel } } - Node::~Node() { + glTFModel::Node::~Node() { if (mesh) { delete mesh; } @@ -388,7 +389,7 @@ namespace glTFModel // Model - void Model::destroy(VkDevice device) + void glTFModel::Model::destroy(VkDevice device) { if (vertices.buffer != VK_NULL_HANDLE) { vkDestroyBuffer(device, vertices.buffer, nullptr); @@ -419,9 +420,9 @@ namespace glTFModel skins.resize(0); }; - void Model::loadNode(vkglTF::Node* parent, const tinygltf::Node& node, uint32_t nodeIndex, const tinygltf::Model& model, LoaderInfo& loaderInfo, float globalscale) + void glTFModel::Model::loadNode(glTFModel::Node* parent, const tinygltf::Node& node, uint32_t nodeIndex, const tinygltf::Model& model, LoaderInfo& loaderInfo, float globalscale) { - vkglTF::Node* newNode = new Node{}; + glTFModel::Node* newNode = new Node{}; newNode->index = nodeIndex; newNode->parent = parent; newNode->name = node.name; @@ -650,7 +651,7 @@ namespace glTFModel linearNodes.push_back(newNode); } - void Model::getNodeProps(const tinygltf::Node& node, const tinygltf::Model& model, size_t& vertexCount, size_t& indexCount) + void glTFModel::Model::getNodeProps(const tinygltf::Node& node, const tinygltf::Model& model, size_t& vertexCount, size_t& indexCount) { if (node.children.size() > 0) { for (size_t i = 0; i < node.children.size(); i++) { @@ -669,7 +670,7 @@ namespace glTFModel } } - void Model::loadSkins(tinygltf::Model& gltfModel) + void glTFModel::Model::loadSkins(tinygltf::Model& gltfModel) { for (tinygltf::Skin& source : gltfModel.skins) { Skin* newSkin = new Skin{}; @@ -701,11 +702,11 @@ namespace glTFModel } } - void Model::loadTextures(tinygltf::Model& gltfModel, vks::VulkanDevice* device, VkQueue transferQueue) + void glTFModel::Model::loadTextures(tinygltf::Model& gltfModel, vks::VulkanDevice* device, VkQueue transferQueue) { for (tinygltf::Texture& tex : gltfModel.textures) { tinygltf::Image image = gltfModel.images[tex.source]; - vkglTF::TextureSampler textureSampler; + glTFModel::TextureSampler textureSampler; if (tex.sampler == -1) { // No sampler specified, use a default one textureSampler.magFilter = VK_FILTER_LINEAR; @@ -717,13 +718,13 @@ namespace glTFModel else { textureSampler = textureSamplers[tex.sampler]; } - vkglTF::Texture texture; + glTFModel::Texture texture; texture.fromglTfImage(image, textureSampler, device, transferQueue); textures.push_back(texture); } } - VkSamplerAddressMode Model::getVkWrapMode(int32_t wrapMode) + VkSamplerAddressMode glTFModel::Model::getVkWrapMode(int32_t wrapMode) { switch (wrapMode) { case -1: @@ -739,7 +740,7 @@ namespace glTFModel return VK_SAMPLER_ADDRESS_MODE_REPEAT; } - VkFilter Model::getVkFilterMode(int32_t filterMode) + VkFilter glTFModel::Model::getVkFilterMode(int32_t filterMode) { switch (filterMode) { case -1: @@ -761,10 +762,10 @@ namespace glTFModel return VK_FILTER_NEAREST; } - void Model::loadTextureSamplers(tinygltf::Model& gltfModel) + void glTFModel::Model::loadTextureSamplers(tinygltf::Model& gltfModel) { for (tinygltf::Sampler smpl : gltfModel.samplers) { - vkglTF::TextureSampler sampler{}; + glTFModel::TextureSampler sampler{}; sampler.minFilter = getVkFilterMode(smpl.minFilter); sampler.magFilter = getVkFilterMode(smpl.magFilter); sampler.addressModeU = getVkWrapMode(smpl.wrapS); @@ -774,10 +775,10 @@ namespace glTFModel } } - void Model::loadMaterials(tinygltf::Model& gltfModel) + void glTFModel::Model::loadMaterials(tinygltf::Model& gltfModel) { for (tinygltf::Material& mat : gltfModel.materials) { - vkglTF::Material material{}; + glTFModel::Material material{}; material.doubleSided = mat.doubleSided; if (mat.values.find("baseColorTexture") != mat.values.end()) { material.baseColorTexture = &textures[mat.values["baseColorTexture"].TextureIndex()]; @@ -862,10 +863,10 @@ namespace glTFModel materials.push_back(Material()); } - void Model::loadAnimations(tinygltf::Model& gltfModel) + void glTFModel::Model::loadAnimations(tinygltf::Model& gltfModel) { for (tinygltf::Animation& anim : gltfModel.animations) { - vkglTF::Animation animation{}; + glTFModel::Animation animation{}; animation.name = anim.name; if (anim.name.empty()) { animation.name = std::to_string(animations.size()); @@ -873,7 +874,7 @@ namespace glTFModel // Samplers for (auto& samp : anim.samplers) { - vkglTF::AnimationSampler sampler{}; + glTFModel::AnimationSampler sampler{}; if (samp.interpolation == "LINEAR") { sampler.interpolation = AnimationSampler::InterpolationType::LINEAR; @@ -946,7 +947,7 @@ namespace glTFModel // Channels for (auto& source : anim.channels) { - vkglTF::AnimationChannel channel{}; + glTFModel::AnimationChannel channel{}; if (source.target_path == "rotation") { channel.path = AnimationChannel::PathType::ROTATION; @@ -974,7 +975,7 @@ namespace glTFModel } } - void Model::loadFromFile(std::string filename, vks::VulkanDevice* device, VkQueue transferQueue, float scale) + void glTFModel::Model::loadFromFile(std::string filename, vks::VulkanDevice* device, VkQueue transferQueue, float scale) { tinygltf::Model gltfModel; tinygltf::TinyGLTF gltfContext; @@ -1115,7 +1116,7 @@ namespace glTFModel getSceneDimensions(); } - void Model::drawNode(Node* node, VkCommandBuffer commandBuffer) + void glTFModel::Model::drawNode(Node* node, VkCommandBuffer commandBuffer) { if (node->mesh) { for (Primitive* primitive : node->mesh->primitives) { @@ -1127,7 +1128,7 @@ namespace glTFModel } } - void Model::draw(VkCommandBuffer commandBuffer) + void glTFModel::Model::draw(VkCommandBuffer commandBuffer) { const VkDeviceSize offsets[1] = { 0 }; vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertices.buffer, offsets); @@ -1137,7 +1138,7 @@ namespace glTFModel } } - void Model::calculateBoundingBox(Node* node, Node* parent) { + void glTFModel::Model::calculateBoundingBox(Node* node, Node* parent) { BoundingBox parentBvh = parent ? parent->bvh : BoundingBox(dimensions.min, dimensions.max); if (node->mesh) { @@ -1159,7 +1160,7 @@ namespace glTFModel } } - void Model::getSceneDimensions() + void glTFModel::Model::getSceneDimensions() { // Calculate binary volume hierarchy for all nodes in the scene for (auto node : linearNodes) { @@ -1183,7 +1184,7 @@ namespace glTFModel aabb[3][2] = dimensions.min[2]; } - void Model::updateAnimation(uint32_t index, float time) + void glTFModel::Model::updateAnimation(uint32_t index, float time) { if (animations.empty()) { std::cout << ".glTF does not contain animation." << std::endl; @@ -1197,7 +1198,7 @@ namespace glTFModel bool updated = false; for (auto& channel : animation.channels) { - vkglTF::AnimationSampler& sampler = animation.samplers[channel.samplerIndex]; + glTFModel::AnimationSampler& sampler = animation.samplers[channel.samplerIndex]; if (sampler.inputs.size() > sampler.outputsVec4.size()) { continue; } @@ -1207,17 +1208,17 @@ namespace glTFModel float u = std::max(0.0f, time - sampler.inputs[i]) / (sampler.inputs[i + 1] - sampler.inputs[i]); if (u <= 1.0f) { switch (channel.path) { - case vkglTF::AnimationChannel::PathType::TRANSLATION: { + case glTFModel::AnimationChannel::PathType::TRANSLATION: { glm::vec4 trans = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], u); channel.node->translation = glm::vec3(trans); break; } - case vkglTF::AnimationChannel::PathType::SCALE: { + case glTFModel::AnimationChannel::PathType::SCALE: { glm::vec4 trans = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], u); channel.node->scale = glm::vec3(trans); break; } - case vkglTF::AnimationChannel::PathType::ROTATION: { + case glTFModel::AnimationChannel::PathType::ROTATION: { glm::quat q1; q1.x = sampler.outputsVec4[i].x; q1.y = sampler.outputsVec4[i].y; @@ -1244,7 +1245,7 @@ namespace glTFModel } } - Node* Model::findNode(Node* parent, uint32_t index) { + glTFModel::Node* glTFModel::Model::findNode(Node* parent, uint32_t index) { Node* nodeFound = nullptr; if (parent->index == index) { return parent; @@ -1258,7 +1259,7 @@ namespace glTFModel return nodeFound; } - Node* Model::nodeFromIndex(uint32_t index) { + glTFModel::Node* glTFModel::Model::nodeFromIndex(uint32_t index) { Node* nodeFound = nullptr; for (auto& node : nodes) { nodeFound = findNode(node, index); @@ -1269,4 +1270,4 @@ namespace glTFModel return nodeFound; } -} + diff --git a/src/render/glTFModel.h b/src/render/glTFModel.h index 1489e71..ffd0b8c 100644 --- a/src/render/glTFModel.h +++ b/src/render/glTFModel.h @@ -27,7 +27,7 @@ #include "vulkan/vulkan.h" #define ENABLE_VALIDATION false - +#define MAX_NUM_JOINTS 128u // 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 diff --git a/src/render/render.cpp b/src/render/render.cpp index e7593ca..de3cb9a 100644 --- a/src/render/render.cpp +++ b/src/render/render.cpp @@ -31,7 +31,7 @@ #include "render.h" #include "GUIFunction.h" - +#include "glTFModel.h" PlumageRender::PlumageRender(): @@ -87,7 +87,7 @@ PlumageRender::PlumageRender(): pushConstBlockMaterial.normalTextureSet = primitive->material.normalTexture != nullptr ? primitive->material.texCoordSets.normal : -1; pushConstBlockMaterial.occlusionTextureSet = primitive->material.occlusionTexture != nullptr ? primitive->material.texCoordSets.occlusion : -1; pushConstBlockMaterial.emissiveTextureSet = primitive->material.emissiveTexture != nullptr ? primitive->material.texCoordSets.emissive : -1; - pushConstBlockMaterial.alphaMask = static_cast(primitive->material.alphaMode == vkglTF::Material::ALPHAMODE_MASK); + pushConstBlockMaterial.alphaMask = static_cast(primitive->material.alphaMode == glTFModel::Material::ALPHAMODE_MASK); pushConstBlockMaterial.alphaMaskCutoff = primitive->material.alphaCutoff; // TODO: glTF specs states that metallic roughness should be preferred, even if specular glosiness is present @@ -126,71 +126,161 @@ PlumageRender::PlumageRender(): for (auto child : node->children) { renderNode(child, cbIndex, alphaMode); } + } void PlumageRender::buildCommandBuffers() { - VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); + VkCommandBufferBeginInfo cmdBufferBeginInfo{}; + cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - VkClearValue clearValues[2]; - clearValues[0].color = defaultClearColor; - clearValues[0].color = { { 0.25f, 0.25f, 0.25f, 1.0f } }; - clearValues[1].depthStencil = { 1.0f, 0 }; + VkClearValue clearValues[3]; + if (settings.multiSampling) { + clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } }; + clearValues[1].color = { { 0.0f, 0.0f, 0.0f, 1.0f } }; + clearValues[2].depthStencil = { 1.0f, 0 }; + } + else { + clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } }; + clearValues[1].depthStencil = { 1.0f, 0 }; + } - VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo(); - renderPassBeginInfo.renderPass = pbrFrameBuffer.fbo.renderPass; + VkRenderPassBeginInfo renderPassBeginInfo{}; + renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassBeginInfo.renderPass = renderPass; renderPassBeginInfo.renderArea.offset.x = 0; renderPassBeginInfo.renderArea.offset.y = 0; renderPassBeginInfo.renderArea.extent.width = width; renderPassBeginInfo.renderArea.extent.height = height; - renderPassBeginInfo.clearValueCount = 2; + renderPassBeginInfo.clearValueCount = settings.multiSampling ? 3 : 2; renderPassBeginInfo.pClearValues = clearValues; - const VkViewport viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f); - const VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0); + for (uint32_t i = 0; i < commandBuffers.size(); ++i) { + renderPassBeginInfo.framebuffer = frameBuffers[i]; - for (int32_t i = 0; i < drawCmdBuffers.size(); ++i) - { - renderPassBeginInfo.framebuffer = pbrFrameBuffer.fbo.frameBuffer; - VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); - vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); - vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); - vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); - // Bind scene matrices descriptor to set 0 - vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.pbrLayout, 0, 1, &descriptorSet, 0, nullptr); - vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.pbrLayout, 6, 1, &skinDescriptorSet, 0, nullptr); - vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, wireframe ? pipelines.wireframe : pipelines.solid); - glTFModel.draw(drawCmdBuffers[i], pipelineLayouts.pbrLayout,false); - vkCmdEndRenderPass(drawCmdBuffers[i]); + VkCommandBuffer currentCB = commandBuffers[i]; - { - VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo(); - renderPassBeginInfo.renderPass = renderPass; - renderPassBeginInfo.framebuffer = VulkanExampleBase::frameBuffers[i]; - renderPassBeginInfo.renderArea.extent.width = width; - renderPassBeginInfo.renderArea.extent.height = height; - renderPassBeginInfo.clearValueCount = 2; - renderPassBeginInfo.pClearValues = clearValues; + VK_CHECK_RESULT(vkBeginCommandBuffer(currentCB, &cmdBufferBeginInfo)); + vkCmdBeginRenderPass(currentCB, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); - vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); - vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); - vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); - vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.tonemappingLayout, 0, 1, &tonemappingDescriptorSet, 0, NULL); - vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.toneMapping); - vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0); - drawUI(drawCmdBuffers[i]); - vkCmdEndRenderPass(drawCmdBuffers[i]); + VkViewport viewport{}; + viewport.width = (float)width; + viewport.height = (float)height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(currentCB, 0, 1, &viewport); + + VkRect2D scissor{}; + scissor.extent = { width, height }; + vkCmdSetScissor(currentCB, 0, 1, &scissor); + + VkDeviceSize offsets[1] = { 0 }; + + if (displayBackground) { + vkCmdBindDescriptorSets(currentCB, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[i].skybox, 0, nullptr); + vkCmdBindPipeline(currentCB, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skybox); + models.skybox.draw(currentCB); } - VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); + + glTFModel::Model& model = models.scene; + + vkCmdBindVertexBuffers(currentCB, 0, 1, &model.vertices.buffer, offsets); + if (model.indices.buffer != VK_NULL_HANDLE) { + vkCmdBindIndexBuffer(currentCB, model.indices.buffer, 0, VK_INDEX_TYPE_UINT32); + } + + boundPipeline = VK_NULL_HANDLE; + + // Opaque primitives first + for (auto node : model.nodes) { + renderNode(node, i, glTFModel::Material::ALPHAMODE_OPAQUE); + } + // Alpha masked primitives + for (auto node : model.nodes) { + renderNode(node, i, glTFModel::Material::ALPHAMODE_MASK); + } + // Transparent primitives + // TODO: Correct depth sorting + for (auto node : model.nodes) { + renderNode(node, i, glTFModel::Material::ALPHAMODE_BLEND); + } + + // User interface + //ui->draw(currentCB); + + vkCmdEndRenderPass(currentCB); + VK_CHECK_RESULT(vkEndCommandBuffer(currentCB)); } } + void PlumageRender::loadScene(std::string filename) + { + std::cout << "Loading scene from " << filename << std::endl; + models.scene.destroy(device); + animationIndex = 0; + animationTimer = 0.0f; + auto tStart = std::chrono::high_resolution_clock::now(); + models.scene.loadFromFile(filename, vulkanDevice, queue); + auto tFileLoad = std::chrono::duration(std::chrono::high_resolution_clock::now() - tStart).count(); + std::cout << "Loading took " << tFileLoad << " ms" << std::endl; + camera.setPosition({ 0.0f, 0.0f, 1.0f }); + camera.setRotation({ 0.0f, 0.0f, 0.0f }); + } + + void PlumageRender::loadEnvironment(std::string filename) + { + std::cout << "Loading environment from " << filename << std::endl; + if (textures.environmentCube.image) { + textures.environmentCube.destroy(); + textures.irradianceCube.destroy(); + textures.prefilteredCube.destroy(); + } + textures.environmentCube.loadFromFile(filename, VK_FORMAT_R16G16B16A16_SFLOAT, vulkanDevice, queue); + generateCubemaps(); + } + // TO DO:reconstruct with getting file path through struct void PlumageRender::loadAssets() { - loadglTFFile(filePath.glTFModelFilePath, glTFModel); - loadglTFFile(filePath.skyboxModleFilePath, skyboxModel, true); - ibltextures.skyboxCube.loadFromFile(filePath.iblTexturesFilePath, VK_FORMAT_R16G16B16A16_SFLOAT, vulkanDevice, queue); + const std::string assetpath = "./../data/"; + struct stat info; + if (stat(assetpath.c_str(), &info) != 0) { + std::string msg = "Could not locate asset path in \"" + assetpath + "\".\nMake sure binary is run from correct relative directory!"; + std::cerr << msg << std::endl; + exit(-1); + } + + readDirectory(assetpath + "environments", "*.ktx", environments, false); + + textures.empty.loadFromFile(assetpath + "textures/empty.ktx", VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue); + + std::string sceneFile = assetpath + "models/DamagedHelmet/glTF-Embedded/DamagedHelmet.gltf"; + std::string envMapFile = assetpath + "environments/papermill.ktx"; + for (size_t i = 0; i < args.size(); i++) { + if ((std::string(args[i]).find(".gltf") != std::string::npos) || (std::string(args[i]).find(".glb") != std::string::npos)) { + std::ifstream file(args[i]); + if (file.good()) { + sceneFile = args[i]; + } + else { + std::cout << "could not load \"" << args[i] << "\"" << std::endl; + } + } + if (std::string(args[i]).find(".ktx") != std::string::npos) { + std::ifstream file(args[i]); + if (file.good()) { + envMapFile = args[i]; + } + else { + std::cout << "could not load \"" << args[i] << "\"" << std::endl; + } + } + } + + loadScene(sceneFile.c_str()); + models.skybox.loadFromFile(assetpath + "models/Box/glTF-Embedded/Box.gltf", vulkanDevice, queue); + + loadEnvironment(envMapFile.c_str()); } void PlumageRender::setupDescriptors() diff --git a/src/render/render.h b/src/render/render.h index 9f68fd3..e6279c6 100644 --- a/src/render/render.h +++ b/src/render/render.h @@ -33,7 +33,7 @@ public: { glTFModel::Model scene; glTFModel::Model skybox; - }models; + } models; struct Textures { vks::TextureCubeMap environmentCube; @@ -150,6 +150,14 @@ public: VkDescriptorSet tonemappingDescriptorSet = VK_NULL_HANDLE; }; + struct Settings { + bool validation = false; + bool fullscreen = false; + bool vsync = false; + bool multiSampling = true; + VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_4_BIT; + } settings; + struct DescriptorSetLayouts { VkDescriptorSetLayout scene; VkDescriptorSetLayout material; @@ -247,7 +255,8 @@ public: virtual void getEnabledFeatures(); void renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFModel::Material::AlphaMode alphaMode); - virtual void setupFrameBuffer(); + void loadScene(std::string filename); + void loadEnvironment(std::string filename); void buildCommandBuffers(); void loadAssets(); void setupDescriptors();