Text Rendering WIP

This commit is contained in:
Brandon Mak 2022-11-07 02:32:38 +08:00
parent 88530e9909
commit cc5c764307
6 changed files with 262 additions and 51 deletions

View File

@ -15,29 +15,25 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include "msdf-atlas-gen/msdf-atlas-gen.h" #include "msdf-atlas-gen/msdf-atlas-gen.h"
#include "math/SHMatrix.h"
namespace SHADE namespace SHADE
{ {
struct SH_API SHFontAsset : SHAssetData struct SH_API SHFontAsset : SHAssetData
{ {
using GlyphData = std::tuple<msdfgen::unicode_t, SHMatrix>;
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
/* MEMBER VARIABLES */ /* MEMBER VARIABLES */
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
//! Individual glyph data
std::vector<msdf_atlas::GlyphGeometry> glyphData;
//! MSDF's data structure containing the raw data of the atlas
msdfgen::Bitmap<msdfgen::byte, 3> fontBitmap;
//! Used for getting data of the font
msdf_atlas::FontGeometry fontGeometry;
//! Handle to the font loaded. We will use this when we initialize font data.
//! This is mainly the asset part of fonts.
msdfgen::FontHandle* fontHandle;
//! Name of the shader file (without parent path) //! Name of the shader file (without parent path)
std::string fontName; //std::string fontName;
//! Data containing character and uv transformation data and other misc data
std::vector<GlyphData> glyphTransformations;
//! The actual data of the atlas to go into the binary
std::unique_ptr<unsigned char[]> bitmapData;
}; };

View File

@ -0,0 +1,217 @@
#include "SHpch.h"
#include "SHFontCompiler.h"
#include "Graphics/MiddleEnd/TextRendering/SHFreetypeInstance.h"
#include "Assets/Asset Types/SHFontAsset.h"
#include "Math/Vector/SHVec2.h"
#include <fstream>
#include <iostream>
namespace SHADE
{
/***************************************************************************/
/*!
\brief
Given a valid ptr to a font asset and relevant data, initialize the data
inside the font asset. See SHFontAsset for details.
\param fontAsset
The ptr to the font asset.
\param glyphData
Individual glyph data.
\param fontBitmap
Actual bitmap data
\param fontGeometry
Font geometry required to get advance
*/
/***************************************************************************/
void SHFontCompiler::WriteToFontAsset(SHFontAsset* fontAsset, std::vector<msdf_atlas::GlyphGeometry> const& glyphData, msdfgen::Bitmap<msdfgen::byte, 3> const& fontBitmap, msdf_atlas::FontGeometry const& fontGeometry) noexcept
{
if (!fontAsset)
return;
uint32_t numGlyphs = static_cast<uint32_t>(glyphData.size());
for (uint32_t i = 0; i < numGlyphs; ++i)
{
// bounding box of the glyph in atlas
double atlasL = 0.0, atlasR = 0.0, atlasT = 0.0, atlasB = 0.0;
// bounding box of glyph as it should be placed on the baseline
double atlasPL = 0.0, atlasPR = 0.0, atlasPT = 0.0, atlasPB = 0.0;
// initialize the bounding boxes
glyphData[i].getQuadAtlasBounds(atlasL, atlasB, atlasR, atlasT);
glyphData[i].getQuadPlaneBounds(atlasPL, atlasPB, atlasPR, atlasPT);
// normalize the bounding box to (0 - 1).
atlasL /= fontBitmap.width();
atlasR /= fontBitmap.width();
atlasT /= fontBitmap.height();
atlasB /= fontBitmap.height();
// Normalized texture dimensions
SHVec2 const NORMALIZED_TEX_DIMS{ static_cast<float> (atlasR - atlasL), static_cast<float> (atlasT - atlasB) };
// When we render the quad, it has to correctly scale depending on what letter/glyph we are rendering. This is for that scale.
SHVec2 const QUAD_SCALE { static_cast<float> (atlasPR - atlasL), static_cast<float> (atlasT - atlasB) };
// initialize a matrix for uv and quad transformation data
SHMatrix transformData
{
// For scaling the tex coords
NORMALIZED_TEX_DIMS[0], 0.0f, 0.0f, 0.0f,
0.0f, NORMALIZED_TEX_DIMS[1], 0.0f, 0.0f,
// For translating the tex coords
static_cast<float>(atlasL), static_cast<float>(atlasB), 1.0f, 0.0f,
// Stores the transformation for a quad to correctly shape the glyph (first 2 values) and the bearing (last 2)
QUAD_SCALE[0], QUAD_SCALE[1], static_cast<float>(atlasPL), static_cast<float>(atlasPB)
};
// Initialize new data (we want the matrix transposed for shader use)
SHFontAsset::GlyphData newData = std::make_tuple(glyphData[i].getCodepoint(), SHMatrix::Transpose(transformData));
// Push 1 set of data for a character/glyph into the asset.
fontAsset->glyphTransformations.push_back(newData);
}
// copy data from bitmap to asset. Each channel is a 32 bit float and there are 3 channels.
fontAsset->bitmapData = std::make_unique<unsigned char[]>(fontBitmap.width() * fontBitmap.height() * 3 * sizeof (float));
}
/***************************************************************************/
/*!
\brief
Loads and compiles a font to binary format. Returns a path to the binary
data (XQ please confirm kor kor thanks <3).
\param path
Path to the font file (truetype font file) to load.
\return
Path to newly created binary data.
*/
/***************************************************************************/
std::optional<AssetPath> SHFontCompiler::LoadAndCompileFont(AssetPath path) noexcept
{
msdfgen::FontHandle* fontHandle = nullptr;
// XQ I need your help for path manipulation to actually load the msdfgen::FontHandle here. Am I doing this correctly?
fontHandle = msdfgen::loadFont(SHFreetypeInstance::GetFreetypeHandle(), path.string().c_str());
// Compile a font asset
auto* fontAsset = CompileFontToMemory(fontHandle);
// No path to binary format
if (!fontAsset)
return {};
CompileFontToBinary(path, *fontAsset);
return {};
}
/***************************************************************************/
/*!
\brief
This function takes in a font handle and generates a font asset from it.
It first geneates an atlas and all relevant data before creating the
asset.
\param fontHandle
MSDF font handle required to initialize member variables in SHFontAsset.
\return
A pointer to a brand new font asset.
*/
/***************************************************************************/
SHADE::SHFontAsset const* SHFontCompiler::CompileFontToMemory(msdfgen::FontHandle* fontHandle) noexcept
{
// Individual glyph geometry
std::vector<msdf_atlas::GlyphGeometry> glyphData;
// Actual bitmap data
msdfgen::Bitmap<msdfgen::byte, 3> fontBitmap;
// Font geometry required to get advance
msdf_atlas::FontGeometry fontGeometry (&glyphData);
// Load char set
fontGeometry.loadCharset(fontHandle, 1.0, msdf_atlas::Charset::ASCII);
// Apply MSDF edge coloring
const double maxCornerAngle = 3.0;
for (msdf_atlas::GlyphGeometry& glyph : glyphData)
glyph.edgeColoring(&msdfgen::edgeColoringInkTrap, maxCornerAngle, 0);
// configure parameters for atlas generation
msdf_atlas::TightAtlasPacker atlasPacker;
atlasPacker.setDimensionsConstraint(msdf_atlas::TightAtlasPacker::DimensionsConstraint::SQUARE);
atlasPacker.setMinimumScale(64.0);
atlasPacker.setPixelRange(2.0);
atlasPacker.setMiterLimit(1.0);
atlasPacker.pack(glyphData.data(), static_cast<int>(glyphData.size()));
// Get the dimensions after applying parameters
int width = 0, height = 0;
atlasPacker.getDimensions(width, height);
// generate the atlas
msdf_atlas::ImmediateAtlasGenerator<float, 3, msdf_atlas::msdfGenerator, msdf_atlas::BitmapAtlasStorage<float, 3>> generator(width, height);
msdf_atlas::GeneratorAttributes genAttribs;
generator.setAttributes(genAttribs);
generator.setThreadCount(4);
generator.generate(glyphData.data(), static_cast<int>(glyphData.size()));
fontBitmap = std::move(((msdfgen::Bitmap<msdfgen::byte, 3>&&)generator.atlasStorage()));
// at this point we have all the required data to initialize a font asset.
// Dynamically allocate new asset
SHFontAsset* newAsset = new SHFontAsset();
// Now we populate it with data
WriteToFontAsset(newAsset, glyphData, fontBitmap, fontGeometry);
return newAsset;
}
std::string SHFontCompiler::CompileFontToBinary(AssetPath path, SHFontAsset const& asset) noexcept
{
std::string newPath{ path.string() };
newPath = newPath.substr(0, newPath.find_last_of('.'));
newPath += SHADER_BUILT_IN_EXTENSION.data();
std::ofstream file{ newPath, std::ios::binary | std::ios::out | std::ios::trunc };
//file.write(
// reinterpret_cast<char const*>(&data.shaderType), sizeof(uint8_t)
//);
//size_t const byteCount = sizeof(uint32_t) * data.spirvBinary.size();
//file.write(
// reinterpret_cast<char const*>(&byteCount), sizeof(size_t)
//);
//file.write(
// reinterpret_cast<char const*>(data.spirvBinary.data()), byteCount
//);
file.close();
return newPath;
}
}

View File

@ -0,0 +1,22 @@
#pragma once
#include "Assets/SHAssetMacros.h"
#include "msdf-atlas-gen/msdf-atlas-gen.h"
namespace SHADE
{
class SHFontAsset;
class SHFontCompiler
{
private:
static void WriteToFontAsset (SHFontAsset* fontAsset, std::vector<msdf_atlas::GlyphGeometry> const& glyphData, msdfgen::Bitmap<msdfgen::byte, 3> const& fontBitmap, msdf_atlas::FontGeometry const& fontGeometry) noexcept;
public:
static std::optional<AssetPath> LoadAndCompileFont (AssetPath path) noexcept;
static SHFontAsset const* CompileFontToMemory (msdfgen::FontHandle* fontHandle) noexcept;
static std::string CompileFontToBinary (AssetPath path, SHFontAsset const& asset) noexcept;
};
}

View File

@ -7,46 +7,18 @@ namespace SHADE
{ {
SHADE::SHAssetData* SHFontLoader::Load(AssetPath path) SHADE::SHAssetData* SHFontLoader::Load(AssetPath path)
{ {
//auto result = new SHFontAsset();
//// save the font name
//result->fontName = path.stem().stem().string();
//result->fontHandle = msdfgen::loadFont(SHFreetypeInstance::GetFreetypeHandle(), path.string().c_str());
////result->fontGeometry = msdf_atlas::FontGeometry(&result->glyphData);
//result->fontGeometry.loadCharset(font, 1.0f, msdf_atlas::Charset::ASCII);
//// Apply MSDF edge coloring
//const double maxCornerAngle = 3.0;
//for (GlyphGeometry& glyph : glyphData)
// glyph.edgeColoring(&msdfgen::edgeColoringInkTrap, maxCornerAngle, 0);
//TightAtlasPacker atlasPacker;
//atlasPacker.setDimensionsConstraint(TightAtlasPacker::DimensionsConstraint::SQUARE);
//atlasPacker.setMinimumScale(64.0);
//atlasPacker.setPixelRange(2.0);
//atlasPacker.setMiterLimit(1.0);
//atlasPacker.pack(glyphData.data(), static_cast<int>(glyphData.size()));
//int width = 0, height = 0;
//atlasPacker.getDimensions(width, height);
//ImmediateAtlasGenerator<float, 3, msdfGenerator, BitmapAtlasStorage<float, 3>> generator(width, height);
//GeneratorAttributes genAttribs;
//generator.setAttributes(genAttribs);
//generator.setThreadCount(4);
//generator.generate(glyphData.data(), static_cast<int>(glyphData.size()));
return nullptr; return nullptr;
} }
void SHFontLoader::Write(SHAssetData const* data, AssetPath path) void SHFontLoader::Write(SHAssetData const* data, AssetPath path)
{ {
/*
Stuff to write to binary file :
- Interleaved per character data
- codepoint (actual character)
- 4x4 Matrix data (stores UV transform + extra data)
- Actual atlas texture
*/
} }

View File

@ -79,7 +79,8 @@ constexpr std::string_view META_EXTENSION {".shmeta"};
constexpr std::string_view AUDIO_EXTENSION {".ogg"}; constexpr std::string_view AUDIO_EXTENSION {".ogg"};
constexpr std::string_view AUDIO_WAV_EXTENSION {".wav"}; constexpr std::string_view AUDIO_WAV_EXTENSION {".wav"};
constexpr std::string_view SHADER_EXTENSION{ ".shshader" }; constexpr std::string_view SHADER_EXTENSION{ ".shshader" };
constexpr std::string_view SHADER_BUILT_IN_EXTENSION{".shshaderb"}; constexpr std::string_view SHADER_BUILT_IN_EXTENSION{ ".shshaderb" };
constexpr std::string_view FONT_EXTENSION{ ".shfont" };
constexpr std::string_view SCRIPT_EXTENSION {".cs"}; constexpr std::string_view SCRIPT_EXTENSION {".cs"};
constexpr std::string_view SCENE_EXTENSION {".shade"}; constexpr std::string_view SCENE_EXTENSION {".shade"};
constexpr std::string_view PREFAB_EXTENSION {".shprefab"}; constexpr std::string_view PREFAB_EXTENSION {".shprefab"};
@ -91,6 +92,7 @@ constexpr std::string_view EXTENSIONS[] = {
AUDIO_EXTENSION, AUDIO_EXTENSION,
SHADER_EXTENSION, SHADER_EXTENSION,
SHADER_BUILT_IN_EXTENSION, SHADER_BUILT_IN_EXTENSION,
FONT_EXTENSION,
MATERIAL_EXTENSION, MATERIAL_EXTENSION,
TEXTURE_EXTENSION, TEXTURE_EXTENSION,
MODEL_EXTENSION, MODEL_EXTENSION,

View File

@ -311,6 +311,8 @@ namespace SHADE
lightingSubSystem = resourceManager.Create<SHLightingSubSystem>(); lightingSubSystem = resourceManager.Create<SHLightingSubSystem>();
lightingSubSystem->Init(device, descPool); lightingSubSystem->Init(device, descPool);
SHFreetypeInstance::Init();
} }
void SHGraphicsSystem::InitBuiltInResources(void) void SHGraphicsSystem::InitBuiltInResources(void)