mirror of
https://github.com/bailwillharr/engine.git
synced 2024-09-21 04:51:18 +00:00
Add font render to texture
This commit is contained in:
parent
83e0935b15
commit
f9099e9ddf
@ -148,7 +148,7 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# stb
|
# stb
|
||||||
target_include_directories(${PROJECT_NAME} SYSTEM PRIVATE dependencies/stb)
|
target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC dependencies/stb)
|
||||||
|
|
||||||
# assimp
|
# assimp
|
||||||
set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "" FORCE)
|
set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "" FORCE)
|
||||||
|
@ -2,19 +2,33 @@
|
|||||||
#define ENGINE_INCLUDE_RESOURCES_FONT_H_
|
#define ENGINE_INCLUDE_RESOURCES_FONT_H_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <stb_truetype.h>
|
||||||
|
|
||||||
namespace engine {
|
namespace engine {
|
||||||
namespace resources {
|
namespace resources {
|
||||||
|
|
||||||
class Font {
|
class Font {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Font(const std::string& path);
|
Font(const std::string& path);
|
||||||
~Font();
|
~Font();
|
||||||
Font(const Font&) = delete;
|
Font(const Font&) = delete;
|
||||||
Font& operator=(const Font&) = delete;
|
Font& operator=(const Font&) = delete;
|
||||||
|
|
||||||
|
std::unique_ptr<std::vector<uint8_t>> GetTextBitmap(const std::string& text,
|
||||||
|
float height_px,
|
||||||
|
int& width_out,
|
||||||
|
int& height_out);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::unique_ptr<stbtt_fontinfo> font_info_{};
|
||||||
|
std::unique_ptr<std::vector<uint8_t>> font_buffer_{};
|
||||||
|
std::map<int, int> unicode_to_glyph_{};
|
||||||
|
|
||||||
|
int GetGlyphIndex(int unicode_codepoint);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace resources
|
} // namespace resources
|
||||||
|
@ -20,6 +20,9 @@ class Texture {
|
|||||||
|
|
||||||
Texture(RenderData* render_data, const std::string& path,
|
Texture(RenderData* render_data, const std::string& path,
|
||||||
Filtering filtering);
|
Filtering filtering);
|
||||||
|
Texture(RenderData* render_data, const uint8_t* bitmap, int width, int height,
|
||||||
|
Filtering filtering);
|
||||||
|
|
||||||
~Texture();
|
~Texture();
|
||||||
Texture(const Texture&) = delete;
|
Texture(const Texture&) = delete;
|
||||||
Texture& operator=(const Texture&) = delete;
|
Texture& operator=(const Texture&) = delete;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "resources/font.h"
|
#include "resources/font.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <stb_truetype.h>
|
#include <stb_truetype.h>
|
||||||
|
|
||||||
@ -10,17 +11,117 @@
|
|||||||
namespace engine::resources {
|
namespace engine::resources {
|
||||||
|
|
||||||
Font::Font(const std::string& path) {
|
Font::Font(const std::string& path) {
|
||||||
// auto font_buffer = util::ReadBinaryFile(path);
|
font_buffer_ = util::ReadBinaryFile(path);
|
||||||
|
font_info_ = std::make_unique<stbtt_fontinfo>();
|
||||||
|
|
||||||
// constexpr int FIRST_CHAR = 32;
|
if (stbtt_InitFont(font_info_.get(), font_buffer_->data(), 0) == 0) {
|
||||||
// constexpr int NUM_CHARS = 96;
|
throw std::runtime_error("Failed to initialise font!");
|
||||||
// constexpr float SCALE = 128.0f;
|
}
|
||||||
|
|
||||||
// TODO finish this
|
|
||||||
|
|
||||||
LOG_INFO("Loaded font: {}", path);
|
LOG_INFO("Loaded font: {}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
Font::~Font() {}
|
Font::~Font() {}
|
||||||
|
|
||||||
|
std::unique_ptr<std::vector<uint8_t>> Font::GetTextBitmap(
|
||||||
|
const std::string& text, float height_px, int& width_out, int& height_out) {
|
||||||
|
const float sf = stbtt_ScaleForPixelHeight(font_info_.get(), height_px);
|
||||||
|
|
||||||
|
int ascent, descent, line_gap;
|
||||||
|
stbtt_GetFontVMetrics(font_info_.get(), &ascent, &descent, &line_gap);
|
||||||
|
|
||||||
|
struct CharacterRenderInfo {
|
||||||
|
int advance; // bitmap advance
|
||||||
|
bool isEmpty;
|
||||||
|
int xoff, yoff, width, height;
|
||||||
|
unsigned char* bitmap;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<CharacterRenderInfo> characterRenderInfos{};
|
||||||
|
|
||||||
|
int width = 0;
|
||||||
|
for (size_t i = 0; i < text.size(); i++) {
|
||||||
|
const int glyph_index = GetGlyphIndex(static_cast<int>(text.at(i)));
|
||||||
|
|
||||||
|
int advanceWidth, leftSideBearing;
|
||||||
|
stbtt_GetGlyphHMetrics(font_info_.get(), glyph_index, &advanceWidth,
|
||||||
|
&leftSideBearing);
|
||||||
|
|
||||||
|
if (i == 0 && leftSideBearing < 0) {
|
||||||
|
// if the character extends before the current point
|
||||||
|
width -= leftSideBearing;
|
||||||
|
}
|
||||||
|
width += advanceWidth;
|
||||||
|
|
||||||
|
CharacterRenderInfo renderInfo{};
|
||||||
|
|
||||||
|
renderInfo.advance = advanceWidth * sf;
|
||||||
|
|
||||||
|
if (stbtt_IsGlyphEmpty(font_info_.get(), glyph_index) == 0) {
|
||||||
|
renderInfo.isEmpty = false;
|
||||||
|
} else {
|
||||||
|
renderInfo.isEmpty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!renderInfo.isEmpty) {
|
||||||
|
renderInfo.bitmap = stbtt_GetGlyphBitmap(
|
||||||
|
font_info_.get(), sf, sf, glyph_index, &renderInfo.width,
|
||||||
|
&renderInfo.height, &renderInfo.xoff, &renderInfo.yoff);
|
||||||
|
}
|
||||||
|
|
||||||
|
characterRenderInfos.push_back(renderInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t bitmap_width = std::ceil(static_cast<float>(width) * sf);
|
||||||
|
const size_t bitmap_height =
|
||||||
|
std::ceil(static_cast<float>(ascent - descent) * sf);
|
||||||
|
auto bitmap =
|
||||||
|
std::make_unique<std::vector<uint8_t>>(bitmap_width * bitmap_height * 4);
|
||||||
|
|
||||||
|
int top_left_x = 0;
|
||||||
|
for (const auto& renderInfo : characterRenderInfos) {
|
||||||
|
if (renderInfo.isEmpty == false) {
|
||||||
|
top_left_x += renderInfo.xoff;
|
||||||
|
const int top_left_y = static_cast<int>(ascent * sf) + renderInfo.yoff;
|
||||||
|
const int char_bitmap_width = renderInfo.width;
|
||||||
|
const int char_bitmap_height = renderInfo.height;
|
||||||
|
|
||||||
|
// copy the 8bpp char bitmap to the 4bpp output
|
||||||
|
for (int y = 0; y < char_bitmap_height; y++) {
|
||||||
|
for (int x = 0; x < char_bitmap_width; x++) {
|
||||||
|
const int out_index =
|
||||||
|
((bitmap_width * (top_left_y + y)) + (top_left_x + x)) * 4;
|
||||||
|
const uint8_t pixel = renderInfo.bitmap[y * char_bitmap_width + x];
|
||||||
|
(*bitmap)[out_index + 0] = pixel;
|
||||||
|
(*bitmap)[out_index + 1] = pixel;
|
||||||
|
(*bitmap)[out_index + 2] = pixel;
|
||||||
|
(*bitmap)[out_index + 3] = 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stbtt_FreeBitmap(renderInfo.bitmap, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
top_left_x += renderInfo.advance - renderInfo.xoff;
|
||||||
|
}
|
||||||
|
|
||||||
|
width_out = bitmap_width;
|
||||||
|
height_out = bitmap_height;
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Font::GetGlyphIndex(int unicode_codepoint) {
|
||||||
|
if (unicode_to_glyph_.contains(unicode_codepoint)) {
|
||||||
|
return unicode_to_glyph_.at(unicode_codepoint);
|
||||||
|
} else {
|
||||||
|
const int glyph_index =
|
||||||
|
stbtt_FindGlyphIndex(font_info_.get(), unicode_codepoint);
|
||||||
|
if (glyph_index == 0) {
|
||||||
|
throw std::runtime_error("Glyph not found in font!");
|
||||||
|
}
|
||||||
|
unicode_to_glyph_.emplace(std::make_pair(unicode_codepoint, glyph_index));
|
||||||
|
return glyph_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace engine::resources
|
} // namespace engine::resources
|
||||||
|
@ -8,12 +8,12 @@
|
|||||||
|
|
||||||
namespace engine::resources {
|
namespace engine::resources {
|
||||||
|
|
||||||
Texture::Texture(RenderData* renderData, const std::string& path, Filtering filtering)
|
Texture::Texture(RenderData* renderData, const std::string& path,
|
||||||
: gfx_(renderData->gfxdev.get())
|
Filtering filtering)
|
||||||
{
|
: gfx_(renderData->gfxdev.get()) {
|
||||||
|
|
||||||
int width, height;
|
int width, height;
|
||||||
std::unique_ptr<std::vector<uint8_t>> texbuf = util::ReadImageFile(path, &width, &height);
|
std::unique_ptr<std::vector<uint8_t>> texbuf =
|
||||||
|
util::ReadImageFile(path, &width, &height);
|
||||||
|
|
||||||
gfx::SamplerInfo samplerInfo{};
|
gfx::SamplerInfo samplerInfo{};
|
||||||
|
|
||||||
@ -42,20 +42,62 @@ Texture::Texture(RenderData* renderData, const std::string& path, Filtering filt
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (renderData->samplers.contains(samplerInfo) == false) {
|
if (renderData->samplers.contains(samplerInfo) == false) {
|
||||||
renderData->samplers.insert(std::make_pair(samplerInfo, gfx_->CreateSampler(samplerInfo)));
|
renderData->samplers.insert(
|
||||||
|
std::make_pair(samplerInfo, gfx_->CreateSampler(samplerInfo)));
|
||||||
}
|
}
|
||||||
|
|
||||||
image_ = gfx_->CreateImage(width, height, texbuf->data());
|
image_ = gfx_->CreateImage(width, height, texbuf->data());
|
||||||
descriptor_set_ = gfx_->AllocateDescriptorSet(renderData->material_set_layout);
|
descriptor_set_ =
|
||||||
gfx_->UpdateDescriptorCombinedImageSampler(descriptor_set_, 0, image_, renderData->samplers.at(samplerInfo));
|
gfx_->AllocateDescriptorSet(renderData->material_set_layout);
|
||||||
|
gfx_->UpdateDescriptorCombinedImageSampler(
|
||||||
|
descriptor_set_, 0, image_, renderData->samplers.at(samplerInfo));
|
||||||
|
|
||||||
LOG_INFO("Loaded texture: {}, width: {} height: {}", path, width, height);
|
LOG_INFO("Loaded texture: {}, width: {} height: {}", path, width, height);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture::~Texture()
|
Texture::Texture(RenderData* render_data, const uint8_t* bitmap, int width,
|
||||||
{
|
int height, Filtering filtering)
|
||||||
gfx_->DestroyImage(image_);
|
: gfx_(render_data->gfxdev.get()) {
|
||||||
|
gfx::SamplerInfo samplerInfo{};
|
||||||
|
|
||||||
|
samplerInfo.magnify = gfx::Filter::kLinear;
|
||||||
|
|
||||||
|
switch (filtering) {
|
||||||
|
case Filtering::kOff:
|
||||||
|
samplerInfo.minify = gfx::Filter::kNearest;
|
||||||
|
samplerInfo.mipmap = gfx::Filter::kNearest;
|
||||||
|
samplerInfo.anisotropic_filtering = false;
|
||||||
|
break;
|
||||||
|
case Filtering::kBilinear:
|
||||||
|
samplerInfo.minify = gfx::Filter::kLinear;
|
||||||
|
samplerInfo.mipmap = gfx::Filter::kNearest;
|
||||||
|
samplerInfo.anisotropic_filtering = false;
|
||||||
|
break;
|
||||||
|
case Filtering::kTrilinear:
|
||||||
|
samplerInfo.minify = gfx::Filter::kLinear;
|
||||||
|
samplerInfo.mipmap = gfx::Filter::kLinear;
|
||||||
|
samplerInfo.anisotropic_filtering = false;
|
||||||
|
break;
|
||||||
|
case Filtering::kAnisotropic:
|
||||||
|
samplerInfo.minify = gfx::Filter::kLinear;
|
||||||
|
samplerInfo.mipmap = gfx::Filter::kLinear;
|
||||||
|
samplerInfo.anisotropic_filtering = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (render_data->samplers.contains(samplerInfo) == false) {
|
||||||
|
render_data->samplers.insert(
|
||||||
|
std::make_pair(samplerInfo, gfx_->CreateSampler(samplerInfo)));
|
||||||
|
}
|
||||||
|
|
||||||
|
image_ = gfx_->CreateImage(width, height, bitmap);
|
||||||
|
descriptor_set_ =
|
||||||
|
gfx_->AllocateDescriptorSet(render_data->material_set_layout);
|
||||||
|
gfx_->UpdateDescriptorCombinedImageSampler(
|
||||||
|
descriptor_set_, 0, image_, render_data->samplers.at(samplerInfo));
|
||||||
|
|
||||||
|
LOG_INFO("Loaded texture: BITMAP, width: {} height: {}", width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
Texture::~Texture() { gfx_->DestroyImage(image_); }
|
||||||
|
|
||||||
|
} // namespace engine::resources
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "components/transform.h"
|
#include "components/transform.h"
|
||||||
#include "input_manager.h"
|
#include "input_manager.h"
|
||||||
#include "meshgen.hpp"
|
#include "meshgen.hpp"
|
||||||
|
#include "resources/font.h"
|
||||||
#include "resources/material.h"
|
#include "resources/material.h"
|
||||||
#include "resources/texture.h"
|
#include "resources/texture.h"
|
||||||
#include "scene.h"
|
#include "scene.h"
|
||||||
@ -86,6 +87,7 @@ void PlayGame(GameSettings settings) {
|
|||||||
auto grass_texture = std::make_shared<engine::resources::Texture>(
|
auto grass_texture = std::make_shared<engine::resources::Texture>(
|
||||||
&app.render_data_, app.GetResourcePath("textures/grass.jpg"),
|
&app.render_data_, app.GetResourcePath("textures/grass.jpg"),
|
||||||
engine::resources::Texture::Filtering::kAnisotropic);
|
engine::resources::Texture::Filtering::kAnisotropic);
|
||||||
|
|
||||||
auto space_texture = std::make_shared<engine::resources::Texture>(
|
auto space_texture = std::make_shared<engine::resources::Texture>(
|
||||||
&app.render_data_, app.GetResourcePath("textures/space2.png"),
|
&app.render_data_, app.GetResourcePath("textures/space2.png"),
|
||||||
engine::resources::Texture::Filtering::kAnisotropic);
|
engine::resources::Texture::Filtering::kAnisotropic);
|
||||||
@ -99,8 +101,8 @@ void PlayGame(GameSettings settings) {
|
|||||||
my_scene->AddComponent<engine::RenderableComponent>(cube);
|
my_scene->AddComponent<engine::RenderableComponent>(cube);
|
||||||
cube_renderable->material = std::make_shared<engine::resources::Material>(
|
cube_renderable->material = std::make_shared<engine::resources::Material>(
|
||||||
app.GetResource<engine::resources::Shader>("builtin.standard"));
|
app.GetResource<engine::resources::Shader>("builtin.standard"));
|
||||||
cube_renderable->material->texture_ =
|
cube_renderable->material->texture_ = grass_texture;
|
||||||
app.GetResource<engine::resources::Texture>("builtin.white");
|
// app.GetResource<engine::resources::Texture>("builtin.white");
|
||||||
cube_renderable->mesh = GenCuboidMesh(app.gfxdev(), 1.0f, 1.0f, 1.0f, 1);
|
cube_renderable->mesh = GenCuboidMesh(app.gfxdev(), 1.0f, 1.0f, 1.0f, 1);
|
||||||
auto cube_collider =
|
auto cube_collider =
|
||||||
my_scene->AddComponent<engine::ColliderComponent>(cube);
|
my_scene->AddComponent<engine::ColliderComponent>(cube);
|
||||||
@ -127,8 +129,8 @@ void PlayGame(GameSettings settings) {
|
|||||||
floor_collider->aabb = {{0.0f, 0.0f, 0.0f}, {10000.0f, 1.0f, 10000.0f}};
|
floor_collider->aabb = {{0.0f, 0.0f, 0.0f}, {10000.0f, 1.0f, 10000.0f}};
|
||||||
}
|
}
|
||||||
|
|
||||||
engine::util::LoadMeshFromFile(
|
//engine::util::LoadMeshFromFile(
|
||||||
my_scene, app.GetResourcePath("models/astronaut/astronaut.dae"));
|
// my_scene, app.GetResourcePath("models/astronaut/astronaut.dae"));
|
||||||
|
|
||||||
/* skybox */
|
/* skybox */
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user