Added texture packer for automatically creating texture atlas on build.

This commit is contained in:
2024-09-19 14:59:14 +02:00
parent b29fefa6ec
commit cf4d4cb8a6
16 changed files with 408 additions and 15 deletions

View File

@@ -11,6 +11,7 @@ src_files = Split("""
util/bitmap.cpp
util/font_map.cpp
util/mesh.cpp
util/texture_atlas.cpp
0_clear_swapchain/app.cpp
1_green_triangle/app.cpp
@@ -48,4 +49,16 @@ for shader_file in shader_files:
spv = env.SpirV(source = shader_file, target=f'{shader_file}.spv')
env.Default(spv)
ui_textures = Split("""
#assets/fonts/symtex.png
#assets/bitmaps/cube.png
#assets/bitmaps/sdl.png
""")
pack = env.PackTextures(
target = '#assets/bitmaps/ui.png',
source = ui_textures
)
env.Default(pack)
Return('env')

View File

@@ -84,14 +84,6 @@ void UIRenderer::init(Application& application)
}
});
// load the font map for rendering text
const FontMap fontMap = loadFontMap(application.getFileSystem().getPath("fonts/symtext.fnt"));
mFontMap = makeUVFontMap({
.original = &fontMap,
.textureWidth = 256,
.textureHeight = 256
});
// create texture and sampler
sdlpp::GPUTextureCreateArgs textureArgs = {
.format = sdlpp::GPUTextureFormat::R8G8B8A8_UNORM_SRGB,
@@ -99,6 +91,30 @@ void UIRenderer::init(Application& application)
};
mTexture = application.loadTexture("bitmaps/ui.png", textureArgs);
// load the texture atlas
const TextureAtlas textureAtlas = loadTextureAtlas(application.getFileSystem().getPath("bitmaps/ui.txt"));
mTextureAtlas = makeUVTextureAtlas({
.original = &textureAtlas,
.textureWidth = textureArgs.width,
.textureHeight = textureArgs.height
});
auto itEntry = mTextureAtlas.entries.find("assets/fonts/symtex.png");
if (itEntry == mTextureAtlas.entries.end())
{
throw std::runtime_error("Texture atlas is missing entry for the font.");
}
// load the font map for rendering text
const FontMap fontMap = loadFontMap(application.getFileSystem().getPath("fonts/symtext.fnt"));
mFontMap = makeUVFontMap({
.original = &fontMap,
.textureWidth = textureArgs.width,
.textureHeight = textureArgs.height,
.textureOffsetX = itEntry->second.uvX,
.textureOffsetY = itEntry->second.uvY
});
mSampler.create(device, {});
}

View File

@@ -9,6 +9,7 @@
#include "../application.hpp"
#include "../sdlpp/gpu.hpp"
#include "../util/font_map.hpp"
#include "../util/texture_atlas.hpp"
namespace sdl_gpu_test::inline app6
{
@@ -58,6 +59,7 @@ private:
std::vector<UIVertex> mVertices;
std::vector<primitive_id_t> mTriangleOwners;
primitive_id_t mNextOwner = 1;
UVTextureAtlas mTextureAtlas;
UVFontMap mFontMap;
bool mVerticesDirty = true;
std::size_t mVertexBufferSize = 0;

View File

@@ -72,7 +72,7 @@ int main(int argc, char* argv[])
}
catch (std::runtime_error& error)
{
spdlog::error("{}.", error.what());
spdlog::error("{}", error.what());
return USER_ERROR;
}

View File

@@ -75,8 +75,8 @@ UVFontMap makeUVFontMap(const MakeUVFontMapArgs& args)
{
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),
.uvX = static_cast<float>(origEntry.x) / static_cast<float>(args.textureWidth) + args.textureOffsetX,
.uvY = static_cast<float>(origEntry.y) / static_cast<float>(args.textureHeight) + args.textureOffsetY,
.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,

View File

@@ -53,8 +53,8 @@ struct MakeUVFontMapArgs
const FontMap* original;
unsigned textureWidth;
unsigned textureHeight;
unsigned textureOffsetX = 0;
unsigned textureOffsetY = 0;
float textureOffsetX = 0.f;
float textureOffsetY = 0.f;
};
[[nodiscard]]

View File

@@ -0,0 +1,53 @@
#include "./texture_atlas.hpp"
#include <mijin/util/string.hpp>
namespace sdl_gpu_test
{
TextureAtlas loadTextureAtlas(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.");
}
};
TextureAtlas result;
std::string line;
while (!stream->isAtEnd())
{
mijin::throwOnError(stream->readLine(line));
const auto [name, x, y, width, height] = mijin::splitFixed<5>(line, " ");
TextureAtlasEntry entry;
parseNumber(x, entry.x);
parseNumber(y, entry.y);
parseNumber(width, entry.width);
parseNumber(height, entry.height);
result.entries.emplace(name, entry);
}
return result;
}
UVTextureAtlas makeUVTextureAtlas(const MakeUVTextureAtlasArgs& args)
{
UVTextureAtlas result;
for (const auto& [name, origEntry] : args.original->entries)
{
result.entries.emplace(name, UVTextureAtlasEntry{
.uvX = static_cast<float>(origEntry.x) / static_cast<float>(args.textureWidth),
.uvY = static_cast<float>(origEntry.y) / 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
});
}
return result;
}
}

View File

@@ -4,11 +4,53 @@
#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>
#include <string>
#include <unordered_map>
#include <mijin/virtual_filesystem/filesystem.hpp>
namespace sdl_gpu_test
{
struct TextureAtlasEntry
{
unsigned x = 0;
unsigned y = 0;
unsigned width = 0;
unsigned height = 0;
};
struct TextureAtlas
{
std::unordered_map<std::string, TextureAtlasEntry> entries;
};
struct UVTextureAtlasEntry
{
float uvX = 0.f;
float uvY = 0.f;
float uvWidth = 0.f;
float uvHeight = 0.f;
unsigned width = 0;
unsigned height = 0;
};
struct UVTextureAtlas
{
std::unordered_map<std::string, UVTextureAtlasEntry> entries;
};
struct MakeUVTextureAtlasArgs
{
const TextureAtlas* original;
unsigned textureWidth;
unsigned textureHeight;
};
[[nodiscard]]
TextureAtlas loadTextureAtlas(const mijin::PathReference& path);
[[nodiscard]]
UVTextureAtlas makeUVTextureAtlas(const MakeUVTextureAtlasArgs& args);
} // namespace sdl_gpu_test
#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_UTIL_TEXTURE_ATLAS_HPP_INCLUDED)