Added 3D scene app and some fixes.

This commit is contained in:
Patrick 2024-09-20 00:56:35 +02:00
parent 09ed4d083f
commit eb72232c2b
6 changed files with 353 additions and 3 deletions

View File

@ -51,7 +51,7 @@ void TexturedQuadApp::init(const AppInitArgs& args)
.dstColorBlendfactor = sdlpp::GPUBlendFactor::ONE_MINUS_SRC_ALPHA,
.colorBlendOp = sdlpp::GPUBlendOp::ADD,
.srcAlphaBlendfactor = sdlpp::GPUBlendFactor::ONE,
.dstAlphaBlendfactor = sdlpp::GPUBlendFactor::ZERO,
.dstAlphaBlendfactor = sdlpp::GPUBlendFactor::ONE,
.alphaBlendOp = sdlpp::GPUBlendOp::ADD
}
}

View File

@ -0,0 +1,298 @@
#include "./app.hpp"
#include <glm/mat4x4.hpp>
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "../sdlpp/keyboard.hpp"
#include "../util/bitmap.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 ThreeDSceneApp::init(const AppInitArgs& args)
{
Application::init(args);
// create depth buffer
mDepthBuffer.create(mDevice, {
.format = sdlpp::GPUTextureFormat::D16_UNORM,
.usage = sdlpp::GPUTextureUsageFlags{.depthStencilTarget = true},
.width = 1280,
.height = 720
});
// create graphics pipeline
createMeshPipeline();
// create UI pipeline
mUIRenderer.init(*this);
// load the mesh
const Mesh mesh = loadMesh(mFileSystem.getPath("meshes/cube.obj"));
mNumVertices = static_cast<Uint32>(mesh.vertices.size());
// create vertex buffer
mVertexBuffer.create(mDevice, {
.usage = {.vertex = true},
.size = static_cast<Uint32>(mesh.vertices.size() * sizeof(Vertex))
});
uploadVertexData(mVertexBuffer, std::span(mesh.vertices));
// 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<SDL_JoystickID> gamepads = sdlpp::getGamepads();
if (!gamepads.empty())
{
mGamepad.open(gamepads[0]);
}
// init the UI
mWidgetTree.init({.renderer = &mUIRenderer});
mLabel = mWidgetTree.getRootWidget().emplaceChild<Label>({
.text = "Test-Text!\nSecond $f00line$fff?",
.color = glm::vec4(0.f, 1.f, 0.f, 1.f),
.posX = 100,
.posY = 100,
.textHeight = 20
});
mButton = mWidgetTree.getRootWidget().emplaceChild<Button>({
.text = "Click Me!",
.posX = 100,
.posY = 500
});
mButton->clicked.connect([&]()
{
mButton->setText("Thanks!");
});
}
void ThreeDSceneApp::update(const AppUpdateArgs& args)
{
Application::update(args);
processInput(args);
mLabel->setText(std::format("Rotation: {}{}\n$rUI vertices: $f8c{}", mRotation > 180.f ? "$8cf": "$fff" , mRotation, mUIRenderer.getNumVertices()));
mWidgetTree.revalidateWidgets();
// 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<float>(swapchainWidth),
/* height = */ static_cast<float>(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"
mUIRenderer.render({
.cmdBuffer = &cmdBuffer,
.targetTexture = &swapchainTexture,
.targetTextureWidth = swapchainWidth,
.targetTextureHeight = swapchainHeight
});
// finalize
cmdBuffer.submit();
}
void ThreeDSceneApp::handleKeyboardEvent(const sdlpp::KeyboardEvent& event)
{
switch (event.key)
{
case SDLK_Q:
if (!event.down)
{
mRunning = false;
}
break;
default: break;
}
}
void ThreeDSceneApp::handleMouseMotionEvent(const sdlpp::MouseMotionEvent& event)
{
mWidgetTree.notifyMouseMoved(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 ThreeDSceneApp::handleMouseButtonEvent(const sdlpp::MouseButtonEvent& event)
{
mWidgetTree.notifyMouseButton(event);
}
void ThreeDSceneApp::createMeshPipeline()
{
// 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
});
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
}
});
}
void ThreeDSceneApp::processInput(const AppUpdateArgs& args)
{
const std::span<const SDL_bool> 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<float>(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);
}
}

View File

@ -0,0 +1,49 @@
#pragma once
#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_7_3D_SCENE_APP_HPP_INCLUDED)
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_7_3D_SCENE_APP_HPP_INCLUDED 1
#include "../application.hpp"
#include "../gui/button.hpp"
#include "../gui/label.hpp"
#include "../gui/widget.hpp"
#include "../gui/ui_renderer.hpp"
#include "../sdlpp/gamepad.hpp"
namespace sdl_gpu_test
{
class ThreeDSceneApp : public Application
{
private:
sdlpp::GPUBuffer mVertexBuffer;
sdlpp::GPUTexture mDepthBuffer;
sdlpp::GPUGraphicsPipeline mMeshPipeline;
sdlpp::GPUTexture mTexture;
sdlpp::GPUSampler mSampler;
UIRenderer mUIRenderer;
WidgetTree mWidgetTree;
Label* mLabel;
Button* mButton;
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;
void handleMouseButtonEvent(const sdlpp::MouseButtonEvent&) override;
private:
void createMeshPipeline();
void processInput(const AppUpdateArgs& args);
};
} // namespace sdl_gpu_test
#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_7_3D_SCENE_APP_HPP_INCLUDED)

View File

@ -21,6 +21,7 @@ src_files = Split("""
4_textured_cube/app.cpp
5_input/app.cpp
6_ui/app.cpp
7_3d_scene/app.cpp
""")
shader_files = env.Glob("#assets/shaders/glsl/*.frag") \

View File

@ -9,6 +9,7 @@
#include "./4_textured_cube/app.hpp"
#include "./5_input/app.hpp"
#include "./6_ui/app.hpp"
#include "./7_3d_scene/app.hpp"
#include "./util/spdlog_wrapper.hpp"
namespace
@ -38,7 +39,8 @@ const std::array APPS = {
makeAppHelper<sdl_gpu_test::TexturedQuadApp>("textured_quad"),
makeAppHelper<sdl_gpu_test::TexturedCubeApp>("textured_cube"),
makeAppHelper<sdl_gpu_test::InputApp>("input"),
makeAppHelper<sdl_gpu_test::UIApp>("ui")
makeAppHelper<sdl_gpu_test::UIApp>("ui"),
makeAppHelper<sdl_gpu_test::ThreeDSceneApp>("3d_scene")
};
}