diff --git a/.idea/misc.xml b/.idea/misc.xml
index 79b3c94..c68f301 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,4 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4ef8f71..fa4c7e1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,6 +6,5 @@ set(CMAKE_CXX_STANDARD 20)
find_package(Vulkan REQUIRED)
find_package(Qt5 COMPONENTS Gui REQUIRED)
-add_executable(VulkanCppSetup src/main.cpp src/VulkanWindow.cpp src/VulkanRenderer.cpp src/ShaderLoader.cpp)
-#include_directories(VulkanCppSetup ${Vulkan_INCLUDE_DIRS} ${Qt5_INCLUDE_DIRS})
+add_executable(VulkanCppSetup src/main.cpp src/VulkanWindow.cpp src/VulkanRenderer.cpp src/ShaderLoader.cpp src/WorldView.cpp src/UniformBuffers.cpp src/camera.cpp)
target_link_libraries(VulkanCppSetup ${Vulkan_LIBRARIES} Qt5::Gui)
diff --git a/shaders/shader.vert b/shaders/shader.vert
index f5b2f8d..8dfe235 100644
--- a/shaders/shader.vert
+++ b/shaders/shader.vert
@@ -1,20 +1,44 @@
#version 450
+layout(push_constant) uniform VertexUniformBufferObject {
+ mat4 model;
+ mat4 view;
+ mat4 proj;
+} ubo;
+
layout(location = 0) out vec3 fragColor;
-vec2 positions[3] = vec2[](
- vec2(0.0, -0.5),
- vec2(0.5, 0.5),
- vec2(-0.5, 0.5)
+vec3 positions[12] = vec3[](
+ vec3(0.0, -1.0, 1.0), // 1
+ vec3(1.0, 1.0, 1.0), // 2
+ vec3(-1.0, 1.0, 1.0), // 3
+ vec3(-1.0, 1.0, 1.0), // 3
+ vec3(0.0, 0.0, -1.0), // 4
+ vec3(0.0, -1.0, 1.0), // 1
+ vec3(0.0, -1.0, 1.0), // 1
+ vec3(0.0, 0.0, -1.0), // 4
+ vec3(1.0, 1.0, 1.0), // 2
+ vec3(1.0, 1.0, 1.0), // 2
+ vec3(0.0, 0.0, -1.0), // 4
+ vec3(-1.0, 1.0, 1.0) // 3
);
-vec3 colors[3] = vec3[](
+vec3 colors[12] = vec3[](
vec3(1.0, 0.0, 0.0),
vec3(0.0, 1.0, 0.0),
+ vec3(0.0, 0.0, 1.0),
+ vec3(0.0, 0.0, 1.0),
+ vec3(1.0, 1.0, 1.0),
+ vec3(1.0, 0.0, 0.0),
+ vec3(1.0, 0.0, 0.0),
+ vec3(1.0, 1.0, 1.0),
+ vec3(0.0, 1.0, 0.0),
+ vec3(0.0, 1.0, 0.0),
+ vec3(1.0, 1.0, 1.0),
vec3(0.0, 0.0, 1.0)
);
void main() {
- gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
+ gl_Position = ubo.proj * ubo.view * ubo.model * vec4(positions[gl_VertexIndex], 1.0);
fragColor = colors[gl_VertexIndex];
}
diff --git a/shaders/vert.spv b/shaders/vert.spv
index ef8a465..f93629d 100644
Binary files a/shaders/vert.spv and b/shaders/vert.spv differ
diff --git a/src/UniformBuffers.cpp b/src/UniformBuffers.cpp
new file mode 100644
index 0000000..716a56e
--- /dev/null
+++ b/src/UniformBuffers.cpp
@@ -0,0 +1,103 @@
+#include "UniformBuffers.h"
+#include "VertexUniformBufferObject.h"
+#include
+
+/// Find a memory in `memoryTypeBitsRequirement` that includes all of
+/// `requiredProperties` see
+/// https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkPhysicalDeviceMemoryProperties.html
+int32_t
+findProperties(const VkPhysicalDeviceMemoryProperties *pMemoryProperties,
+ uint32_t memoryTypeBitsRequirement,
+ VkMemoryPropertyFlags requiredProperties) {
+ const uint32_t memoryCount = pMemoryProperties->memoryTypeCount;
+ for (uint32_t memoryIndex = 0; memoryIndex < memoryCount; ++memoryIndex) {
+ const uint32_t memoryTypeBits = (1 << memoryIndex);
+ const bool isRequiredMemoryType =
+ memoryTypeBitsRequirement & memoryTypeBits;
+
+ const VkMemoryPropertyFlags properties =
+ pMemoryProperties->memoryTypes[memoryIndex].propertyFlags;
+ const bool hasRequiredProperties =
+ (properties & requiredProperties) == requiredProperties;
+
+ if (isRequiredMemoryType && hasRequiredProperties)
+ return static_cast(memoryIndex);
+ }
+
+ // failed to find memory type
+ return -1;
+}
+
+void UniformBuffers::createBuffers(QVulkanWindow *window,
+ QVulkanDeviceFunctions *devFuncs) {
+ VkResult result;
+
+ VkDeviceSize bufferSize = sizeof(VertexUniformBufferObject);
+
+ int swapChainImageCount = window->swapChainImageCount();
+ if (!m_uniformBuffers.empty() || !m_uniformBuffersMemory.empty()) {
+ qFatal("Uniform buffers are allocated! Old buffers need to be destroyed.");
+ }
+ m_uniformBuffers.resize(swapChainImageCount);
+ m_uniformBuffersMemory.resize(swapChainImageCount);
+
+ VkBufferCreateInfo bufferCreateInfo{};
+ bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ bufferCreateInfo.size = bufferSize;
+ bufferCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
+ bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
+ QVulkanFunctions *pFunctions = window->vulkanInstance()->functions();
+ VkPhysicalDeviceMemoryProperties memProperties;
+ pFunctions->vkGetPhysicalDeviceMemoryProperties(window->physicalDevice(),
+ &memProperties);
+
+ for (size_t i = 0; i < swapChainImageCount; i++) {
+ result = devFuncs->vkCreateBuffer(window->device(), &bufferCreateInfo,
+ VK_NULL_HANDLE, &m_uniformBuffers[i]);
+ if (result != VK_SUCCESS) {
+ qFatal("Failed to create buffer, code %d", result);
+ }
+
+ VkMemoryRequirements memRequirements;
+ devFuncs->vkGetBufferMemoryRequirements(
+ window->device(), m_uniformBuffers[i], &memRequirements);
+
+ VkMemoryAllocateInfo allocInfo{};
+ allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ allocInfo.allocationSize = memRequirements.size;
+ uint32_t requiredProperties{VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+ VK_MEMORY_PROPERTY_HOST_COHERENT_BIT};
+ allocInfo.memoryTypeIndex = findProperties(
+ &memProperties, memRequirements.memoryTypeBits, requiredProperties);
+
+ if (allocInfo.memoryTypeIndex == -1) {
+ qFatal("Unable to find memory type with props %d", requiredProperties);
+ }
+
+ result =
+ devFuncs->vkAllocateMemory(window->device(), &allocInfo, VK_NULL_HANDLE,
+ &m_uniformBuffersMemory[i]);
+ if (result != VK_SUCCESS) {
+ qFatal("Failed to allocate buffer memory, code %d", result);
+ }
+
+ result = devFuncs->vkBindBufferMemory(window->device(), m_uniformBuffers[i],
+ m_uniformBuffersMemory[i], 0);
+ if (result != VK_SUCCESS) {
+ qFatal("Failed to bind buffer memory, code %d", result);
+ }
+ }
+}
+
+void UniformBuffers::destroyBuffers(QVulkanWindow *window,
+ QVulkanDeviceFunctions *devFuncs) {
+ for (VkBuffer &buffer : m_uniformBuffers) {
+ devFuncs->vkDestroyBuffer(window->device(), buffer, VK_NULL_HANDLE);
+ }
+ m_uniformBuffers.clear();
+ for (VkDeviceMemory &memory : m_uniformBuffersMemory) {
+ devFuncs->vkFreeMemory(window->device(), memory, VK_NULL_HANDLE);
+ }
+ m_uniformBuffersMemory.clear();
+}
diff --git a/src/UniformBuffers.h b/src/UniformBuffers.h
new file mode 100644
index 0000000..43fcafb
--- /dev/null
+++ b/src/UniformBuffers.h
@@ -0,0 +1,23 @@
+#ifndef VULKANCPPSETUP_UNIFORMBUFFERS_H
+#define VULKANCPPSETUP_UNIFORMBUFFERS_H
+
+#include
+#include
+#include
+
+// Note: Switched to push constants, but kept this work
+class UniformBuffers {
+public:
+ explicit UniformBuffers() = default;
+ ~UniformBuffers() = default;
+
+ void createBuffers(QVulkanWindow *window, QVulkanDeviceFunctions *devFuncs);
+ void destroyBuffers(QVulkanWindow *window,
+ QVulkanDeviceFunctions *devFuncs);
+
+private:
+ std::vector m_uniformBuffers{};
+ std::vector m_uniformBuffersMemory{};
+};
+
+#endif // VULKANCPPSETUP_UNIFORMBUFFERS_H
diff --git a/src/VertexUniformBufferObject.h b/src/VertexUniformBufferObject.h
new file mode 100644
index 0000000..65fb469
--- /dev/null
+++ b/src/VertexUniformBufferObject.h
@@ -0,0 +1,12 @@
+#ifndef VULKANCPPSETUP_VERTEXUNIFORMBUFFEROBJECT_H
+#define VULKANCPPSETUP_VERTEXUNIFORMBUFFEROBJECT_H
+
+#include
+
+struct VertexUniformBufferObject {
+ std::array model;
+ std::array view;
+ std::array proj;
+};
+
+#endif // VULKANCPPSETUP_VERTEXUNIFORMBUFFEROBJECT_H
diff --git a/src/VulkanRenderer.cpp b/src/VulkanRenderer.cpp
index c139b6b..6ece0f0 100644
--- a/src/VulkanRenderer.cpp
+++ b/src/VulkanRenderer.cpp
@@ -1,32 +1,38 @@
#include "VulkanRenderer.h"
+#include "VertexUniformBufferObject.h"
#include
-VulkanRenderer::VulkanRenderer(QVulkanWindow *w) : m_window(w) {}
+VulkanRenderer::VulkanRenderer(QVulkanWindow *window, WorldView *worldView)
+ : m_window(window), m_worldView(worldView) {}
-void VulkanRenderer::initResources() {
+void VulkanRenderer::createPipeline() {
VkResult result;
- m_devFuncs = m_window->vulkanInstance()->deviceFunctions(m_window->device());
- VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
- pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
- pipelineLayoutInfo.setLayoutCount = 0; // Optional
- pipelineLayoutInfo.pSetLayouts = nullptr; // Optional
- pipelineLayoutInfo.pushConstantRangeCount = 0; // Optional
- pipelineLayoutInfo.pPushConstantRanges = nullptr; // Optional
+ // Note: switched to push constants instead
+ // VkDescriptorSetLayoutBinding uboLayoutBinding{};
+ // uboLayoutBinding.binding = 0;
+ // uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ // uboLayoutBinding.descriptorCount = 1;
+ // uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
+ //
+ // VkDescriptorSetLayoutCreateInfo layoutInfo{};
+ // layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+ // layoutInfo.bindingCount = 1;
+ // layoutInfo.pBindings = &uboLayoutBinding;
+ //
+ // result = m_devFuncs->vkCreateDescriptorSetLayout(
+ // m_window->device(), &layoutInfo, VK_NULL_HANDLE,
+ // &m_descriptorSetLayout);
+ // if (result != VK_SUCCESS) {
+ // qFatal("Failed to create descriptor set layout, code %d", result);
+ // }
- result = m_devFuncs->vkCreatePipelineLayout(
- m_window->device(), &pipelineLayoutInfo, nullptr, &m_pipelineLayout);
- if (result != VK_SUCCESS) {
- qFatal("Failed to create pipeline layout, code: %d", result);
- }
-
- VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
- vertexInputInfo.sType =
- VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
- vertexInputInfo.vertexBindingDescriptionCount = 0;
- vertexInputInfo.pVertexBindingDescriptions = nullptr; // Optional
- vertexInputInfo.vertexAttributeDescriptionCount = 0;
- vertexInputInfo.pVertexAttributeDescriptions = nullptr; // Optional
+ VkPushConstantRange pushConstant{};
+ pushConstant.offset = 0;
+ // might be too big for some graphics cards, spec requires min 128 bytes (the
+ // validation layer will tell us)
+ pushConstant.size = sizeof(VertexUniformBufferObject);
+ pushConstant.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
auto shaders =
std::array{"shaders/vert.spv", "shaders/frag.spv"};
@@ -46,6 +52,30 @@ void VulkanRenderer::initResources() {
fragShaderStageInfo.pName = "main";
std::array shaderStages{
vertShaderStageInfo, fragShaderStageInfo};
+
+ VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
+ pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ // Note: switched to push constants instead
+ // pipelineLayoutInfo.setLayoutCount = 1;
+ // pipelineLayoutInfo.pSetLayouts = &m_descriptorSetLayout;
+ pipelineLayoutInfo.pushConstantRangeCount = 1;
+ pipelineLayoutInfo.pPushConstantRanges = &pushConstant;
+
+ result = m_devFuncs->vkCreatePipelineLayout(
+ m_window->device(), &pipelineLayoutInfo, VK_NULL_HANDLE,
+ &m_pipelineLayout);
+ if (result != VK_SUCCESS) {
+ qFatal("Failed to create pipeline layout, code: %d", result);
+ }
+
+ VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
+ vertexInputInfo.sType =
+ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+ vertexInputInfo.vertexBindingDescriptionCount = 0;
+ vertexInputInfo.pVertexBindingDescriptions = VK_NULL_HANDLE; // Optional
+ vertexInputInfo.vertexAttributeDescriptionCount = 0;
+ vertexInputInfo.pVertexAttributeDescriptions = VK_NULL_HANDLE; // Optional
+
VkGraphicsPipelineCreateInfo pipelineInfo{};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = shaderStages.size();
@@ -114,34 +144,55 @@ void VulkanRenderer::initResources() {
pipelineInfo.basePipelineIndex = -1; // Optional
result = m_devFuncs->vkCreateGraphicsPipelines(
- m_window->device(), VK_NULL_HANDLE, 1, &pipelineInfo, nullptr,
+ m_window->device(), VK_NULL_HANDLE, 1, &pipelineInfo, VK_NULL_HANDLE,
&m_graphicsPipeline);
if (result != VK_SUCCESS) {
qFatal("Failed to create graphics pipeline: code %d", result);
}
}
+void VulkanRenderer::initResources() {
+ QVulkanWindowRenderer::initResources();
+ m_devFuncs = m_window->vulkanInstance()->deviceFunctions(m_window->device());
+ createPipeline();
+}
+
void VulkanRenderer::releaseResources() {
+ QVulkanWindowRenderer::releaseResources();
m_devFuncs->vkDestroyPipeline(m_window->device(), m_graphicsPipeline,
- nullptr);
+ VK_NULL_HANDLE);
m_devFuncs->vkDestroyPipelineLayout(m_window->device(), m_pipelineLayout,
- nullptr);
+ VK_NULL_HANDLE);
+ // m_devFuncs->vkDestroyDescriptorSetLayout(
+ // m_window->device(), m_descriptorSetLayout, VK_NULL_HANDLE);
m_shaderLoader.destroyShaders(m_window->device(), m_devFuncs);
}
+void VulkanRenderer::initSwapChainResources() {
+ QVulkanWindowRenderer::initSwapChainResources();
+ // m_uniformBuffers.createBuffers(m_window, m_devFuncs);
+ m_worldView->initializeProjectionMatrix(m_window);
+}
+
+void VulkanRenderer::releaseSwapChainResources() {
+ QVulkanWindowRenderer::releaseSwapChainResources();
+ // m_uniformBuffers.destroyBuffers(m_window, m_devFuncs);
+}
+
void VulkanRenderer::startNextFrame() {
VkCommandBuffer cmdBuf = m_window->currentCommandBuffer();
VkClearDepthStencilValue clearDS = {1, 0};
std::array clearValue{};
- float flash = std::abs(std::sin(static_cast(m_frameNumber) / 120.f));
+ float flash =
+ std::abs(std::sin(static_cast(m_frameNumber) / 80.f)) / 2.f;
clearValue[0].color = {{0.0f, 0.0f, flash, 1.0f}};
clearValue[1].depthStencil = clearDS;
clearValue[2].color = clearValue[0].color;
VkRenderPassBeginInfo rpInfo{};
rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
- rpInfo.pNext = nullptr;
+ rpInfo.pNext = VK_NULL_HANDLE;
rpInfo.renderPass = m_window->defaultRenderPass();
rpInfo.renderArea.offset.x = 0;
@@ -169,7 +220,12 @@ void VulkanRenderer::startNextFrame() {
m_devFuncs->vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS,
m_graphicsPipeline);
- m_devFuncs->vkCmdDraw(cmdBuf, 3, 1, 0, 0);
+ const VertexUniformBufferObject &mvp = m_worldView->getBufferObject();
+ m_devFuncs->vkCmdPushConstants(cmdBuf, m_pipelineLayout,
+ VK_SHADER_STAGE_VERTEX_BIT, 0,
+ sizeof(VertexUniformBufferObject), &mvp);
+
+ m_devFuncs->vkCmdDraw(cmdBuf, 12, 1, 0, 0);
m_devFuncs->vkCmdEndRenderPass(cmdBuf);
m_window->frameReady();
diff --git a/src/VulkanRenderer.h b/src/VulkanRenderer.h
index 45bf2b9..1fe4d2d 100644
--- a/src/VulkanRenderer.h
+++ b/src/VulkanRenderer.h
@@ -1,26 +1,35 @@
#ifndef VULKANCPPSETUP_VULKANRENDERER_H
#define VULKANCPPSETUP_VULKANRENDERER_H
+#include "WorldView.h"
#include "ShaderLoader.h"
+#include "UniformBuffers.h"
#include
#include
class VulkanRenderer : public QVulkanWindowRenderer {
public:
- explicit VulkanRenderer(QVulkanWindow *w);
+ explicit VulkanRenderer(QVulkanWindow *window, WorldView *worldView);
void initResources() override;
void releaseResources() override;
+ void initSwapChainResources() override;
+ void releaseSwapChainResources() override;
void startNextFrame() override;
private:
QVulkanWindow *m_window;
+ WorldView *m_worldView;
QVulkanDeviceFunctions *m_devFuncs{};
unsigned long m_frameNumber{0UL};
ShaderLoader m_shaderLoader{};
+// UniformBuffers m_uniformBuffers{};
+// VkDescriptorSetLayout m_descriptorSetLayout{};
VkPipelineLayout m_pipelineLayout{};
VkPipeline m_graphicsPipeline{};
+
+ void createPipeline();
};
#endif // VULKANCPPSETUP_VULKANRENDERER_H
diff --git a/src/VulkanWindow.cpp b/src/VulkanWindow.cpp
index e55d189..dac1b2d 100644
--- a/src/VulkanWindow.cpp
+++ b/src/VulkanWindow.cpp
@@ -3,16 +3,57 @@
#include
static const int KEY_ESCAPE = 16777216;
+static const int KEY_W = 87;
+static const int KEY_A = 65;
+static const int KEY_S = 83;
+static const int KEY_D = 68;
void VulkanWindow::keyPressEvent(QKeyEvent *event) {
QWindow::keyPressEvent(event);
qDebug() << "text" << event->text() << "key" << event->key() << "modifiers"
<< event->modifiers();
- if (event->matches(QKeySequence::Quit) || event->key() == KEY_ESCAPE) {
+ if (event->key() == KEY_W) {
+ m_worldView.moveCamera(1.f);
+ } else if (event->key() == KEY_S) {
+ m_worldView.moveCamera(-1.f);
+ } else if (event->key() == KEY_A) {
+ m_worldView.strafeCamera(-1.f);
+ } else if (event->key() == KEY_D) {
+ m_worldView.strafeCamera(1.f);
+ } else if (event->matches(QKeySequence::Quit) || event->key() == KEY_ESCAPE) {
close();
}
}
-QVulkanWindowRenderer *VulkanWindow::createRenderer() {
- return new VulkanRenderer(this);
+void VulkanWindow::mousePressEvent(QMouseEvent *e)
+{
+ m_mousePressed = true;
+ m_lastMousePos = e->localPos().toPoint();
+}
+
+void VulkanWindow::mouseReleaseEvent(QMouseEvent *)
+{ m_mousePressed = false;
+}
+
+void VulkanWindow::mouseMoveEvent(QMouseEvent *e)
+{
+ if (!m_mousePressed)
+ return;
+
+ int dx = e->localPos().toPoint().x() - m_lastMousePos.x();
+ int dy = e->localPos().toPoint().y() - m_lastMousePos.y();
+
+ if (dy) {
+ m_worldView.pitchCamera(static_cast(dy) / 10.0f);
+ }
+
+ if (dx) {
+ m_worldView.yawCamera(static_cast(dx) / 10.0f);
+ }
+
+ m_lastMousePos = e->localPos().toPoint();
+}
+
+QVulkanWindowRenderer *VulkanWindow::createRenderer() {
+ return new VulkanRenderer(this, &m_worldView);
}
diff --git a/src/VulkanWindow.h b/src/VulkanWindow.h
index 84adc2b..c5cc421 100644
--- a/src/VulkanWindow.h
+++ b/src/VulkanWindow.h
@@ -1,13 +1,10 @@
-//
-// Created by ben on 13.01.22.
-//
-
#ifndef VULKANCPPSETUP_VULKANWINDOW_H
#define VULKANCPPSETUP_VULKANWINDOW_H
#include
#include
#include
+#include "WorldView.h"
class VulkanWindow : public QVulkanWindow {
public:
@@ -15,6 +12,14 @@ public:
protected:
void keyPressEvent(QKeyEvent *event) override;
+ void mousePressEvent(QMouseEvent *event) override;
+ void mouseReleaseEvent(QMouseEvent *event) override;
+ void mouseMoveEvent(QMouseEvent *event) override;
+
+private:
+ WorldView m_worldView{};
+ bool m_mousePressed{false};
+ QPoint m_lastMousePos;
};
#endif // VULKANCPPSETUP_VULKANWINDOW_H
diff --git a/src/WorldView.cpp b/src/WorldView.cpp
new file mode 100644
index 0000000..1a4092c
--- /dev/null
+++ b/src/WorldView.cpp
@@ -0,0 +1,43 @@
+#include "WorldView.h"
+
+WorldView::WorldView() {
+// m_modelMat.translate(0, 0, -5);
+// m_modelMat.rotate(-90, 1, 0, 0);
+// m_modelMat.scale(1.f, 1.f, 1.5f);
+}
+
+void WorldView::initializeProjectionMatrix(QVulkanWindow *window) {
+// m_projectionMat = window->clipCorrectionMatrix();
+// const QSize sz = window->swapChainImageSize();
+// m_projectionMat.perspective(
+// 90.0f, static_cast(sz.width()) / static_cast(sz.height()),
+// 0.01f, 1000.0f);
+}
+
+VertexUniformBufferObject WorldView::getBufferObject() {
+ VertexUniformBufferObject obj{};
+ m_modelMat.copyDataTo(obj.model.data());
+ m_camera.viewMatrix().copyDataTo(obj.view.data());
+ m_projectionMat.copyDataTo(obj.proj.data());
+ return obj;
+}
+
+QVector4D WorldView::multiply(const QVector4D &vector) {
+ return m_projectionMat * m_camera.viewMatrix() * m_modelMat * vector;
+}
+
+void WorldView::pitchCamera(float degrees) {
+ m_camera.pitch(degrees);
+}
+
+void WorldView::yawCamera(float degrees) {
+ m_camera.yaw(degrees);
+}
+
+void WorldView::strafeCamera(float amount) {
+ m_camera.strafe(amount);
+}
+
+void WorldView::moveCamera(float amount) {
+ m_camera.walk(amount);
+}
diff --git a/src/WorldView.h b/src/WorldView.h
new file mode 100644
index 0000000..a90491c
--- /dev/null
+++ b/src/WorldView.h
@@ -0,0 +1,26 @@
+#ifndef VULKANCPPSETUP_WORLDVIEW_H
+#define VULKANCPPSETUP_WORLDVIEW_H
+
+#include "camera.h"
+#include "VertexUniformBufferObject.h"
+#include
+#include
+
+class WorldView {
+public:
+ explicit WorldView();
+ void initializeProjectionMatrix(QVulkanWindow *window);
+ QVector4D multiply(const QVector4D &vector);
+ VertexUniformBufferObject getBufferObject();
+ void pitchCamera(float degrees);
+ void yawCamera(float degrees);
+ void moveCamera(float amount);
+ void strafeCamera(float amount);
+
+private:
+ QMatrix4x4 m_modelMat{};
+ QMatrix4x4 m_projectionMat{};
+ Camera m_camera{QVector3D{0.f, 0.f, -1.f}};
+};
+
+#endif // VULKANCPPSETUP_WORLDVIEW_H
diff --git a/src/camera.cpp b/src/camera.cpp
new file mode 100644
index 0000000..64dee03
--- /dev/null
+++ b/src/camera.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "camera.h"
+
+Camera::Camera(const QVector3D &pos)
+ : m_forward(0.0f, 0.0f, -1.0f),
+ m_right(1.0f, 0.0f, 0.0f),
+ m_up(0.0f, 1.0f, 0.0f),
+ m_pos(pos),
+ m_yaw(0.0f),
+ m_pitch(0.0f)
+{
+}
+
+static inline void clamp360(float *v)
+{
+ if (*v > 360.0f)
+ *v -= 360.0f;
+ if (*v < -360.0f)
+ *v += 360.0f;
+}
+
+void Camera::yaw(float degrees)
+{
+ m_yaw += degrees;
+ clamp360(&m_yaw);
+ m_yawMatrix.setToIdentity();
+ m_yawMatrix.rotate(m_yaw, 0, 1, 0);
+
+ QMatrix4x4 rotMat = m_pitchMatrix * m_yawMatrix;
+ m_forward = (QVector4D(0.0f, 0.0f, -1.0f, 0.0f) * rotMat).toVector3D();
+ m_right = (QVector4D(1.0f, 0.0f, 0.0f, 0.0f) * rotMat).toVector3D();
+}
+
+void Camera::pitch(float degrees)
+{
+ m_pitch += degrees;
+ clamp360(&m_pitch);
+ m_pitchMatrix.setToIdentity();
+ m_pitchMatrix.rotate(m_pitch, 1, 0, 0);
+
+ QMatrix4x4 rotMat = m_pitchMatrix * m_yawMatrix;
+ m_forward = (QVector4D(0.0f, 0.0f, -1.0f, 0.0f) * rotMat).toVector3D();
+ m_up = (QVector4D(0.0f, 1.0f, 0.0f, 0.0f) * rotMat).toVector3D();
+}
+
+void Camera::walk(float amount)
+{
+ m_pos[0] += amount * m_forward.x();
+ m_pos[2] += amount * m_forward.z();
+}
+
+void Camera::strafe(float amount)
+{
+ m_pos[0] += amount * m_right.x();
+ m_pos[2] += amount * m_right.z();
+}
+
+QMatrix4x4 Camera::viewMatrix() const
+{
+ QMatrix4x4 m = m_pitchMatrix * m_yawMatrix;
+ m.translate(-m_pos);
+ return m;
+}
diff --git a/src/camera.h b/src/camera.h
new file mode 100644
index 0000000..9287bfd
--- /dev/null
+++ b/src/camera.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CAMERA_H
+#define CAMERA_H
+
+#include
+#include
+
+class Camera
+{
+public:
+ explicit Camera(const QVector3D &pos);
+
+ void yaw(float degrees);
+ void pitch(float degrees);
+ void walk(float amount);
+ void strafe(float amount);
+
+ [[nodiscard]] QMatrix4x4 viewMatrix() const;
+
+private:
+ QVector3D m_forward;
+ QVector3D m_right;
+ QVector3D m_up;
+ QVector3D m_pos;
+ float m_yaw;
+ float m_pitch;
+ QMatrix4x4 m_yawMatrix;
+ QMatrix4x4 m_pitchMatrix;
+};
+
+#endif
diff --git a/src/main.cpp b/src/main.cpp
index 81217be..d385d0c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -6,11 +6,15 @@ int main(int argc, char **argv) {
QGuiApplication app(argc, argv);
QVulkanInstance inst;
- QByteArrayList layers{"VK_LAYER_LUNARG_standard_validation"};
+ QByteArrayList layers{"VK_LAYER_KHRONOS_validation"};
inst.setLayers(layers);
if (!inst.create()) {
- qFatal("Could not create Vulkan instance %d", inst.errorCode());
- return EXIT_FAILURE;
+ qFatal("Could not create Vulkan instance, code %d", inst.errorCode());
+ }
+ for (const auto& layer : layers) {
+ if (!inst.layers().contains(layer)) {
+ qFatal("Layer not available: %s", layer.toStdString().c_str());
+ }
}
VulkanWindow window{};
window.setVulkanInstance(&inst);