reconstruct render
parent
8c857eb707
commit
a8fe5397c2
|
@ -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 <exception>
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include "vulkan/vulkan.h"
|
||||
|
||||
#if defined(VK_USE_PLATFORM_MACOS_MVK) && (VK_HEADER_VERSION >= 216)
|
||||
#include <vulkan/vulkan_beta.h>
|
||||
#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<VkQueueFamilyProperties> 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<uint32_t>(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<uint32_t>(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<const char*> 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<VkDeviceQueueCreateInfo> 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<const char*> 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<uint32_t>(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);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "vulkan/vulkan.h"
|
||||
#include "VulkanDevice.hpp"
|
||||
#if defined(__ANDROID__)
|
||||
#include <android/asset_manager.h>
|
||||
#elif defined(__linux__)
|
||||
#include <dirent.h>
|
||||
#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<std::string, std::string> &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
|
||||
}
|
|
@ -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,7 +351,7 @@ namespace glTFModel
|
|||
return m;
|
||||
}
|
||||
|
||||
void Node::update() {
|
||||
void glTFModel::Node::update() {
|
||||
if (mesh) {
|
||||
glm::mat4 m = getMatrix();
|
||||
if (skin) {
|
||||
|
@ -359,7 +360,7 @@ namespace glTFModel
|
|||
glm::mat4 inverseTransform = glm::inverse(m);
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<float>(primitive->material.alphaMode == vkglTF::Material::ALPHAMODE_MASK);
|
||||
pushConstBlockMaterial.alphaMask = static_cast<float>(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 } };
|
||||
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<double, std::milli>(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()
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue