mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
Improved MSDF generation and rendering
This commit is contained in:
@@ -9,6 +9,27 @@ namespace GlitchyEngine.Renderer
|
|||||||
|
|
||||||
public class Renderer2D
|
public class Renderer2D
|
||||||
{
|
{
|
||||||
|
enum DrawOrder
|
||||||
|
{
|
||||||
|
/// Immediately draw the sprites.
|
||||||
|
Immediate,
|
||||||
|
/**
|
||||||
|
* Deferres rendering until the call of End().
|
||||||
|
* Sorts the sprites by their texture.
|
||||||
|
*/
|
||||||
|
SortByTexture,
|
||||||
|
/**
|
||||||
|
* Deferres rendering until the call of End().
|
||||||
|
* Sorts the sprites so that the backmost will be drawn first and the frontmost last.
|
||||||
|
*/
|
||||||
|
BackToFront,
|
||||||
|
/**
|
||||||
|
* Deferres rendering until the call of End().
|
||||||
|
* Sorts the sprites so that the frontmost will be drawn first and the backmost last.
|
||||||
|
*/
|
||||||
|
FrontToBack
|
||||||
|
}
|
||||||
|
|
||||||
struct RenderVertex : IVertexData
|
struct RenderVertex : IVertexData
|
||||||
{
|
{
|
||||||
public Vector2 Position;
|
public Vector2 Position;
|
||||||
@@ -66,6 +87,10 @@ namespace GlitchyEngine.Renderer
|
|||||||
|
|
||||||
private BatchVertex[] _rawInstances = new BatchVertex[1024] ~ delete _;
|
private BatchVertex[] _rawInstances = new BatchVertex[1024] ~ delete _;
|
||||||
private uint32 _setInstances = 0;
|
private uint32 _setInstances = 0;
|
||||||
|
|
||||||
|
Matrix _projection;
|
||||||
|
|
||||||
|
DrawOrder _drawOrder;
|
||||||
|
|
||||||
/// The effect that is currently being used to draw the sprites.
|
/// The effect that is currently being used to draw the sprites.
|
||||||
private Effect _currentEffect ~ _?.ReleaseRef();
|
private Effect _currentEffect ~ _?.ReleaseRef();
|
||||||
@@ -160,31 +185,6 @@ namespace GlitchyEngine.Renderer
|
|||||||
instancingBinding.SetIndexBuffer(quadBinding.GetIndexBuffer(), 0);
|
instancingBinding.SetIndexBuffer(quadBinding.GetIndexBuffer(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Matrix _projection;
|
|
||||||
|
|
||||||
DrawOrder _drawOrder;
|
|
||||||
|
|
||||||
enum DrawOrder
|
|
||||||
{
|
|
||||||
/// Immediately draw the sprites.
|
|
||||||
Immediate,
|
|
||||||
/**
|
|
||||||
* Deferres rendering until the call of End().
|
|
||||||
* Sorts the sprites by their texture.
|
|
||||||
*/
|
|
||||||
SortByTexture,
|
|
||||||
/**
|
|
||||||
* Deferres rendering until the call of End().
|
|
||||||
* Sorts the sprites so that the backmost will be drawn first and the frontmost last.
|
|
||||||
*/
|
|
||||||
BackToFront,
|
|
||||||
/**
|
|
||||||
* Deferres rendering until the call of End().
|
|
||||||
* Sorts the sprites so that the frontmost will be drawn first and the backmost last.
|
|
||||||
*/
|
|
||||||
FrontToBack
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the renderer.
|
* Initializes the renderer.
|
||||||
* @param drawOrder Determines if and how the sprites will be ordered before rendering.
|
* @param drawOrder Determines if and how the sprites will be ordered before rendering.
|
||||||
|
|||||||
@@ -16,11 +16,20 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
|
|
||||||
public FT_UInt GlyphIndex;
|
public FT_UInt GlyphIndex;
|
||||||
|
|
||||||
|
// TODO: Consider using floats
|
||||||
public Int32_3 MapCoord;
|
public Int32_3 MapCoord;
|
||||||
public int32 SizeX, SizeY;
|
public int32 Width, Height;
|
||||||
|
|
||||||
public FT_Glyph_Metrics Metrics;
|
public double TranslationX, TranslationY;
|
||||||
|
|
||||||
|
/// How many pixels we have to move the pen after drawing this glyph.
|
||||||
|
public float Advance;
|
||||||
|
|
||||||
|
// Aligns the image of the glyph with the baseline
|
||||||
|
public float AdjustToBaseLine;
|
||||||
|
// Aligns the image of the glyph with the pen
|
||||||
|
public float AdjustToPen;
|
||||||
|
|
||||||
public bool IsBitmap;
|
public bool IsBitmap;
|
||||||
|
|
||||||
public bool IsCalculated = false;
|
public bool IsCalculated = false;
|
||||||
@@ -49,7 +58,7 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
/// How much we have to scale the geometry to fit it into our desired pixels
|
/// How much we have to scale the geometry to fit it into our desired pixels
|
||||||
private double _geometryScaler;
|
private double _geometryScaler;
|
||||||
|
|
||||||
private double _range = 4.0;
|
internal double _range = 4.0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets or sets the fallback Font for this Font.
|
* Gets or sets the fallback Font for this Font.
|
||||||
@@ -189,7 +198,7 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Debug only
|
/// Debug only
|
||||||
public void RedrawAtlas()
|
private void RedrawAtlas()
|
||||||
{
|
{
|
||||||
for(let (char, desc) in _glyphs)
|
for(let (char, desc) in _glyphs)
|
||||||
{
|
{
|
||||||
@@ -200,6 +209,10 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
_penPos = .Zero;
|
_penPos = .Zero;
|
||||||
_lastRowHeight = 0;
|
_lastRowHeight = 0;
|
||||||
|
|
||||||
|
int8[] data = new int8[_atlas.Width * _atlas.Height * 4];
|
||||||
|
|
||||||
|
_atlas.SetData<Color>((Color*)data.Ptr);
|
||||||
|
|
||||||
UpdateAtlas();
|
UpdateAtlas();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,92 +222,71 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
const uint32 maxArray = 2048; // D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION
|
const uint32 maxArray = 2048; // D3D11_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION
|
||||||
|
|
||||||
ref Int32_3 pen = ref _penPos;
|
ref Int32_3 pen = ref _penPos;
|
||||||
ref int32 penLeft = ref _penPos.X;
|
|
||||||
ref int32 penTop = ref _penPos.Y;
|
|
||||||
ref int32 penArray = ref _penPos.Z;
|
|
||||||
ref int32 rowHeight = ref _lastRowHeight;
|
ref int32 rowHeight = ref _lastRowHeight;
|
||||||
|
|
||||||
int32 atlasWidth = _atlasSize.X;
|
int32 atlasWidth = _atlasSize.X;
|
||||||
int32 atlasHeight = _atlasSize.Y;
|
int32 atlasHeight = _atlasSize.Y;
|
||||||
int32 atlasArray = _atlasSize.Z;
|
int32 atlasArray = _atlasSize.Z;
|
||||||
|
|
||||||
for(let (char, desc) in _glyphs)
|
for(var (char, desc) in _glyphs)
|
||||||
{
|
{
|
||||||
if(desc == null || desc.IsCalculated)
|
if(desc == null || desc.IsCalculated)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let loadResult = FreeType.Load_Glyph(_face, desc.GlyphIndex, .Color | .TargetNormal);
|
if(!Calculate(ref desc))
|
||||||
if(loadResult.Error)
|
continue;
|
||||||
|
|
||||||
|
const int32 border = 1;
|
||||||
|
|
||||||
|
if(desc.Width != 0 && desc.Height != 0)
|
||||||
{
|
{
|
||||||
Log.EngineLogger.Error($"Failed to load glyph for '{char}'(Char Code:{(uint32)char} | Error {(int)loadResult}): {loadResult}");
|
int32 glyphRight = pen.X + desc.Width + border;
|
||||||
}
|
|
||||||
|
|
||||||
desc.Metrics = _face.glyph.metrics;
|
|
||||||
|
|
||||||
int32 border = 2;
|
|
||||||
|
|
||||||
// TODO: adjust size to glyph
|
|
||||||
int32 glyphWidth = (.)_fontSize + border;
|
|
||||||
//int32 glyphWidth = (desc.Metrics.width / 64) + 1;
|
|
||||||
int32 glyphHeight = (.)_fontSize + border;
|
|
||||||
//int32 glyphHeight = (desc.Metrics.height / 64) + 1;
|
|
||||||
|
|
||||||
// when size is 0 (1 because we added 1) the glyph has no image
|
|
||||||
if(glyphWidth != border || glyphHeight != border)
|
|
||||||
{
|
|
||||||
//int32 glyphRight = penLeft + glyphWidth;
|
|
||||||
|
|
||||||
int32 glyphRight = penLeft + glyphWidth + border;
|
|
||||||
|
|
||||||
if(glyphRight > maxRes)
|
if(glyphRight > maxRes)
|
||||||
{
|
{
|
||||||
penLeft = 0;
|
pen.X = 0;
|
||||||
penTop += rowHeight;
|
pen.Y += rowHeight + border;
|
||||||
rowHeight = 0;
|
rowHeight = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 glyphBottom = penTop + glyphHeight + border;
|
int32 glyphBottom = pen.Y + desc.Height + border;
|
||||||
|
|
||||||
if(glyphBottom > maxRes)
|
if(glyphBottom > maxRes)
|
||||||
{
|
{
|
||||||
penLeft = 0;
|
pen.X = 0;
|
||||||
penTop = 0;
|
pen.Y = 0;
|
||||||
rowHeight = 0;
|
rowHeight = 0;
|
||||||
|
|
||||||
penArray++;
|
pen.Z++;
|
||||||
}
|
|
||||||
|
if(pen.Z >= maxArray)
|
||||||
if(penArray >= maxArray)
|
{
|
||||||
{
|
Log.EngineLogger.Error("Sprite font exceeded maximum texture size.");
|
||||||
Log.EngineLogger.Error("Spritefont exceeds maximum texture size.");
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
desc.MapCoord = pen;
|
desc.MapCoord = pen;
|
||||||
//desc.SizeX = glyphWidth - 1;
|
|
||||||
//desc.SizeY = glyphHeight - 1;
|
|
||||||
desc.SizeX = (.)_fontSize;
|
|
||||||
desc.SizeY = (.)_fontSize;
|
|
||||||
|
|
||||||
if(desc.GlyphIndex == 0 && _missingGlyph == null)
|
if(desc.GlyphIndex == 0 && _missingGlyph == null)
|
||||||
{
|
{
|
||||||
_missingGlyph = desc;
|
_missingGlyph = desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
penLeft += glyphWidth;
|
|
||||||
|
|
||||||
if (penLeft > atlasWidth)
|
pen.X += desc.Width + border;
|
||||||
atlasWidth = penLeft;
|
|
||||||
|
|
||||||
if (glyphHeight > rowHeight)
|
if (pen.X > atlasWidth)
|
||||||
rowHeight = glyphHeight;
|
atlasWidth = pen.X;
|
||||||
|
|
||||||
if (penTop + glyphHeight > atlasHeight)
|
if (desc.Height > rowHeight)
|
||||||
atlasHeight = penTop + glyphHeight;
|
rowHeight = desc.Height;
|
||||||
|
|
||||||
if (penArray == atlasArray)
|
if (pen.Y + desc.Height > atlasHeight)
|
||||||
|
atlasHeight = pen.Y + desc.Height;
|
||||||
|
|
||||||
|
if (pen.Z == atlasArray)
|
||||||
atlasArray++;
|
atlasArray++;
|
||||||
|
|
||||||
desc.IsCalculated = true;
|
desc.IsCalculated = true;
|
||||||
@@ -340,37 +332,40 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
|
|
||||||
var glyphIndex = desc.GlyphIndex;
|
var glyphIndex = desc.GlyphIndex;
|
||||||
|
|
||||||
if(desc.IsRendered || glyphIndex == 0 || desc.SizeX == 0 || desc.SizeY == 0)
|
if(desc.IsRendered || glyphIndex == 0 || desc.Width == 0 || desc.Height == 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
DoTheThing(desc);
|
GenerateMSDF(desc);
|
||||||
|
|
||||||
desc.IsRendered = true;
|
desc.IsRendered = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoTheThing(GlyphDescriptor desc)
|
bool Calculate(ref GlyphDescriptor desc)
|
||||||
{
|
{
|
||||||
// prepare shape
|
// prepare shape
|
||||||
|
|
||||||
double advance = 0;
|
double advance = 0;
|
||||||
|
|
||||||
Shape shape;
|
Shape shape;
|
||||||
msdfgen.LoadGlyph(out shape, ref _face, desc.GlyphIndex, out advance);
|
if(!msdfgen.LoadGlyph(out shape, ref _face, desc.GlyphIndex, out advance) || !shape.Validate())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
desc.Advance = (float)(advance * _geometryScaler);
|
||||||
|
|
||||||
shape.Normalize();
|
shape.Normalize();
|
||||||
|
|
||||||
var bounds = shape.GetBounds();
|
var bounds = shape.GetBounds();
|
||||||
|
|
||||||
msdfgen.EdgeColoringSimple(shape, 3.0);
|
|
||||||
|
|
||||||
// prepare projection
|
// prepare projection
|
||||||
|
|
||||||
int width;
|
double width;
|
||||||
int height;
|
double height;
|
||||||
|
|
||||||
double translationX;
|
double translationX;
|
||||||
double translationY;
|
double translationY;
|
||||||
|
|
||||||
@@ -385,14 +380,16 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
b -= 0.5 * _range;
|
b -= 0.5 * _range;
|
||||||
r += 0.5 * _range;
|
r += 0.5 * _range;
|
||||||
t += 0.5 * _range;
|
t += 0.5 * _range;
|
||||||
|
|
||||||
// TODO: miter?!
|
// TODO: miter?
|
||||||
|
//if (miterLimit > 0)
|
||||||
|
// shape.boundMiters(l, b, r, t, .5*range, miterLimit, 1);
|
||||||
|
|
||||||
double w = _geometryScaler * (r - l);
|
double w = _geometryScaler * (r - l);
|
||||||
double h = _geometryScaler * (t - b);
|
double h = _geometryScaler * (t - b);
|
||||||
|
|
||||||
width = (int)Math.Ceiling(w) + 1;
|
width = Math.Ceiling(w) + 1;
|
||||||
height = (int)Math.Ceiling(h) + 1;
|
height = Math.Ceiling(h) + 1;
|
||||||
|
|
||||||
translationX = -l + 0.5 * (width - w) / _geometryScaler;
|
translationX = -l + 0.5 * (width - w) / _geometryScaler;
|
||||||
translationY = -b + 0.5 * (height - h) / _geometryScaler;
|
translationY = -b + 0.5 * (height - h) / _geometryScaler;
|
||||||
@@ -405,59 +402,66 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
translationY = 0;
|
translationY = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
desc.Width = (.)width;
|
||||||
|
desc.Height = (.)height;
|
||||||
|
|
||||||
|
desc.TranslationX = translationX;
|
||||||
|
desc.TranslationY = translationY;
|
||||||
|
|
||||||
|
//desc.AdjustToBaseLine = (float)(- height + _range + (_face.glyph.metrics.height / 64 - _face.glyph.metrics.horiBearingY / 64) * _geometryScaler);
|
||||||
|
//(float)(- height + translationY * _geometryScaler + (_face.glyph.metrics.height / 64 - _face.glyph.metrics.horiBearingY / 64) * _geometryScaler);
|
||||||
|
desc.AdjustToBaseLine = (float)(-height + translationY * _geometryScaler);
|
||||||
|
|
||||||
|
//desc.AdjustToPen = (float)(- _range + (_face.glyph.metrics.horiBearingX / 64) * _geometryScaler);
|
||||||
|
desc.AdjustToPen = (float)(-translationX);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenerateMSDF(GlyphDescriptor desc)
|
||||||
|
{
|
||||||
|
// prepare shape
|
||||||
|
|
||||||
|
double advance = 0;
|
||||||
|
|
||||||
|
Shape shape;
|
||||||
|
msdfgen.LoadGlyph(out shape, ref _face, desc.GlyphIndex, out advance);
|
||||||
|
|
||||||
|
shape.Normalize();
|
||||||
|
|
||||||
|
var bounds = shape.GetBounds();
|
||||||
|
|
||||||
|
shape.ReverseIfNeeded(bounds);
|
||||||
|
|
||||||
|
msdfgen.EdgeColoringSimple(shape, 3.0);
|
||||||
|
|
||||||
|
// prepare projection
|
||||||
msdfgen.Projection projection = .();
|
msdfgen.Projection projection = .();
|
||||||
projection.ScaleX = _geometryScaler;
|
projection.ScaleX = _geometryScaler;
|
||||||
projection.ScaleY = _geometryScaler;
|
projection.ScaleY = _geometryScaler;
|
||||||
|
|
||||||
projection.TranslationX = translationX;
|
projection.TranslationX = desc.TranslationX;
|
||||||
projection.TranslationY = translationY;
|
projection.TranslationY = desc.TranslationY;
|
||||||
|
|
||||||
/*
|
int bufferX = desc.Width;
|
||||||
|
int bufferY = desc.Height;
|
||||||
|
|
||||||
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit) {
|
Bitmap<ColorRGB, const 1> bitmap = .((.)bufferX, (.)bufferY);
|
||||||
scale *= geometryScale;
|
|
||||||
range /= geometryScale;
|
|
||||||
box.range = range;
|
|
||||||
box.scale = scale;
|
|
||||||
if (bounds.l < bounds.r && bounds.b < bounds.t) {
|
|
||||||
double l = bounds.l, b = bounds.b, r = bounds.r, t = bounds.t;
|
|
||||||
l -= .5*range, b -= .5*range;
|
|
||||||
r += .5*range, t += .5*range;
|
|
||||||
if (miterLimit > 0)
|
|
||||||
shape.boundMiters(l, b, r, t, .5*range, miterLimit, 1);
|
|
||||||
double w = scale*(r-l);
|
|
||||||
double h = scale*(t-b);
|
|
||||||
box.rect.w = (int) ceil(w)+1;
|
|
||||||
box.rect.h = (int) ceil(h)+1;
|
|
||||||
box.translate.x = -l+.5*(box.rect.w-w)/scale;
|
|
||||||
box.translate.y = -b+.5*(box.rect.h-h)/scale;
|
|
||||||
} else {
|
|
||||||
box.rect.w = 0, box.rect.h = 0;
|
|
||||||
box.translate = msdfgen::Vector2();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
int x = _fontSize;
|
|
||||||
int y = _fontSize;
|
|
||||||
|
|
||||||
Bitmap<ColorRGB, const 1> bitmap = .((.)x, (.)y);
|
|
||||||
|
|
||||||
MSDFGeneratorConfig config = .();
|
MSDFGeneratorConfig config = .();
|
||||||
|
|
||||||
msdfgen.GenerateMSDF(*(Bitmap<float, const 3>*)&bitmap, shape, projection, _range, config);
|
msdfgen.GenerateMSDF(*(Bitmap<float, const 3>*)&bitmap, shape, projection, _range, config);
|
||||||
|
|
||||||
int8[] pixels = new int8[x * y * 4];
|
int8[] pixels = new int8[desc.Width * desc.Height * 4];
|
||||||
|
|
||||||
int8 ToInt8(float f) => (.)Math.Clamp(127f * f, int8.MinValue, int8.MaxValue);
|
int8 ToInt8(float f) => (.)Math.Clamp(127f * f, int8.MinValue, int8.MaxValue);
|
||||||
|
|
||||||
for(int iy = 0; iy < y; iy++)
|
for(int y = 0; y < desc.Height; y++)
|
||||||
for(int ix = 0; ix < x; ix++)
|
for(int x = 0; x < desc.Width; x++)
|
||||||
{
|
{
|
||||||
ColorRGB pixel = bitmap.Pixels[iy * x + ix];
|
ColorRGB pixel = bitmap.Pixels[(y) * bufferX + x];
|
||||||
|
|
||||||
int index = ((y - iy - 1) * x + ix) * 4;
|
int index = ((desc.Height - y - 1) * desc.Width + x) * 4;
|
||||||
|
|
||||||
pixels[index + 0] = ToInt8(pixel.Red);
|
pixels[index + 0] = ToInt8(pixel.Red);
|
||||||
pixels[index + 1] = ToInt8(pixel.Green);
|
pixels[index + 1] = ToInt8(pixel.Green);
|
||||||
@@ -467,7 +471,7 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
}
|
}
|
||||||
|
|
||||||
_atlas.SetData<Color>((Color*)pixels.Ptr, (.)desc.MapCoord.X, (.)desc.MapCoord.Y,
|
_atlas.SetData<Color>((Color*)pixels.Ptr, (.)desc.MapCoord.X, (.)desc.MapCoord.Y,
|
||||||
(.)desc.SizeX, (.)desc.SizeY, (.)desc.MapCoord.Z);
|
(.)desc.Width, (.)desc.Height, (.)desc.MapCoord.Z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,38 +21,58 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Effect _msdfEffect;
|
||||||
|
|
||||||
static this()
|
static this()
|
||||||
{
|
{
|
||||||
FontRenderer.InitLibrary();
|
FontRenderer.InitLibrary();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Init(EffectLibrary effectLibrary)
|
||||||
|
{
|
||||||
|
if(effectLibrary.Exists("msdfShader"))
|
||||||
|
_msdfEffect = effectLibrary.Get("msdfShader");
|
||||||
|
else
|
||||||
|
_msdfEffect = effectLibrary.Load("content\\Shaders\\msdfShader.hlsl");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DeInit()
|
||||||
|
{
|
||||||
|
_msdfEffect?.ReleaseRef();
|
||||||
|
_msdfEffect = null;
|
||||||
|
}
|
||||||
|
|
||||||
/** @brief Draws a given text using a specified font stack and renderer.
|
/** @brief Draws a given text using a specified font stack and renderer.
|
||||||
* @param renderer The 2D renderer that will be used to draw the text.
|
* @param renderer The 2D renderer that will be used to draw the text.
|
||||||
* @param font the Fontstack that will be used to draw the text.
|
* @param font the Fontstack that will be used to draw the text.
|
||||||
* @param x The horizontal position of the text (i.e. distance between left side of viewport and the left side of the left-most character, well not really but close enough).
|
* @param x The horizontal position of the text (i.e. distance between left side of viewport and the left side of the left-most character, well not really but close enough).
|
||||||
* @param y The vertical position of the text (i.e. distance between top side of viewport and the top side of the top-most character, well not really but close enough).
|
* @param y The vertical position of the text (i.e. distance between top side of viewport and the top side of the top-most character, well not really but close enough).
|
||||||
|
* @param fontSize The font size in pixels. Note: due to technical reasons the actual size might deviate from the specified size.
|
||||||
* @param fontColor The (default) color used for glyphs that don't have color (e.g. letters or emojies if the font doesn't use bitmaps for them).
|
* @param fontColor The (default) color used for glyphs that don't have color (e.g. letters or emojies if the font doesn't use bitmaps for them).
|
||||||
* @param bitmapColor The (default) color used for glyphs that are bitmaps (e.g. emojies, if the font provies them as bitmap).
|
* @param bitmapColor The (default) color used for glyphs that are bitmaps (e.g. emojies, if the font provies them as bitmap).
|
||||||
* @param fontSize The font size in pixels. If set to 0 the default size of the font will be used. Note: due to technical reasons the actual size might deviate from the specified size.
|
|
||||||
* @param lineGapOffset Can be used to manually increase or decrease the gap between lines.
|
* @param lineGapOffset Can be used to manually increase or decrease the gap between lines.
|
||||||
*/
|
*/
|
||||||
public static void DrawText(Renderer2D renderer, Font font, String text, float x, float y, Color fontColor = .White, Color bitmapColor = .White, float fontSize = 0, float lineGapOffset = 0)
|
public static void DrawText(Renderer2D renderer, Font font, String text, float x, float y, float fontSize, Color fontColor = .White, Color bitmapColor = .White, float lineGapOffset = 0)
|
||||||
{
|
{
|
||||||
if(text.IsWhiteSpace)
|
if(text.IsWhiteSpace)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
float scale = 1.0f;
|
renderer.End();
|
||||||
|
var lastEffect = renderer.[Friend]_currentEffect;
|
||||||
if(fontSize != 0)
|
renderer.[Friend]_currentEffect = _msdfEffect..AddRef();
|
||||||
{
|
|
||||||
scale = (float)fontSize / (float)font._fontSize;
|
float scale = (float)fontSize / (float)font._fontSize;
|
||||||
}
|
|
||||||
|
_msdfEffect.Variables["screenPixelRange"].SetData(scale * 4.0f);
|
||||||
|
|
||||||
// Space between two baselines
|
// Space between two baselines
|
||||||
float linespace = (((font._face.size.metrics.ascender - font._face.size.metrics.descender) / 64) + lineGapOffset) * scale;
|
float linespace = (((font._face.size.metrics.ascender - font._face.size.metrics.descender) / 64) + lineGapOffset) * scale;
|
||||||
|
|
||||||
// The line we are writing on
|
// The line we are writing on
|
||||||
float baseline = y + linespace;
|
float baseline = y + linespace;
|
||||||
|
|
||||||
|
//renderer.Draw(null, x, baseline, 10000, 1, .Blue);
|
||||||
|
|
||||||
// Position of the next character on the line
|
// Position of the next character on the line
|
||||||
float penPosition = x;
|
float penPosition = x;
|
||||||
|
|
||||||
@@ -68,7 +88,8 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
// smallest possible increase a float can represent.
|
// smallest possible increase a float can represent.
|
||||||
// Note: This might do funky stuff when objects are very close to each other. But realistically whe have to do
|
// Note: This might do funky stuff when objects are very close to each other. But realistically whe have to do
|
||||||
// about 8 million (2^23) increments to reach a depth of 1 so I think it's safe enough.
|
// about 8 million (2^23) increments to reach a depth of 1 so I think it's safe enough.
|
||||||
int32 depthInt = 0;
|
float f = 1.0f;
|
||||||
|
int32 depthInt = *(int32*)&f;
|
||||||
|
|
||||||
// enumerate through the unicode codepoints
|
// enumerate through the unicode codepoints
|
||||||
for(char32 char in text.DecodedChars)
|
for(char32 char in text.DecodedChars)
|
||||||
@@ -91,6 +112,8 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
|
|
||||||
movedLines = 0;
|
movedLines = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//renderer.Draw(null, penPosition, baseline - fontSize, 1, fontSize, .Lime);
|
||||||
|
|
||||||
// Get glyph from Font
|
// Get glyph from Font
|
||||||
var glyphDesc = font.GetGlyph(char);
|
var glyphDesc = font.GetGlyph(char);
|
||||||
@@ -98,7 +121,11 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
// This never happens
|
// This never happens
|
||||||
Debug.Assert(glyphDesc != null);
|
Debug.Assert(glyphDesc != null);
|
||||||
|
|
||||||
Texture2D atlas = glyphDesc.Font._atlas;
|
// The glyph might belong to a fallback font
|
||||||
|
var glyphFont = glyphDesc.Font;
|
||||||
|
float glyphFontScale = (float)fontSize / glyphFont._fontSize;
|
||||||
|
|
||||||
|
Texture2D atlas = glyphFont._atlas;
|
||||||
|
|
||||||
// Add reference to atlas in case it is recreated during rendering
|
// Add reference to atlas in case it is recreated during rendering
|
||||||
if(!atlasses.Contains(atlas))
|
if(!atlasses.Contains(atlas))
|
||||||
@@ -108,26 +135,21 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
|
|
||||||
Vector2 atlasSize = .(atlas.Width, atlas.Height);
|
Vector2 atlasSize = .(atlas.Width, atlas.Height);
|
||||||
|
|
||||||
float geoToTex = (float)glyphDesc.Font.[Friend]_geometryScaler;
|
float adjustToPenX = glyphDesc.AdjustToPen;
|
||||||
|
adjustToPenX *= glyphFontScale;
|
||||||
|
|
||||||
float adjustToPenX = - 4.0f / geoToTex + (glyphDesc.Metrics.horiBearingX / 64);
|
float adjustToBaseline = glyphDesc.AdjustToBaseLine;
|
||||||
adjustToPenX *= (float)fontSize / glyphDesc.Font._fontSize;
|
adjustToBaseline *= glyphFontScale;
|
||||||
|
|
||||||
float adjustToBaseline = glyphDesc.Font._fontSize - 4.0f / geoToTex -
|
|
||||||
(glyphDesc.Metrics.height / 64) * geoToTex +
|
|
||||||
(glyphDesc.Metrics.horiBearingY / 64) * geoToTex;
|
|
||||||
|
|
||||||
adjustToBaseline *= (float)fontSize / glyphDesc.Font._fontSize;
|
|
||||||
|
|
||||||
// Rectangle on the screen
|
// Rectangle on the screen
|
||||||
Vector4 viewportRect = .(
|
Vector4 viewportRect = .(
|
||||||
penPosition + adjustToPenX,
|
penPosition + adjustToPenX,
|
||||||
baseline - adjustToBaseline,
|
baseline + adjustToBaseline,
|
||||||
glyphDesc.SizeX * scale,
|
glyphDesc.Width * glyphFontScale,
|
||||||
glyphDesc.SizeY * scale);
|
glyphDesc.Height * glyphFontScale);
|
||||||
|
|
||||||
// Rectangle on the font atlas
|
// Rectangle on the font atlas
|
||||||
Vector4 texRect = .(glyphDesc.MapCoord.X, glyphDesc.MapCoord.Y, glyphDesc.SizeX, glyphDesc.SizeY);
|
Vector4 texRect = .(glyphDesc.MapCoord.X, glyphDesc.MapCoord.Y, glyphDesc.Width, glyphDesc.Height);
|
||||||
|
|
||||||
Color glyphColor = glyphDesc.IsBitmap ? bitmapColor : fontColor;
|
Color glyphColor = glyphDesc.IsBitmap ? bitmapColor : fontColor;
|
||||||
|
|
||||||
@@ -135,13 +157,16 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
|
|
||||||
renderer.Draw(atlas, viewportRect.X, viewportRect.Y, viewportRect.Z, viewportRect.W, glyphColor, *(float*)(&depthInt), texRect);
|
renderer.Draw(atlas, viewportRect.X, viewportRect.Y, viewportRect.Z, viewportRect.W, glyphColor, *(float*)(&depthInt), texRect);
|
||||||
|
|
||||||
penPosition += (glyphDesc.Metrics.horiAdvance / 64) * scale;
|
penPosition += glyphDesc.Advance * glyphFontScale;
|
||||||
|
|
||||||
// Increase depth for the next glyph
|
// Increase depth for the next glyph
|
||||||
depthInt++;
|
depthInt--;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer.End();
|
renderer.End();
|
||||||
|
|
||||||
|
_msdfEffect.ReleaseRef();
|
||||||
|
renderer.[Friend]_currentEffect = lastEffect;
|
||||||
|
|
||||||
// release all atlas textures
|
// release all atlas textures
|
||||||
for(int i < atlasses.Count)
|
for(int i < atlasses.Count)
|
||||||
|
|||||||
Vendored
+1
-1
Submodule GlitchyEngine/vendor/msdfgen updated: 87cdeae2bc...634a8ee221
@@ -42,8 +42,7 @@ float4 PS(PS_Input input) : SV_Target0
|
|||||||
float sd = median(msd.r, msd.g, msd.b);
|
float sd = median(msd.r, msd.g, msd.b);
|
||||||
float screenPxDistance = screenPixelRange * (sd - 0.5);
|
float screenPxDistance = screenPixelRange * (sd - 0.5);
|
||||||
float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
|
float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);
|
||||||
//color = mix(bgColor, fgColor, opacity);
|
//float4 color = lerp(float4(1, 0, 0, 1), input.Color, opacity);
|
||||||
//float4 color = lerp(float4(0, 0, 0, 0), input.Color, opacity);
|
|
||||||
//return color;
|
//return color;
|
||||||
|
|
||||||
return float4(input.Color.rgb, opacity * input.Color.a);
|
return float4(input.Color.rgb, opacity * input.Color.a);
|
||||||
|
|||||||
@@ -64,8 +64,6 @@ namespace Sandbox
|
|||||||
|
|
||||||
String testText ~ delete _;
|
String testText ~ delete _;
|
||||||
|
|
||||||
Effect _msdfEffect ~ _.ReleaseRef();
|
|
||||||
|
|
||||||
TextureViewer _textureViewer ~ delete _;
|
TextureViewer _textureViewer ~ delete _;
|
||||||
|
|
||||||
[AllowAppend]
|
[AllowAppend]
|
||||||
@@ -137,9 +135,14 @@ namespace Sandbox
|
|||||||
// Load test text
|
// Load test text
|
||||||
File.ReadAllText("test.txt", testText = new String(), true);
|
File.ReadAllText("test.txt", testText = new String(), true);
|
||||||
|
|
||||||
_msdfEffect = _effectLibrary.Load("content\\Shaders\\msdfShader.hlsl");
|
|
||||||
|
|
||||||
_textureViewer = new TextureViewer(_context, Renderer2D, _effectLibrary);
|
_textureViewer = new TextureViewer(_context, Renderer2D, _effectLibrary);
|
||||||
|
|
||||||
|
FontRenderer.Init(_effectLibrary);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ~this()
|
||||||
|
{
|
||||||
|
FontRenderer.DeInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
Font fonty ~ _.ReleaseRef();
|
Font fonty ~ _.ReleaseRef();
|
||||||
@@ -222,11 +225,6 @@ namespace Sandbox
|
|||||||
|
|
||||||
public override void Update(GameTime gameTime)
|
public override void Update(GameTime gameTime)
|
||||||
{
|
{
|
||||||
if(Input.IsKeyPressing(Key.P))
|
|
||||||
{
|
|
||||||
fonty.RedrawAtlas();
|
|
||||||
}
|
|
||||||
|
|
||||||
var cameraTransform = _world.GetComponent<TransformComponent>(_cameraEntity);
|
var cameraTransform = _world.GetComponent<TransformComponent>(_cameraEntity);
|
||||||
|
|
||||||
if(Application.Get().Window.IsActive)
|
if(Application.Get().Window.IsActive)
|
||||||
@@ -328,19 +326,12 @@ namespace Sandbox
|
|||||||
_alphaBlendState.Bind();
|
_alphaBlendState.Bind();
|
||||||
Renderer2D.Begin(.FrontToBack, .(_context.SwapChain.Width, _context.SwapChain.Height));
|
Renderer2D.Begin(.FrontToBack, .(_context.SwapChain.Width, _context.SwapChain.Height));
|
||||||
|
|
||||||
//Renderer2D.Draw(null, 0, 0, fonty.[Friend]_atlas.Width * 10, fonty.[Friend]_atlas.Height * 10, .Red, 5);
|
|
||||||
|
|
||||||
// Hallo Welt, wie geht es dir? 😂😂😂\nMir geht es gut, danke der Nachfrage. Wie geht es dir?\nMir geht es hervorragend und besser noch: meine Engine kann endlich Text rendern!💕❤\n und es scheint ganz in ordnung zu sein.
|
|
||||||
//FontRenderer.DrawText(Renderer2D, fonty, testText, 0, 0, .White, .White, 32);
|
|
||||||
|
|
||||||
Renderer2D.End();
|
Renderer2D.End();
|
||||||
|
|
||||||
_msdfEffect.Variables["screenPixelRange"].SetData(size / 64f * 4.0f);
|
|
||||||
|
|
||||||
_alphaBlendState.Bind();
|
_alphaBlendState.Bind();
|
||||||
Renderer2D.Begin(.FrontToBack, .(_context.SwapChain.Width, _context.SwapChain.Height), 100, _msdfEffect);
|
Renderer2D.Begin(.FrontToBack, .(_context.SwapChain.Width, _context.SwapChain.Height), 100);
|
||||||
|
|
||||||
FontRenderer.DrawText(Renderer2D, fonty, "Hallo", 0, 0, .White, .White, 256);
|
FontRenderer.DrawText(Renderer2D, fonty, "Hallo! gjy", 0, 0, 512, .White, .White);
|
||||||
|
|
||||||
Renderer2D.End();
|
Renderer2D.End();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user