295 lines
9.0 KiB
C++
295 lines
9.0 KiB
C++
|
|
#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 UIApp::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("meshes/cube.png", textureArgs);
|
|
mSampler.create(mDevice, {});
|
|
|
|
// open gamepad
|
|
mGamepad = openFirstGamepad();
|
|
|
|
// 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 UIApp::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 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)
|
|
{
|
|
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 UIApp::handleMouseButtonEvent(const sdlpp::MouseButtonEvent& event)
|
|
{
|
|
mWidgetTree.notifyMouseButton(event);
|
|
}
|
|
|
|
|
|
void UIApp::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 UIApp::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);
|
|
}
|
|
}
|