Compare commits
3 Commits
19c25ba670
...
56fb496ec7
Author | SHA1 | Date | |
---|---|---|---|
56fb496ec7 | |||
43fd780e0b | |||
ac402dbef8 |
2
Makefile
2
Makefile
@ -1,5 +1,5 @@
|
||||
CPPFLAGS=-g
|
||||
LDFLAGS=-lglfw -lvulkan -ldl -lpthread -lX11 -lXxf86vm -lXrandr -lXi
|
||||
LDFLAGS=-lglfw -lvulkan -ldl -lpthread -lX11 -lXxf86vm -lXrandr -lXi -ltinyobjloader
|
||||
DEBUGFLAGS=-DDEBUG -fsanitize=address
|
||||
GDBFLAGS=
|
||||
SRC = $(shell find . -name "*.cpp")
|
||||
|
BIN
assets/textures/viking_room.png
Normal file
BIN
assets/textures/viking_room.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 940 KiB |
2029
lib/tiny_obj_loader.h
Normal file
2029
lib/tiny_obj_loader.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -291,14 +291,14 @@ namespace DeviceControl {
|
||||
vkDestroySwapchainKHR(Global::device, Global::swapChain, nullptr);
|
||||
if(Global::enableValidationLayers) std::cout << "Destroyed Swap Chain safely\n" << std::endl;
|
||||
}
|
||||
VkImageView devicelibrary::createImageView(VkImage image, VkFormat format) {
|
||||
VkImageView devicelibrary::createImageView(VkImage image, VkFormat format, VkImageAspectFlags flags) {
|
||||
// This defines the parameters of a newly created image object!
|
||||
VkImageViewCreateInfo viewInfo{};
|
||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
viewInfo.image = image;
|
||||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
viewInfo.format = format;
|
||||
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
viewInfo.subresourceRange.aspectMask = flags;
|
||||
viewInfo.subresourceRange.baseMipLevel = 0;
|
||||
viewInfo.subresourceRange.levelCount = 1;
|
||||
viewInfo.subresourceRange.baseArrayLayer = 0;
|
||||
@ -315,7 +315,7 @@ namespace DeviceControl {
|
||||
swapChainImageViews.resize(swapChainImages.size());
|
||||
|
||||
for (uint32_t i = 0; i < swapChainImages.size(); i++) {
|
||||
swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat);
|
||||
swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
}
|
||||
}
|
||||
void devicelibrary::destroyImageViews() {
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "global.h"
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <optional>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
@ -16,7 +17,7 @@ class devicelibrary {
|
||||
void destroySurface(VkInstance& instance);
|
||||
void createSwapChain(GLFWwindow* window);
|
||||
void destroySwapChain();
|
||||
VkImageView createImageView(VkImage image, VkFormat format);
|
||||
VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags flags);
|
||||
void createImageViews();
|
||||
void destroyImageViews();
|
||||
void createCommandPool();
|
||||
|
@ -5,8 +5,11 @@ Graphics::graphicspipeline graphicsPipeline;
|
||||
RenderPresent::render renderPresentation;
|
||||
BuffersLibraries::buffers buffers;
|
||||
TextureLibraries::texture texture;
|
||||
ModelLib::model model;
|
||||
VkInstance vulkaninstance;
|
||||
|
||||
//TODO: add global instances?
|
||||
|
||||
// Getters and Setters!
|
||||
void EntryApp::setFramebufferResized(bool setter) {
|
||||
framebufferResized = setter;
|
||||
@ -60,11 +63,13 @@ void initVulkan() {
|
||||
graphicsPipeline.createRenderPass();
|
||||
buffers.createDescriptorSetLayout();
|
||||
graphicsPipeline.createGraphicsPipeline();
|
||||
graphicsPipeline.createFramebuffers();
|
||||
graphicsPipeline.createCommandPool();
|
||||
texture.createDepthResources();
|
||||
graphicsPipeline.createFramebuffers();
|
||||
texture.createTextureImage();
|
||||
texture.createTextureImageView();
|
||||
texture.createTextureSampler();
|
||||
model.loadModel();
|
||||
buffers.createVertexBuffer();
|
||||
buffers.createIndexBuffer();
|
||||
buffers.createUniformBuffers();
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "graphics/render.h"
|
||||
#include "graphics/texture.h"
|
||||
#include "global.h"
|
||||
#include "graphics/model.h"
|
||||
class EntryApp {
|
||||
public:
|
||||
static EntryApp& getInstance();
|
||||
|
@ -26,7 +26,13 @@ namespace Global {
|
||||
uint32_t currentFrame = 0;
|
||||
VkImageView textureImageView;
|
||||
VkSampler textureSampler;
|
||||
VkImageView depthImageView;
|
||||
VkImage depthImage;
|
||||
VkDeviceMemory depthImageMemory;
|
||||
|
||||
std::vector<Vertex> vertices;
|
||||
// Index buffer definition, showing which points to reuse.
|
||||
std::vector<uint32_t> indices;
|
||||
Global::QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
|
||||
// First we feed in a integer we want to use to hold the number of queued items, that fills it, then we create that amount of default constructed *VkQueueFamilyProperties* structs.
|
||||
// These store the flags, the amount of queued items in the family, and timestamp data. Queue families are simply group collections of tasks we want to get done.
|
||||
|
33
src/global.h
33
src/global.h
@ -1,23 +1,29 @@
|
||||
#pragma once
|
||||
#include "graphics/texture.h"
|
||||
#include <cstdint>
|
||||
#include <glm/detail/qualifier.hpp>
|
||||
#include <glm/ext/vector_float2.hpp>
|
||||
#include <glm/ext/vector_float3.hpp>
|
||||
#include <glm/fwd.hpp>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#include "debug/vulkandebuglibs.h"
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <array>
|
||||
|
||||
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <array>
|
||||
#define GLFW_INCLUDE_VULKAN
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
namespace Global {
|
||||
// Global variables and includes we are going to use almost everywhere, validation layers hook into everything, and you need to check if they are enabled first,
|
||||
// so that's one obvious global, as well as the glfw includes!
|
||||
|
||||
extern const std::vector<const char*> validationLayers;
|
||||
extern const bool enableValidationLayers;
|
||||
extern VkDevice device;
|
||||
@ -32,6 +38,13 @@ namespace Global {
|
||||
extern std::vector<VkDescriptorSet> descriptorSets;
|
||||
extern VkImageView textureImageView;
|
||||
extern VkSampler textureSampler;
|
||||
extern VkImageView depthImageView;
|
||||
extern VkImage depthImage;
|
||||
extern VkDeviceMemory depthImageMemory;
|
||||
const std::string MODEL_PATH = "assets/models/viking_room.obj";
|
||||
const std::string TEXTURE_PATH = "assets/textures/viking_room.png";
|
||||
|
||||
|
||||
|
||||
struct UniformBufferObject {
|
||||
float time;
|
||||
@ -40,7 +53,9 @@ namespace Global {
|
||||
alignas(16) glm::mat4 proj;
|
||||
};
|
||||
struct Vertex {
|
||||
glm::vec2 pos;
|
||||
// This defines what a vertex is!
|
||||
// We control the position, color and texture coordinate here!
|
||||
glm::vec3 pos;
|
||||
glm::vec3 color;
|
||||
glm::vec2 texCoord;
|
||||
|
||||
@ -57,7 +72,7 @@ namespace Global {
|
||||
|
||||
attributeDescriptions[0].binding = 0;
|
||||
attributeDescriptions[0].location = 0;
|
||||
attributeDescriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
|
||||
attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
|
||||
attributeDescriptions[0].offset = offsetof(Vertex, pos);
|
||||
|
||||
attributeDescriptions[1].binding = 0;
|
||||
@ -71,10 +86,16 @@ namespace Global {
|
||||
attributeDescriptions[2].offset = offsetof(Vertex, texCoord);
|
||||
return attributeDescriptions;
|
||||
}
|
||||
bool operator==(const Vertex& other) const {
|
||||
return pos == other.pos && color == other.color && texCoord == other.texCoord;
|
||||
}
|
||||
};
|
||||
const uint32_t WIDTH = 800;
|
||||
const uint32_t HEIGHT = 600;
|
||||
|
||||
const uint32_t WIDTH = 800;
|
||||
const uint32_t HEIGHT = 800;
|
||||
extern std::vector<Vertex> vertices;
|
||||
// Index buffer definition, showing which points to reuse.
|
||||
extern std::vector<uint32_t> indices;
|
||||
struct QueueFamilyIndices {
|
||||
// We need to check that the Queue families support graphics operations and window presentation, sometimes they can support one or the other,
|
||||
// therefore, we take into account both for completion.
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "buffers.h"
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#include <cstring>
|
||||
#include "../devicelibrary.h"
|
||||
|
||||
VkBuffer vertexBuffer;
|
||||
@ -19,16 +18,6 @@ std::vector<void*> uniformBuffersMapped;
|
||||
namespace BuffersLibraries {
|
||||
|
||||
|
||||
const std::vector<Global::Vertex> vertices = {
|
||||
{{-0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f}},
|
||||
{{0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f}},
|
||||
{{0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}, {0.0f, 1.0f}},
|
||||
{{-0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}}
|
||||
};
|
||||
// Index buffer definition, showing which points to reuse.
|
||||
const std::vector<uint16_t> indices = {
|
||||
0, 1, 2, 2, 3, 0
|
||||
};
|
||||
|
||||
uint32_t buffers::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) {
|
||||
// Graphics cards offer different types of memory to allocate from, here we query to find the right type of memory for our needs.
|
||||
@ -104,7 +93,7 @@ namespace BuffersLibraries {
|
||||
}
|
||||
|
||||
void buffers::createIndexBuffer() {
|
||||
VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size();
|
||||
VkDeviceSize bufferSize = sizeof(Global::indices[0]) * Global::indices.size();
|
||||
|
||||
VkBuffer stagingBuffer;
|
||||
VkDeviceMemory stagingBufferMemory;
|
||||
@ -113,7 +102,8 @@ namespace BuffersLibraries {
|
||||
|
||||
void* data;
|
||||
vkMapMemory(Global::device, stagingBufferMemory, 0, bufferSize, 0, &data);
|
||||
memcpy(data, indices.data(), (size_t) bufferSize);
|
||||
memcpy(data, Global::indices.data(), (size_t) bufferSize);
|
||||
|
||||
vkUnmapMemory(Global::device, stagingBufferMemory);
|
||||
|
||||
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory);
|
||||
@ -127,7 +117,7 @@ namespace BuffersLibraries {
|
||||
// Create a Vertex Buffer to hold the vertex information in memory so it doesn't have to be hardcoded!
|
||||
// Size denotes the size of the buffer in bytes, usage in this case is the buffer behaviour, using a bitwise OR.
|
||||
// Sharing mode denostes the same as the images in the swap chain! in this case, only the graphics queue uses this buffer, so we make it exclusive.
|
||||
VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size();
|
||||
VkDeviceSize bufferSize = sizeof(Global::vertices[0]) * Global::vertices.size();
|
||||
|
||||
VkBuffer stagingBuffer;
|
||||
VkDeviceMemory stagingBufferMemory;
|
||||
@ -135,7 +125,7 @@ namespace BuffersLibraries {
|
||||
|
||||
void* data;
|
||||
vkMapMemory(Global::device, stagingBufferMemory, 0, bufferSize, 0, &data);
|
||||
memcpy(data, vertices.data(), (size_t) bufferSize);
|
||||
memcpy(data, Global::vertices.data(), (size_t) bufferSize);
|
||||
vkUnmapMemory(Global::device, stagingBufferMemory);
|
||||
|
||||
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory);
|
||||
@ -159,12 +149,7 @@ namespace BuffersLibraries {
|
||||
VkBuffer buffers::getIndexBuffer() {
|
||||
return indexBuffer;
|
||||
}
|
||||
std::vector<Global::Vertex> buffers::getVertices() {
|
||||
return vertices;
|
||||
}
|
||||
std::vector<uint16_t> buffers::getIndices() {
|
||||
return indices;
|
||||
}
|
||||
|
||||
// ------------------------------ Uniform Buffer Setup -------------------------------- //
|
||||
void buffers::createDescriptorSetLayout() {
|
||||
// Create a table of pointers to data, a Descriptor Set!
|
||||
@ -223,10 +208,10 @@ namespace BuffersLibraries {
|
||||
Global::UniformBufferObject ubo{};
|
||||
ubo.time = time;
|
||||
// Modify the model projection transformation to rotate around the Z over time.
|
||||
ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
ubo.model = glm::rotate(glm::mat4(1.0f), glm::radians(180.0f), glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
// Modify the view transformation to look at the object from above at a 45 degree angle.
|
||||
// This takes the eye position, center position, and the up direction.
|
||||
ubo.view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
ubo.view = glm::lookAt(glm::vec3(4.0f, 4.0f, 3.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
// 45 degree field of view, set aspect ratio, and near and far clipping range.
|
||||
ubo.proj = glm::perspective(glm::radians(45.0f), deviceLibrary.getSwapChainExtent().width / (float) deviceLibrary.getSwapChainExtent().height, 0.1f, 10.0f);
|
||||
|
||||
|
@ -11,8 +11,6 @@ namespace BuffersLibraries {
|
||||
void destroyBuffers();
|
||||
VkBuffer getVertexBuffer();
|
||||
VkBuffer getIndexBuffer();
|
||||
std::vector<Global::Vertex> getVertices();
|
||||
std::vector<uint16_t> getIndices();
|
||||
void createDescriptorSetLayout();
|
||||
void createUniformBuffers();
|
||||
void updateUniformBuffer(uint32_t currentImage);
|
||||
|
@ -1,7 +1,10 @@
|
||||
|
||||
#include "graphicspipeline.h"
|
||||
#include "buffers.h"
|
||||
#include "texture.h"
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
namespace Graphics {
|
||||
std::vector<VkDynamicState> dynamicStates = {
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
@ -14,6 +17,7 @@ namespace Graphics {
|
||||
VkPipeline graphicsPipeline;
|
||||
DeviceControl::devicelibrary deviceLibs;
|
||||
BuffersLibraries::buffers buffers;
|
||||
TextureLibraries::texture textureLibs;
|
||||
|
||||
std::vector<VkFramebuffer> swapChainFramebuffers;
|
||||
|
||||
@ -136,6 +140,15 @@ namespace Graphics {
|
||||
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||
multisampling.sampleShadingEnable = VK_FALSE;
|
||||
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
// TODO: Document!
|
||||
VkPipelineDepthStencilStateCreateInfo depthStencil{};
|
||||
depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
||||
depthStencil.depthTestEnable = VK_TRUE;
|
||||
depthStencil.depthWriteEnable = VK_TRUE;
|
||||
depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;
|
||||
depthStencil.depthBoundsTestEnable = VK_FALSE;
|
||||
depthStencil.stencilTestEnable = VK_FALSE;
|
||||
// Most of the graphics pipeline is set in stone, some of the pipeline state can be modified without recreating it at runtime though!
|
||||
// There are TONS of settings, this would be another TODO to see what else we can mess with dynamically, but right now we just allow dynamic size of the viewport
|
||||
// and dynamic scissor states. Scissors are pretty straightforward, they are basically pixel masks for the rasterizer.
|
||||
@ -168,6 +181,7 @@ namespace Graphics {
|
||||
pipelineInfo.layout = pipelineLayout;
|
||||
pipelineInfo.renderPass = renderPass;
|
||||
pipelineInfo.subpass = 0;
|
||||
pipelineInfo.pDepthStencilState = &depthStencil;
|
||||
|
||||
if (vkCreateGraphicsPipelines(Global::device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to create graphics pipeline!");
|
||||
@ -192,18 +206,45 @@ namespace Graphics {
|
||||
colorAttachmentRef.attachment = 0;
|
||||
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkAttachmentDescription depthAttachment{};
|
||||
depthAttachment.format = textureLibs.findDepthFormat();
|
||||
depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkAttachmentReference depthAttachmentRef{};
|
||||
depthAttachmentRef.attachment = 1;
|
||||
depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkSubpassDescription subpass{};
|
||||
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
subpass.colorAttachmentCount = 1;
|
||||
subpass.pColorAttachments = &colorAttachmentRef;
|
||||
subpass.pDepthStencilAttachment = &depthAttachmentRef;
|
||||
|
||||
VkSubpassDependency dependency{};
|
||||
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
|
||||
dependency.dstSubpass = 0;
|
||||
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
||||
dependency.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
|
||||
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
|
||||
std::array<VkAttachmentDescription, 2> attachments = {colorAttachment, depthAttachment};
|
||||
VkRenderPassCreateInfo renderPassInfo{};
|
||||
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
renderPassInfo.attachmentCount = 1;
|
||||
renderPassInfo.pAttachments = &colorAttachment;
|
||||
renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
|
||||
renderPassInfo.pAttachments = attachments.data();
|
||||
renderPassInfo.subpassCount = 1;
|
||||
renderPassInfo.pSubpasses = &subpass;
|
||||
|
||||
renderPassInfo.dependencyCount = 1;
|
||||
renderPassInfo.pDependencies = &dependency;
|
||||
|
||||
|
||||
if (vkCreateRenderPass(Global::device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
|
||||
throw std::runtime_error("failed to create render pass!");
|
||||
}
|
||||
@ -219,15 +260,16 @@ namespace Graphics {
|
||||
swapChainFramebuffers.resize(framebuffersSize);
|
||||
|
||||
for(size_t i = 0; i < framebuffersSize; i++) {
|
||||
VkImageView attachments[] = {
|
||||
deviceLibs.getSwapChainImageViews()[i]
|
||||
std::array<VkImageView, 2> attachments = {
|
||||
deviceLibs.getSwapChainImageViews()[i],
|
||||
Global::depthImageView
|
||||
};
|
||||
|
||||
VkFramebufferCreateInfo framebufferInfo{};
|
||||
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
||||
framebufferInfo.renderPass = renderPass;
|
||||
framebufferInfo.attachmentCount = 1;
|
||||
framebufferInfo.pAttachments = attachments;
|
||||
framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
|
||||
framebufferInfo.pAttachments = attachments.data();
|
||||
framebufferInfo.width = deviceLibs.getSwapChainExtent().width;
|
||||
framebufferInfo.height = deviceLibs.getSwapChainExtent().height;
|
||||
framebufferInfo.layers = 1;
|
||||
@ -285,9 +327,12 @@ namespace Graphics {
|
||||
renderPassInfo.renderArea.offset = {0, 0};
|
||||
renderPassInfo.renderArea.extent = deviceLibs.getSwapChainExtent();
|
||||
|
||||
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
|
||||
renderPassInfo.clearValueCount = 1;
|
||||
renderPassInfo.pClearValues = &clearColor;
|
||||
std::array<VkClearValue, 2> clearValues{};
|
||||
clearValues[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
|
||||
clearValues[1].depthStencil = {1.0f, 0};
|
||||
|
||||
renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
|
||||
renderPassInfo.pClearValues = clearValues.data();
|
||||
|
||||
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
@ -309,11 +354,11 @@ namespace Graphics {
|
||||
VkBuffer vertexBuffers[] = {buffers.getVertexBuffer()};
|
||||
VkDeviceSize offsets[] = {0};
|
||||
vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets);
|
||||
vkCmdBindIndexBuffer(commandBuffer, buffers.getIndexBuffer(), 0, VK_INDEX_TYPE_UINT16);
|
||||
vkCmdBindIndexBuffer(commandBuffer, buffers.getIndexBuffer(), 0, VK_INDEX_TYPE_UINT32);
|
||||
|
||||
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &Global::descriptorSets[Global::currentFrame], 0, nullptr);
|
||||
|
||||
vkCmdDrawIndexed(commandBuffer, static_cast<uint32_t>(buffers.getIndices().size()), 1, 0, 0, 0);
|
||||
vkCmdDrawIndexed(commandBuffer, static_cast<uint32_t>(Global::indices.size()), 1, 0, 0, 0);
|
||||
vkCmdEndRenderPass(commandBuffer);
|
||||
|
||||
if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) {
|
||||
|
63
src/graphics/model.cpp
Normal file
63
src/graphics/model.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
#define TINY_OBJ_IMPLEMENTATION
|
||||
#include <tiny_obj_loader.h>
|
||||
#include "model.h"
|
||||
#include <unordered_map>
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/hash.hpp>
|
||||
|
||||
namespace std {
|
||||
template<> struct hash<Global::Vertex> {
|
||||
size_t operator()(Global::Vertex const& vertex) const {
|
||||
return ((hash<glm::vec3>()(vertex.pos) ^
|
||||
(hash<glm::vec3>()(vertex.color) << 1)) >> 1) ^
|
||||
(hash<glm::vec2>()(vertex.texCoord) << 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace ModelLib {
|
||||
|
||||
std::unordered_map<Global::Vertex, uint32_t> uniqueVertices{};
|
||||
|
||||
void model::loadModel() {
|
||||
tinyobj::ObjReaderConfig readerConfig;
|
||||
|
||||
tinyobj::ObjReader reader;
|
||||
|
||||
if(!reader.ParseFromFile(Global::MODEL_PATH, readerConfig)) {
|
||||
if(!reader.Error().empty()) {
|
||||
throw std::runtime_error(reader.Error());
|
||||
}
|
||||
}
|
||||
|
||||
auto& attrib = reader.GetAttrib();
|
||||
auto& shapes = reader.GetShapes();
|
||||
auto& materials = reader.GetMaterials();
|
||||
|
||||
for (const auto& shape : shapes) {
|
||||
for (const auto& index : shape.mesh.indices) {
|
||||
Global::Vertex vertex{};
|
||||
|
||||
vertex.pos = {
|
||||
attrib.vertices[3 * index.vertex_index + 0],
|
||||
attrib.vertices[3 * index.vertex_index + 1],
|
||||
attrib.vertices[3 * index.vertex_index + 2]
|
||||
};
|
||||
|
||||
vertex.texCoord = {
|
||||
attrib.texcoords[2 * index.texcoord_index + 0],
|
||||
1.0f - attrib.texcoords[2 * index.texcoord_index + 1]
|
||||
};
|
||||
|
||||
vertex.color = {1.0f, 1.0f, 1.0f};
|
||||
|
||||
if (uniqueVertices.count(vertex) == 0) {
|
||||
uniqueVertices[vertex] = static_cast<uint32_t>(Global::vertices.size());
|
||||
Global::vertices.push_back(vertex);
|
||||
}
|
||||
|
||||
Global::indices.push_back(uniqueVertices[vertex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
8
src/graphics/model.h
Normal file
8
src/graphics/model.h
Normal file
@ -0,0 +1,8 @@
|
||||
#include "../global.h"
|
||||
|
||||
namespace ModelLib {
|
||||
class model {
|
||||
public:
|
||||
void loadModel();
|
||||
};
|
||||
}
|
@ -2,6 +2,8 @@
|
||||
#include "graphicspipeline.h"
|
||||
#include "../devicelibrary.h"
|
||||
#include "../entrypoint.h"
|
||||
#include "texture.h"
|
||||
#include <vulkan/vulkan_core.h>
|
||||
namespace RenderPresent {
|
||||
|
||||
std::vector<VkSemaphore> imageAvailableSemaphores;
|
||||
@ -10,6 +12,7 @@ namespace RenderPresent {
|
||||
Graphics::graphicspipeline pipeline;
|
||||
DeviceControl::devicelibrary deviceLibs;
|
||||
BuffersLibraries::buffers buffers;
|
||||
TextureLibraries::texture tex;
|
||||
|
||||
void recreateSwapChain() {
|
||||
int width = 0, height = 0;
|
||||
@ -30,6 +33,7 @@ namespace RenderPresent {
|
||||
|
||||
deviceLibs.createSwapChain(Global::window);
|
||||
deviceLibs.createImageViews();
|
||||
tex.createDepthResources();
|
||||
pipeline.createFramebuffers();
|
||||
}
|
||||
// At a high level, rendering in Vulkan consists of 5 steps:
|
||||
@ -153,6 +157,9 @@ namespace RenderPresent {
|
||||
}
|
||||
}
|
||||
void render::cleanupSwapChain() {
|
||||
vkDestroyImageView(Global::device, Global::depthImageView, nullptr);
|
||||
vkDestroyImage(Global::device, Global::depthImage, nullptr);
|
||||
vkFreeMemory(Global::device, Global::depthImageMemory, nullptr);
|
||||
for(auto framebuffer : pipeline.getSwapChainFramebuffers()) {
|
||||
vkDestroyFramebuffer(Global::device, framebuffer, nullptr);
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb/stb_image.h>
|
||||
#include "texture.h"
|
||||
@ -12,6 +13,7 @@ VkPipelineStageFlags sourceStage;
|
||||
VkPipelineStageFlags destinationStage;
|
||||
|
||||
|
||||
|
||||
namespace TextureLibraries {
|
||||
void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) {
|
||||
// This function specifies all the data in an image object, this is called directly after the creation of an image object.
|
||||
@ -164,12 +166,31 @@ void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t
|
||||
|
||||
endSingleTimeCommands(commandBuffer);
|
||||
}
|
||||
VkFormat findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features) {
|
||||
for(VkFormat format : candidates) {
|
||||
VkFormatProperties props;
|
||||
vkGetPhysicalDeviceFormatProperties(Global::physicalDevice, format, &props);
|
||||
|
||||
// Do we support linear tiling?
|
||||
if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & features) == features) {
|
||||
return format;
|
||||
// Or do we support optimal tiling?
|
||||
} else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & features) == features) {
|
||||
return format;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("failed to find supported depth buffering format!");
|
||||
}
|
||||
|
||||
bool hasStencilComponent(VkFormat format) {
|
||||
return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT;
|
||||
}
|
||||
// -------------------------------- Image Libraries ------------------------------- //
|
||||
void texture::createTextureImage() {
|
||||
// Import pixels from image with data on color channels, width and height, and colorspace!
|
||||
// Its a lot of kind of complicated memory calls to bring it from a file -> to a buffer -> to a image object.
|
||||
int textureWidth, textureHeight, textureChannels;
|
||||
stbi_uc* pixels = stbi_load("assets/textures/test.png", &textureWidth, &textureHeight, &textureChannels, STBI_rgb_alpha);
|
||||
stbi_uc* pixels = stbi_load(Global::TEXTURE_PATH.c_str(), &textureWidth, &textureHeight, &textureChannels, STBI_rgb_alpha);
|
||||
|
||||
VkDeviceSize imageSize = textureWidth * textureHeight * 4;
|
||||
|
||||
@ -198,7 +219,7 @@ void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t
|
||||
}
|
||||
void texture::createTextureImageView() {
|
||||
// Create a texture image view, which is a struct of information about the image.
|
||||
Global::textureImageView = deviceLibraries.createImageView(textureImage, VK_FORMAT_R8G8B8A8_SRGB);
|
||||
Global::textureImageView = deviceLibraries.createImageView(textureImage, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
}
|
||||
void texture::createTextureSampler() {
|
||||
// Create a sampler to access and parse the texture object.
|
||||
@ -250,5 +271,21 @@ void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t
|
||||
vkDestroyImage(Global::device, textureImage, nullptr);
|
||||
vkFreeMemory(Global::device, textureImageMemory, nullptr);
|
||||
}
|
||||
|
||||
VkFormat texture::findDepthFormat() {
|
||||
return findSupportedFormat(
|
||||
{VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT},
|
||||
VK_IMAGE_TILING_OPTIMAL,
|
||||
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
|
||||
);
|
||||
}
|
||||
void texture::createDepthResources() {
|
||||
VkFormat depthFormat = findDepthFormat();
|
||||
VkExtent2D swapChainExtent = deviceLibraries.getSwapChainExtent();
|
||||
createImage(swapChainExtent.width, swapChainExtent.height, depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, Global::depthImage, Global::depthImageMemory);
|
||||
Global::depthImageView = deviceLibraries.createImageView(Global::depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT);
|
||||
// Explicit transition from the layout of the image to the depth attachment is unnecessary here, since that will be handled in the render pass!
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "../global.h"
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
namespace TextureLibraries {
|
||||
class texture {
|
||||
@ -9,5 +10,7 @@ namespace TextureLibraries {
|
||||
void createTextureSampler();
|
||||
void destroyTextureImage();
|
||||
void destroyTextureSampler();
|
||||
VkFormat findDepthFormat();
|
||||
void createDepthResources();
|
||||
};
|
||||
}
|
||||
|
@ -6,9 +6,11 @@ layout(binding = 0) uniform UniformBufferObject {
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
} ubo;
|
||||
|
||||
// inPosition and inColor are vertex attributes, per-vertex properties defined in the vertex buffer!
|
||||
// Layout assigns indices to access these inputs, dvec3 takes 2 slots so we must index it at 2. https://www.khronos.org/opengl/wiki/Layout_Qualifier_(GLSL)
|
||||
layout(location = 0) in vec2 inPosition;
|
||||
|
||||
layout(location = 0) in vec3 inPosition;
|
||||
layout(location = 1) in vec3 inColor;
|
||||
layout(location = 2) in vec2 inTexCoord;
|
||||
|
||||
@ -16,7 +18,7 @@ layout(location = 0) out vec3 fragColor;
|
||||
layout(location = 1) out vec2 fragTexCoord;
|
||||
|
||||
void main() {
|
||||
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0);
|
||||
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
|
||||
fragColor = inColor;
|
||||
fragTexCoord = inTexCoord;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user