From dd321473cf9ade903956526c5533e6a52b5bd8ad Mon Sep 17 00:00:00 2001 From: Patrick Wuttke Date: Mon, 16 Sep 2024 12:13:47 +0200 Subject: [PATCH] Added app for UI. --- private/sdl_gpu_test/6_ui/app.cpp | 256 ++++++++++++++++++++++++++++++ private/sdl_gpu_test/6_ui/app.hpp | 36 +++++ private/sdl_gpu_test/SModule | 1 + private/sdl_gpu_test/main.cpp | 4 +- 4 files changed, 296 insertions(+), 1 deletion(-) create mode 100644 private/sdl_gpu_test/6_ui/app.cpp create mode 100644 private/sdl_gpu_test/6_ui/app.hpp diff --git a/private/sdl_gpu_test/6_ui/app.cpp b/private/sdl_gpu_test/6_ui/app.cpp new file mode 100644 index 0000000..f004031 --- /dev/null +++ b/private/sdl_gpu_test/6_ui/app.cpp @@ -0,0 +1,256 @@ + +#include "./app.hpp" + +#include +#include +#include +#include +#include + +#include "../sdlpp/keyboard.hpp" +#include "../util/mesh.hpp" + +namespace sdl_gpu_test +{ +namespace +{ +inline constexpr float Y_POS_MIN = -10.f; +inline constexpr float Y_POS_MAX = 10.f; +inline constexpr Uint16 AXIS_DEADZONE = 5000; + +struct VertexShaderParameters +{ + glm::mat4 worldToView; + glm::mat4 viewToClip; +}; +} + +void UIApp::init(const AppInitArgs& args) +{ + Application::init(args); + + // create shaders + const sdlpp::GPUShader vertexShader = loadShader("shaders/glsl/textured_3dtriangles_from_buffer.vert.spv", { + .format = sdlpp::GPUShaderFormat::SPIRV, + .stage = sdlpp::GPUShaderStage::VERTEX, + .numUniformBuffers = 1 + }); + const sdlpp::GPUShader fragmentShader = loadShader("shaders/glsl/color_from_texture.frag.spv", { + .format = sdlpp::GPUShaderFormat::SPIRV, + .stage = sdlpp::GPUShaderStage::FRAGMENT, + .numSamplers = 1, + .numUniformBuffers = 1 + }); + + // create depth buffer + mDepthBuffer.create(mDevice, { + .format = sdlpp::GPUTextureFormat::D16_UNORM, + .usage = sdlpp::GPUTextureUsageFlags{.depthStencilTarget = true}, + .width = 1280, + .height = 720 + }); + + // create graphics pipeline + std::array colorTargetsDescs = { + sdlpp::GPUColorTargetDescription{ + .format = mDevice.getSwapchainTextureFormat(mWindow), + .blendState = { + .enableBlend = true, + .srcColorBlendfactor = sdlpp::GPUBlendFactor::SRC_ALPHA, + .dstColorBlendfactor = sdlpp::GPUBlendFactor::ONE_MINUS_SRC_ALPHA, + .colorBlendOp = sdlpp::GPUBlendOp::ADD, + .srcAlphaBlendfactor = sdlpp::GPUBlendFactor::ONE, + .dstAlphaBlendfactor = sdlpp::GPUBlendFactor::ZERO, + .alphaBlendOp = sdlpp::GPUBlendOp::ADD + } + } + }; + std::array vertexBindings = { + sdlpp::GPUVertexBinding{ + .index = 0, + .pitch = sizeof(Vertex) + } + }; + std::array vertexAttributes = { + sdlpp::GPUVertexAttribute{ + .location = 0, + .bindingIndex = 0, + .format = sdlpp::GPUVertexElementFormat::FLOAT3, + .offset = offsetof(Vertex, pos) + }, + sdlpp::GPUVertexAttribute{ + .location = 1, + .bindingIndex = 0, + .format = sdlpp::GPUVertexElementFormat::FLOAT2, + .offset = offsetof(Vertex, texcoord) + } + }; + mMeshPipeline.create(mDevice, { + .vertexShader = vertexShader, + .fragmentShader = fragmentShader, + .vertexInputState = { + .vertexBindings = vertexBindings, + .vertexAttributes = vertexAttributes + }, + .rasterizerState = { + .cullMode = sdlpp::GPUCullMode::BACK + }, + .targetInfo = { + .colorTargetDescriptions = colorTargetsDescs, + .depthStencilFormat = sdlpp::GPUTextureFormat::D16_UNORM + } + }); + + // load the mesh + const Mesh mesh = loadMesh(mFileSystem.getPath("meshes/cube.obj")); + mNumVertices = static_cast(mesh.vertices.size()); + + // create vertex buffer + mVertexBuffer.create(mDevice, { + .usage = {.vertex = true}, + .size = static_cast(mesh.vertices.size() * sizeof(Vertex)) + }); + uploadVertexData(mVertexBuffer, std::span(mesh.vertices.begin(), mesh.vertices.end())); + + // create texture and sampler + sdlpp::GPUTextureCreateArgs textureArgs = { + .format = sdlpp::GPUTextureFormat::R8G8B8A8_UNORM_SRGB, + .usage = {.sampler = true} + }; + mTexture = loadTexture("bitmaps/cube.png", textureArgs); + mSampler.create(mDevice, {}); + + // open gamepad + const std::vector gamepads = sdlpp::getGamepads(); + if (!gamepads.empty()) + { + mGamepad.open(gamepads[0]); + } +} + +void UIApp::update(const AppUpdateArgs& args) +{ + Application::update(args); + + processInput(args); + + // begin rendering + sdlpp::GPUCommandBuffer cmdBuffer = mDevice.acquireCommandBuffer(); + Uint32 swapchainWidth = 0, swapchainHeight = 0; + const sdlpp::GPUTexture swapchainTexture = cmdBuffer.acquireSwapchainTexture(mWindow, swapchainWidth, swapchainHeight); + + if (swapchainWidth != mLastSwapchainWidth || swapchainHeight != mLastSwapchainHeight) + { + mDepthBuffer.destroy(); + mDepthBuffer.create(mDevice, { + .format = sdlpp::GPUTextureFormat::D16_UNORM, + .usage = sdlpp::GPUTextureUsageFlags{.depthStencilTarget = true}, + .width = swapchainWidth, + .height = swapchainHeight + }); + mLastSwapchainWidth = swapchainWidth; + mLastSwapchainHeight = swapchainHeight; + } + + // render the 3D "scene" + const VertexShaderParameters vertexShaderParameters = { + .worldToView = glm::lookAt(glm::vec3(2.f, mYPos, 2.f), glm::vec3(0.f, 0.f, 0.f), glm::vec3(0.f, 1.f, 0.f)) + * glm::rotate(glm::mat4(1.f), glm::radians(mRotation), glm::vec3(0.f, 1.f, 0.f)), + .viewToClip = glm::perspectiveFov( + /* fov = */ glm::radians(90.f), + /* width = */ static_cast(swapchainWidth), + /* height = */ static_cast(swapchainHeight), + /* zNear = */ 0.1f, + /* zFar = */ 100.f + ) + }; + + std::array colorTargets = {sdlpp::GPUColorTargetInfo{ + .texture = swapchainTexture, + .clearColor = {.r = 0.f, .g = 0.f, .b = 0.f, .a = 1.f}, + .loadOp = sdlpp::GPULoadOp::CLEAR, + }}; + sdlpp::GPURenderPass renderPass = cmdBuffer.beginRenderPass({ + .colorTargetInfos = colorTargets, + .depthStencilTargetInfo = sdlpp::GPUDepthStencilTargetInfo{ + .texture = mDepthBuffer, + .loadOp = sdlpp::GPULoadOp::CLEAR + } + }); + static const glm::vec4 WHITE(1.f, 1.f, 1.f, 1.f); + cmdBuffer.pushFragmentUniformData(0, std::span(&WHITE, 1)); + cmdBuffer.pushVertexUniformData(0, std::span(&vertexShaderParameters, 1)); + renderPass.bindFragmentSampler({.texture = mTexture, .sampler = mSampler}); + renderPass.bindGraphicsPipeline(mMeshPipeline); + renderPass.bindVertexBuffer({.buffer = mVertexBuffer}); + renderPass.drawPrimitives({.numVertices = mNumVertices}); + renderPass.end(); + + // render the "UI" + + + // finalize + cmdBuffer.submit(); +} + +void UIApp::handleKeyboardEvent(const sdlpp::KeyboardEvent& event) +{ + switch (event.key) + { + case SDLK_Q: + if (!event.down) + { + mRunning = false; + } + break; + default: break; + } +} + +void UIApp::handleMouseMotionEvent(const sdlpp::MouseMotionEvent& event) +{ + if (event.state.left) + { + mRotation += 0.5f * event.xrel; + mYPos = std::clamp(mYPos + 0.02f * event.yrel, Y_POS_MIN, Y_POS_MAX); + } +} + +void UIApp::processInput(const AppUpdateArgs& args) +{ + const std::span keystates = sdlpp::getKeyboardState(); + if (keystates[SDL_SCANCODE_LEFT]) + { + mRotation -= 40.f * args.tickSeconds; + } + if (keystates[SDL_SCANCODE_RIGHT]) + { + mRotation += 40.f * args.tickSeconds; + } + if (keystates[SDL_SCANCODE_UP]) + { + mYPos -= 2.f * args.tickSeconds; + } + if (keystates[SDL_SCANCODE_DOWN]) + { + mYPos += 2.f * args.tickSeconds; + } + if (mGamepad) + { + const Sint16 xAxis = mGamepad.getAxis(sdlpp::GamepadAxis::LEFTX); + if (std::abs(xAxis) > AXIS_DEADZONE) + { + mRotation += 0.001f * args.tickSeconds * static_cast(xAxis); + } + const Sint16 yAxis = mGamepad.getAxis(sdlpp::GamepadAxis::LEFTY); + if (std::abs(yAxis) > AXIS_DEADZONE) + { + mYPos += 0.0002f * args.tickSeconds * yAxis; + } + } + + while (mRotation >= 360.f) { mRotation -= 360.f; } + while (mRotation < 0.f) { mRotation += 360.f; } + mYPos = std::clamp(mYPos, Y_POS_MIN, Y_POS_MAX); +} +} diff --git a/private/sdl_gpu_test/6_ui/app.hpp b/private/sdl_gpu_test/6_ui/app.hpp new file mode 100644 index 0000000..bd6d85e --- /dev/null +++ b/private/sdl_gpu_test/6_ui/app.hpp @@ -0,0 +1,36 @@ + +#pragma once + +#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_6_UI_APP_HPP_INCLUDED) +#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_6_UI_APP_HPP_INCLUDED 1 + +#include "../application.hpp" +#include "../sdlpp/gamepad.hpp" + +namespace sdl_gpu_test +{ +class UIApp : public Application +{ +private: + sdlpp::GPUBuffer mVertexBuffer; + sdlpp::GPUTexture mDepthBuffer; + sdlpp::GPUGraphicsPipeline mMeshPipeline; + sdlpp::GPUTexture mTexture; + sdlpp::GPUSampler mSampler; + sdlpp::Gamepad mGamepad; + Uint32 mNumVertices = 0; + Uint32 mLastSwapchainWidth = 1280; + Uint32 mLastSwapchainHeight = 720; + float mRotation = 0.f; + float mYPos = 1.5f; +public: + void init(const AppInitArgs& args) override; + void update(const AppUpdateArgs& args) override; + void handleKeyboardEvent(const sdlpp::KeyboardEvent& event) override; + void handleMouseMotionEvent(const sdlpp::MouseMotionEvent& event) override; +private: + void processInput(const AppUpdateArgs& args); +}; +} // namespace sdl_gpu_test + +#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_6_UI_APP_HPP_INCLUDED) diff --git a/private/sdl_gpu_test/SModule b/private/sdl_gpu_test/SModule index efdbf4a..ebc040d 100644 --- a/private/sdl_gpu_test/SModule +++ b/private/sdl_gpu_test/SModule @@ -14,6 +14,7 @@ src_files = Split(""" 3_textured_quad/app.cpp 4_textured_cube/app.cpp 5_input/app.cpp + 6_ui/app.cpp """) shader_files = env.Glob("#assets/shaders/glsl/*.frag") \ diff --git a/private/sdl_gpu_test/main.cpp b/private/sdl_gpu_test/main.cpp index 0a2d170..fca063a 100644 --- a/private/sdl_gpu_test/main.cpp +++ b/private/sdl_gpu_test/main.cpp @@ -8,6 +8,7 @@ #include "./3_textured_quad/app.hpp" #include "./4_textured_cube/app.hpp" #include "./5_input/app.hpp" +#include "./6_ui/app.hpp" #include "./util/spdlog_wrapper.hpp" namespace @@ -36,7 +37,8 @@ const std::array APPS = { makeAppHelper("triangle_with_texcoords"), makeAppHelper("textured_quad"), makeAppHelper("textured_cube"), - makeAppHelper("input") + makeAppHelper("input"), + makeAppHelper("ui") }; }