Added some simple test text.

This commit is contained in:
2024-09-17 00:52:04 +02:00
parent dd321473cf
commit d42ab3a258
9 changed files with 548 additions and 64 deletions

View File

@@ -8,7 +8,10 @@
#include <glm/gtc/matrix_transform.hpp>
#include "../sdlpp/keyboard.hpp"
#include "../util/bitmap.hpp"
#include "../util/font_map.hpp"
#include "../util/mesh.hpp"
#include "../util/texture_atlas.hpp"
namespace sdl_gpu_test
{
@@ -23,25 +26,17 @@ struct VertexShaderParameters
glm::mat4 worldToView;
glm::mat4 viewToClip;
};
struct UIVertexShaderParameters
{
glm::vec2 windowSize;
};
}
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,
@@ -51,55 +46,10 @@ void UIApp::init(const AppInitArgs& args)
});
// 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
}
});
createMeshPipeline();
// create UI pipeline
createUIPipeline();
// load the mesh
const Mesh mesh = loadMesh(mFileSystem.getPath("meshes/cube.obj"));
@@ -110,7 +60,7 @@ void UIApp::init(const AppInitArgs& args)
.usage = {.vertex = true},
.size = static_cast<Uint32>(mesh.vertices.size() * sizeof(Vertex))
});
uploadVertexData(mVertexBuffer, std::span(mesh.vertices.begin(), mesh.vertices.end()));
uploadVertexData(mVertexBuffer, std::span(mesh.vertices));
// create texture and sampler
sdlpp::GPUTextureCreateArgs textureArgs = {
@@ -120,6 +70,57 @@ void UIApp::init(const AppInitArgs& args)
mTexture = loadTexture("bitmaps/cube.png", textureArgs);
mSampler.create(mDevice, {});
const FontMap fontMap = loadFontMap(mFileSystem.getPath("fonts/symtext.fnt"));
const UVFontMap uvFontMap = makeUVFontMap({
.original = &fontMap,
.textureWidth = 256,
.textureHeight = 256
});
unsigned posX = 100;
const unsigned posY = 100;
for (const char chr : std::string_view("Dies ist ein Test-Text!"))
{
const UVFontMapEntry& entry = uvFontMap.entries[chr];
const UIVertex topLeft = {
.pos = {posX + entry.xOffset, posY + entry.yOffset},
.texcoord = {entry.uvX, entry.uvY}
};
const UIVertex bottomRight = {
.pos = {topLeft.pos.x + static_cast<float>(entry.width), topLeft.pos.y + static_cast<float>(entry.height)},
.texcoord = {entry.uvX + entry.uvWidth, entry.uvY + entry.uvHeight}
};
const UIVertex bottomLeft = {
.pos = {topLeft.pos.x, bottomRight.pos.y},
.texcoord = {topLeft.texcoord.x, bottomRight.texcoord.y}
};
const UIVertex topRight = {
.pos = {bottomRight.pos.x, topLeft.pos.y},
.texcoord = {bottomRight.texcoord.x, topLeft.texcoord.y}
};
mUIVertices.push_back(topLeft);
mUIVertices.push_back(bottomLeft);
mUIVertices.push_back(topRight);
mUIVertices.push_back(bottomRight);
mUIVertices.push_back(topRight);
mUIVertices.push_back(bottomLeft);
posX += entry.xAdvance;
}
// create UI vertex buffer
mUIVertexBuffer.create(mDevice, {
.usage = {.vertex = true},
.size = static_cast<Uint32>(mUIVertices.size() * sizeof(UIVertex))
});
uploadVertexData(mUIVertexBuffer, std::span(mUIVertices));
// and UI texture
mUITexture = loadTexture("bitmaps/ui.png", textureArgs);
mUISampler.create(mDevice, {});
// open gamepad
const std::vector<SDL_JoystickID> gamepads = sdlpp::getGamepads();
if (!gamepads.empty())
@@ -187,7 +188,25 @@ void UIApp::update(const AppUpdateArgs& args)
renderPass.end();
// render the "UI"
if (!mUIVertices.empty())
{
const UIVertexShaderParameters uiVertexShaderParameters = {
.windowSize = {
static_cast<float>(swapchainWidth), static_cast<float>(swapchainHeight)
}
};
colorTargets[0].loadOp = sdlpp::GPULoadOp::LOAD; // don't clear again
renderPass = cmdBuffer.beginRenderPass({
.colorTargetInfos = colorTargets
});
cmdBuffer.pushFragmentUniformData(0, std::span(&WHITE, 1));
cmdBuffer.pushVertexUniformData(0, std::span(&uiVertexShaderParameters, 1));
renderPass.bindFragmentSampler({.texture = mUITexture, .sampler = mUISampler});
renderPass.bindGraphicsPipeline(mUIPipeline);
renderPass.bindVertexBuffer({.buffer = mUIVertexBuffer});
renderPass.drawPrimitives({.numVertices = static_cast<Uint32>(mUIVertices.size())});
renderPass.end();
}
// finalize
cmdBuffer.submit();
@@ -216,6 +235,132 @@ void UIApp::handleMouseMotionEvent(const sdlpp::MouseMotionEvent& 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::createUIPipeline()
{
// create shaders
const sdlpp::GPUShader vertexShader = loadShader("shaders/glsl/ui.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(UIVertex)
}
};
std::array vertexAttributes = {
sdlpp::GPUVertexAttribute{
.location = 0,
.bindingIndex = 0,
.format = sdlpp::GPUVertexElementFormat::FLOAT2,
.offset = offsetof(UIVertex, pos)
},
sdlpp::GPUVertexAttribute{
.location = 1,
.bindingIndex = 0,
.format = sdlpp::GPUVertexElementFormat::FLOAT2,
.offset = offsetof(UIVertex, texcoord)
}
};
mUIPipeline.create(mDevice, {
.vertexShader = vertexShader,
.fragmentShader = fragmentShader,
.vertexInputState = {
.vertexBindings = vertexBindings,
.vertexAttributes = vertexAttributes
},
.targetInfo = {
.colorTargetDescriptions = colorTargetsDescs
}
});
}
void UIApp::processInput(const AppUpdateArgs& args)
{
const std::span<const SDL_bool> keystates = sdlpp::getKeyboardState();

View File

@@ -4,11 +4,19 @@
#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 <glm/vec2.hpp>
#include "../application.hpp"
#include "../sdlpp/gamepad.hpp"
namespace sdl_gpu_test
{
struct UIVertex
{
glm::vec2 pos;
glm::vec2 texcoord;
};
class UIApp : public Application
{
private:
@@ -17,6 +25,13 @@ private:
sdlpp::GPUGraphicsPipeline mMeshPipeline;
sdlpp::GPUTexture mTexture;
sdlpp::GPUSampler mSampler;
sdlpp::GPUBuffer mUIVertexBuffer;
sdlpp::GPUGraphicsPipeline mUIPipeline;
std::vector<UIVertex> mUIVertices;
sdlpp::GPUTexture mUITexture;
sdlpp::GPUSampler mUISampler;
sdlpp::Gamepad mGamepad;
Uint32 mNumVertices = 0;
Uint32 mLastSwapchainWidth = 1280;
@@ -29,6 +44,8 @@ public:
void handleKeyboardEvent(const sdlpp::KeyboardEvent& event) override;
void handleMouseMotionEvent(const sdlpp::MouseMotionEvent& event) override;
private:
void createMeshPipeline();
void createUIPipeline();
void processInput(const AppUpdateArgs& args);
};
} // namespace sdl_gpu_test

View File

@@ -6,6 +6,7 @@ src_files = Split("""
application.cpp
util/bitmap.cpp
util/font_map.cpp
util/mesh.cpp
0_clear_swapchain/app.cpp

View File

@@ -0,0 +1,91 @@
#include "./font_map.hpp"
#include <mijin/util/string.hpp>
namespace sdl_gpu_test
{
FontMap loadFontMap(const mijin::PathReference& path)
{
std::unique_ptr<mijin::Stream> stream;
mijin::throwOnError(path.open(mijin::FileOpenMode::READ, stream));
auto parseNumber = [](std::string_view value, auto& outNumber)
{
if (!mijin::toNumber(value, outNumber))
{
throw std::runtime_error("Invalid number.");
}
};
FontMap result;
std::string line;
while (!stream->isAtEnd())
{
mijin::throwOnError(stream->readLine(line));
const std::vector<std::string_view> parts = mijin::split(line, " ");
if (parts.empty()) {
continue;
}
if (parts[0] == "common")
{
for (const std::string_view& part : std::span(parts).subspan<1>())
{
const auto [name, value] = mijin::splitFixed<2>(part, "=");
if (name == "lineHeight") { parseNumber(value, result.lineHeight); }
else if (name == "base") { parseNumber(value, result.base); }
else if (name == "scaleW") { parseNumber(value, result.scaleW); }
else if (name == "scaleH") { parseNumber(value, result.scaleH); }
}
}
else if (parts[0] == "char")
{
FontMapEntry entry;
unsigned id = 0;
for (const std::string_view& part : std::span(parts).subspan<1>())
{
const auto [name, value] = mijin::splitFixed<2>(part, "=");
if (name == "id") { parseNumber(value, id); }
else if (name == "x") { parseNumber(value, entry.x); }
else if (name == "y") { parseNumber(value, entry.y); }
else if (name == "width") { parseNumber(value, entry.width); }
else if (name == "height") { parseNumber(value, entry.height); }
else if (name == "xoffset") { parseNumber(value, entry.xOffset); }
else if (name == "yoffset") { parseNumber(value, entry.yOffset); }
else if (name == "xadvance") { parseNumber(value, entry.xAdvance); }
}
if (id > 0 && id < result.entries.size())
{
result.entries[id] = entry;
}
}
}
return result;
}
UVFontMap makeUVFontMap(const MakeUVFontMapArgs& args)
{
UVFontMap result = {
.lineHeight = args.original->lineHeight,
.base = args.original->base,
.scaleW = args.original->scaleW,
.scaleH = args.original->scaleH
};
for (std::size_t chr = 0; chr < result.entries.size(); ++chr)
{
const FontMapEntry& origEntry = args.original->entries[chr];
result.entries[chr] = {
.uvX = static_cast<float>(origEntry.x + args.textureOffsetX) / static_cast<float>(args.textureWidth),
.uvY = static_cast<float>(origEntry.y + args.textureOffsetX) / static_cast<float>(args.textureHeight),
.uvWidth = static_cast<float>(origEntry.width) / static_cast<float>(args.textureWidth),
.uvHeight = static_cast<float>(origEntry.height) / static_cast<float>(args.textureHeight),
.width = origEntry.width,
.height = origEntry.height,
.xOffset = origEntry.xOffset,
.yOffset = origEntry.yOffset,
.xAdvance = origEntry.xAdvance
};
}
return result;
}
}

View File

@@ -0,0 +1,67 @@
#pragma once
#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_UTIL_FONT_MAP_HPP_INCLUDED)
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_UTIL_FONT_MAP_HPP_INCLUDED 1
#include <array>
#include <mijin/virtual_filesystem/filesystem.hpp>
namespace sdl_gpu_test
{
struct FontMapEntry
{
unsigned x = 0;
unsigned y = 0;
unsigned width = 0;
unsigned height = 0;
int xOffset = 0;
int yOffset = 0;
unsigned xAdvance = 0;
};
template<typename TEntry>
struct BaseFontMap
{
std::array<TEntry, 256> entries;
unsigned lineHeight = 0;
unsigned base = 0;
unsigned scaleW = 0;
unsigned scaleH = 0;
};
using FontMap = BaseFontMap<FontMapEntry>;
struct UVFontMapEntry
{
float uvX = 0.f;
float uvY = 0.f;
float uvWidth = 0.f;
float uvHeight = 0.f;
unsigned width = 0;
unsigned height = 0;
int xOffset = 0;
int yOffset = 0;
unsigned xAdvance = 0;
};
using UVFontMap = BaseFontMap<UVFontMapEntry>;
struct MakeUVFontMapArgs
{
const FontMap* original;
unsigned textureWidth;
unsigned textureHeight;
unsigned textureOffsetX = 0;
unsigned textureOffsetY = 0;
};
[[nodiscard]]
FontMap loadFontMap(const mijin::PathReference& path);
[[nodiscard]]
UVFontMap makeUVFontMap(const MakeUVFontMapArgs& args);
} // namespace sdl_gpu_test
#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_UTIL_FONT_MAP_HPP_INCLUDED)

View File

@@ -0,0 +1,14 @@
#pragma once
#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_UTIL_TEXTURE_ATLAS_HPP_INCLUDED)
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_UTIL_TEXTURE_ATLAS_HPP_INCLUDED 1
#include <vector>
namespace sdl_gpu_test
{
} // namespace sdl_gpu_test
#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_UTIL_TEXTURE_ATLAS_HPP_INCLUDED)