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