reconstruct render

pull/2/head
InkSoul 2023-06-05 22:37:31 +08:00
parent 8c857eb707
commit a8fe5397c2
6 changed files with 776 additions and 100 deletions

View File

@ -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);
}
}
};
}

View File

@ -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
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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()

View File

@ -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();