Interaction.. Yay?

This commit is contained in:
Ben 2022-01-20 23:37:04 +01:00
parent 3c8e214f6d
commit cc7678aec6
Signed by: ben
GPG key ID: 0F54A7ED232D3319
16 changed files with 589 additions and 47 deletions

View file

@ -1,4 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
<component name="CidrRootsConfiguration">
<excludeRoots>
<file path="$PROJECT_DIR$/build" />
</excludeRoots>
</component>
</project>

View file

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

View file

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

Binary file not shown.

103
src/UniformBuffers.cpp Normal file
View file

@ -0,0 +1,103 @@
#include "UniformBuffers.h"
#include "VertexUniformBufferObject.h"
#include <QVulkanDeviceFunctions>
/// 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<int32_t>(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();
}

23
src/UniformBuffers.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef VULKANCPPSETUP_UNIFORMBUFFERS_H
#define VULKANCPPSETUP_UNIFORMBUFFERS_H
#include <QVulkanDeviceFunctions>
#include <QVulkanWindow>
#include <vector>
// 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<VkBuffer> m_uniformBuffers{};
std::vector<VkDeviceMemory> m_uniformBuffersMemory{};
};
#endif // VULKANCPPSETUP_UNIFORMBUFFERS_H

View file

@ -0,0 +1,12 @@
#ifndef VULKANCPPSETUP_VERTEXUNIFORMBUFFEROBJECT_H
#define VULKANCPPSETUP_VERTEXUNIFORMBUFFEROBJECT_H
#include <array>
struct VertexUniformBufferObject {
std::array<float, 16> model;
std::array<float, 16> view;
std::array<float, 16> proj;
};
#endif // VULKANCPPSETUP_VERTEXUNIFORMBUFFEROBJECT_H

View file

@ -1,32 +1,38 @@
#include "VulkanRenderer.h"
#include "VertexUniformBufferObject.h"
#include <complex>
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<std::string, 2>{"shaders/vert.spv", "shaders/frag.spv"};
@ -46,6 +52,30 @@ void VulkanRenderer::initResources() {
fragShaderStageInfo.pName = "main";
std::array<VkPipelineShaderStageCreateInfo, 2> 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<VkClearValue, 3> clearValue{};
float flash = std::abs(std::sin(static_cast<float>(m_frameNumber) / 120.f));
float flash =
std::abs(std::sin(static_cast<float>(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();

View file

@ -1,26 +1,35 @@
#ifndef VULKANCPPSETUP_VULKANRENDERER_H
#define VULKANCPPSETUP_VULKANRENDERER_H
#include "WorldView.h"
#include "ShaderLoader.h"
#include "UniformBuffers.h"
#include <QVulkanWindowRenderer>
#include <qvulkanfunctions.h>
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

View file

@ -3,16 +3,57 @@
#include <qevent.h>
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<float>(dy) / 10.0f);
}
if (dx) {
m_worldView.yawCamera(static_cast<float>(dx) / 10.0f);
}
m_lastMousePos = e->localPos().toPoint();
}
QVulkanWindowRenderer *VulkanWindow::createRenderer() {
return new VulkanRenderer(this, &m_worldView);
}

View file

@ -1,13 +1,10 @@
//
// Created by ben on 13.01.22.
//
#ifndef VULKANCPPSETUP_VULKANWINDOW_H
#define VULKANCPPSETUP_VULKANWINDOW_H
#include <QVulkanWindow>
#include <iostream>
#include <qevent.h>
#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

43
src/WorldView.cpp Normal file
View file

@ -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<float>(sz.width()) / static_cast<float>(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);
}

26
src/WorldView.h Normal file
View file

@ -0,0 +1,26 @@
#ifndef VULKANCPPSETUP_WORLDVIEW_H
#define VULKANCPPSETUP_WORLDVIEW_H
#include "camera.h"
#include "VertexUniformBufferObject.h"
#include <QMatrix4x4>
#include <QVulkanWindow>
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

112
src/camera.cpp Normal file
View file

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

80
src/camera.h Normal file
View file

@ -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 <QVector3D>
#include <QMatrix4x4>
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

View file

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