mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
Added MSDF to Font and FontRenderer
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
FileVersion = 1
|
FileVersion = 1
|
||||||
Dependencies = {GlitchLog = "*", corlib = "*", DirectX = "*", ImGui = "*", ImGuiImplWin32 = "*", ImGuiImplDX11 = "*", DirectXTK = "*", FreeType = "*", cgltf-beef = "*"}
|
Dependencies = {GlitchLog = "*", corlib = "*", DirectX = "*", ImGui = "*", ImGuiImplWin32 = "*", ImGuiImplDX11 = "*", DirectXTK = "*", FreeType = "*", cgltf-beef = "*", msdfgen-beef = "*"}
|
||||||
|
|
||||||
[Project]
|
[Project]
|
||||||
Name = "GlitchyEngine"
|
Name = "GlitchyEngine"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System;
|
|||||||
using FreeType;
|
using FreeType;
|
||||||
using GlitchyEngine.Math;
|
using GlitchyEngine.Math;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using msdfgen;
|
||||||
|
|
||||||
using internal GlitchyEngine.Renderer.Text;
|
using internal GlitchyEngine.Renderer.Text;
|
||||||
|
|
||||||
@@ -44,6 +45,11 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
GlyphDescriptor _missingGlyph;
|
GlyphDescriptor _missingGlyph;
|
||||||
|
|
||||||
private SamplerState _sampler ~ _.ReleaseRef();
|
private SamplerState _sampler ~ _.ReleaseRef();
|
||||||
|
|
||||||
|
/// How much we have to scale the geometry to fit it into our desired pixels
|
||||||
|
private double _geometryScaler;
|
||||||
|
|
||||||
|
private double _range = 4.0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets or sets the fallback Font for this Font.
|
* Gets or sets the fallback Font for this Font.
|
||||||
@@ -84,6 +90,12 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Inline]
|
||||||
|
double F26Dot6ToDouble(int32 value)
|
||||||
|
{
|
||||||
|
return (double(value) / 64.0);
|
||||||
|
}
|
||||||
|
|
||||||
public this(GraphicsContext context, String fontPath, uint32 fontSize, bool hasColor = true, char32 firstChar = '\0', uint32 charCount = 128, int32 faceIndex = 0)
|
public this(GraphicsContext context, String fontPath, uint32 fontSize, bool hasColor = true, char32 firstChar = '\0', uint32 charCount = 128, int32 faceIndex = 0)
|
||||||
{
|
{
|
||||||
_context = context..AddRef();
|
_context = context..AddRef();
|
||||||
@@ -105,6 +117,11 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
res = FreeType.Set_Pixel_Sizes(_face, 0, _fontSize);
|
res = FreeType.Set_Pixel_Sizes(_face, 0, _fontSize);
|
||||||
Log.EngineLogger.Assert(res.Success, scope $"Set_Pixel_Sizes failed({(int)res}): {res}");
|
Log.EngineLogger.Assert(res.Success, scope $"Set_Pixel_Sizes failed({(int)res}): {res}");
|
||||||
|
|
||||||
|
double unitsPerEm = F26Dot6ToDouble(_face.units_per_EM);
|
||||||
|
_geometryScaler = _fontSize / unitsPerEm;
|
||||||
|
|
||||||
|
_range = 4.0 / _geometryScaler;
|
||||||
|
|
||||||
LoadGlyphs(firstChar, charCount);
|
LoadGlyphs(firstChar, charCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,6 +188,21 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
DrawAtlas();
|
DrawAtlas();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Debug only
|
||||||
|
public void RedrawAtlas()
|
||||||
|
{
|
||||||
|
for(let (char, desc) in _glyphs)
|
||||||
|
{
|
||||||
|
desc?.IsCalculated = false;
|
||||||
|
desc?.IsRendered = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_penPos = .Zero;
|
||||||
|
_lastRowHeight = 0;
|
||||||
|
|
||||||
|
UpdateAtlas();
|
||||||
|
}
|
||||||
|
|
||||||
Int32_3 PrepareAtlas()
|
Int32_3 PrepareAtlas()
|
||||||
{
|
{
|
||||||
const uint32 maxRes = 16384; // D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION
|
const uint32 maxRes = 16384; // D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION
|
||||||
@@ -201,13 +233,20 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
|
|
||||||
desc.Metrics = _face.glyph.metrics;
|
desc.Metrics = _face.glyph.metrics;
|
||||||
|
|
||||||
int32 glyphWidth = (desc.Metrics.width / 64) + 1;
|
int32 border = 2;
|
||||||
int32 glyphHeight = (desc.Metrics.height / 64) + 1;
|
|
||||||
|
// 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
|
// when size is 0 (1 because we added 1) the glyph has no image
|
||||||
if(glyphWidth != 1 || glyphHeight != 1)
|
if(glyphWidth != border || glyphHeight != border)
|
||||||
{
|
{
|
||||||
int32 glyphRight = penLeft + glyphWidth;
|
//int32 glyphRight = penLeft + glyphWidth;
|
||||||
|
|
||||||
|
int32 glyphRight = penLeft + glyphWidth + border;
|
||||||
|
|
||||||
if(glyphRight > maxRes)
|
if(glyphRight > maxRes)
|
||||||
{
|
{
|
||||||
@@ -216,7 +255,7 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
rowHeight = 0;
|
rowHeight = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 glyphBottom = penTop + glyphHeight;
|
int32 glyphBottom = penTop + glyphHeight + border;
|
||||||
|
|
||||||
if(glyphBottom > maxRes)
|
if(glyphBottom > maxRes)
|
||||||
{
|
{
|
||||||
@@ -234,8 +273,10 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
}
|
}
|
||||||
|
|
||||||
desc.MapCoord = pen;
|
desc.MapCoord = pen;
|
||||||
desc.SizeX = glyphWidth - 1;
|
//desc.SizeX = glyphWidth - 1;
|
||||||
desc.SizeY = glyphHeight - 1;
|
//desc.SizeY = glyphHeight - 1;
|
||||||
|
desc.SizeX = (.)_fontSize;
|
||||||
|
desc.SizeY = (.)_fontSize;
|
||||||
|
|
||||||
if(desc.GlyphIndex == 0 && _missingGlyph == null)
|
if(desc.GlyphIndex == 0 && _missingGlyph == null)
|
||||||
{
|
{
|
||||||
@@ -262,6 +303,8 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
return .(atlasWidth, atlasHeight, atlasArray);
|
return .(atlasWidth, atlasHeight, atlasArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool secondTime = false;
|
||||||
|
|
||||||
void DrawAtlas()
|
void DrawAtlas()
|
||||||
{
|
{
|
||||||
Int32_3 oldAtlasSize = _atlasSize;
|
Int32_3 oldAtlasSize = _atlasSize;
|
||||||
@@ -272,7 +315,7 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
var oldAtlas = _atlas;
|
var oldAtlas = _atlas;
|
||||||
|
|
||||||
Texture2DDesc desc;
|
Texture2DDesc desc;
|
||||||
desc.Format = .B8G8R8A8_UNorm;
|
desc.Format = .R8G8B8A8_SNorm;
|
||||||
desc.MipLevels = 1;
|
desc.MipLevels = 1;
|
||||||
desc.Width = (.)_atlasSize.X;
|
desc.Width = (.)_atlasSize.X;
|
||||||
desc.Height = (.)_atlasSize.Y;
|
desc.Height = (.)_atlasSize.Y;
|
||||||
@@ -302,50 +345,129 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var loadResult = FreeType.Load_Glyph(_face, glyphIndex, .Color | .Render | .TargetNormal);
|
DoTheThing(desc);
|
||||||
if(loadResult.Error)
|
|
||||||
{
|
|
||||||
Log.EngineLogger.Error($"Failed to render glyph for '{char}'(Char Code:{(uint32)char} | Error {(int)loadResult}): {loadResult}");
|
|
||||||
}
|
|
||||||
|
|
||||||
FT_GlyphSlot glyphSlot = _face.glyph;
|
|
||||||
FT_Bitmap bitmap = glyphSlot.bitmap;
|
|
||||||
|
|
||||||
//ResourceBox glyphBox = .((.)desc.MapCoord.X, (.)desc.MapCoord.X, 0,
|
|
||||||
// (.)desc.MapCoord.X + (.)desc.SizeX, (.)desc.MapCoord.Y + (.)desc.SizeY, 1);
|
|
||||||
|
|
||||||
if(_face.glyph.format == .Bitmap)
|
|
||||||
{
|
|
||||||
if(bitmap.pixel_mode == .Gray)
|
|
||||||
{
|
|
||||||
Color[] pixels = new Color[bitmap.width * bitmap.rows];
|
|
||||||
defer delete pixels;
|
|
||||||
|
|
||||||
for(int i < pixels.Count)
|
|
||||||
{
|
|
||||||
uint8 gray = bitmap.buffer[i];
|
|
||||||
|
|
||||||
pixels[i] = .(gray, gray, gray, gray);
|
|
||||||
}
|
|
||||||
|
|
||||||
_atlas.SetData(pixels.Ptr, (.)desc.MapCoord.X, (.)desc.MapCoord.Y,
|
|
||||||
(.)desc.SizeX, (.)desc.SizeY, (.)desc.MapCoord.Z);
|
|
||||||
}
|
|
||||||
else if(bitmap.pixel_mode == .Bgra)
|
|
||||||
{
|
|
||||||
_atlas.SetData<Color>((.)bitmap.buffer, (.)desc.MapCoord.X, (.)desc.MapCoord.Y,
|
|
||||||
(.)desc.SizeX, (.)desc.SizeY, (.)desc.MapCoord.Z);
|
|
||||||
|
|
||||||
desc.IsBitmap = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Runtime.NotImplemented();
|
|
||||||
}
|
|
||||||
|
|
||||||
desc.IsRendered = true;
|
desc.IsRendered = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DoTheThing(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();
|
||||||
|
|
||||||
|
msdfgen.EdgeColoringSimple(shape, 3.0);
|
||||||
|
|
||||||
|
// prepare projection
|
||||||
|
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
|
||||||
|
double translationX;
|
||||||
|
double translationY;
|
||||||
|
|
||||||
|
if(bounds.Left < bounds.Right && bounds.Bottom < bounds.Top)
|
||||||
|
{
|
||||||
|
double l = bounds.Left;
|
||||||
|
double r = bounds.Right;
|
||||||
|
double b = bounds.Bottom;
|
||||||
|
double t = bounds.Top;
|
||||||
|
|
||||||
|
l -= 0.5 * _range;
|
||||||
|
b -= 0.5 * _range;
|
||||||
|
r += 0.5 * _range;
|
||||||
|
t += 0.5 * _range;
|
||||||
|
|
||||||
|
// TODO: miter?!
|
||||||
|
|
||||||
|
double w = _geometryScaler * (r - l);
|
||||||
|
double h = _geometryScaler * (t - b);
|
||||||
|
|
||||||
|
width = (int)Math.Ceiling(w) + 1;
|
||||||
|
height = (int)Math.Ceiling(h) + 1;
|
||||||
|
|
||||||
|
translationX = -l + 0.5 * (width - w) / _geometryScaler;
|
||||||
|
translationY = -b + 0.5 * (height - h) / _geometryScaler;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
width = 0;
|
||||||
|
height = 0;
|
||||||
|
translationX = 0;
|
||||||
|
translationY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
msdfgen.Projection projection = .();
|
||||||
|
projection.ScaleX = _geometryScaler;
|
||||||
|
projection.ScaleY = _geometryScaler;
|
||||||
|
|
||||||
|
projection.TranslationX = translationX;
|
||||||
|
projection.TranslationY = translationY;
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit) {
|
||||||
|
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 = .();
|
||||||
|
|
||||||
|
msdfgen.GenerateMSDF(*(Bitmap<float, const 3>*)&bitmap, shape, projection, _range, config);
|
||||||
|
|
||||||
|
int8[] pixels = new int8[x * y * 4];
|
||||||
|
|
||||||
|
int8 ToInt8(float f) => (.)Math.Clamp(127f * f, int8.MinValue, int8.MaxValue);
|
||||||
|
|
||||||
|
for(int iy = 0; iy < y; iy++)
|
||||||
|
for(int ix = 0; ix < x; ix++)
|
||||||
|
{
|
||||||
|
ColorRGB pixel = bitmap.Pixels[iy * x + ix];
|
||||||
|
|
||||||
|
int index = ((y - iy - 1) * x + ix) * 4;
|
||||||
|
|
||||||
|
pixels[index + 0] = ToInt8(pixel.Red);
|
||||||
|
pixels[index + 1] = ToInt8(pixel.Green);
|
||||||
|
pixels[index + 2] = ToInt8(pixel.Blue);
|
||||||
|
|
||||||
|
pixels[index + 3] = Int8.MaxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_atlas.SetData<Color>((Color*)pixels.Ptr, (.)desc.MapCoord.X, (.)desc.MapCoord.Y,
|
||||||
|
(.)desc.SizeX, (.)desc.SizeY, (.)desc.MapCoord.Z);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,15 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
|
|
||||||
List<Texture2D> atlasses = scope .();
|
List<Texture2D> atlasses = scope .();
|
||||||
|
|
||||||
|
// We render every char with a slightly greater depth, so that the chars don't cull each other.
|
||||||
|
// In order to do that we increment an integer for each glyph and use it as the depth for the next one.
|
||||||
|
// Whenn passing the depth to the renderer we treat the integer as the binary representation of a float.
|
||||||
|
// Therefore every increment will increment the mantissa of the float and is therefore equivalent to the
|
||||||
|
// 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
|
||||||
|
// about 8 million (2^23) increments to reach a depth of 1 so I think it's safe enough.
|
||||||
|
int32 depthInt = 0;
|
||||||
|
|
||||||
// enumerate through the unicode codepoints
|
// enumerate through the unicode codepoints
|
||||||
for(char32 char in text.DecodedChars)
|
for(char32 char in text.DecodedChars)
|
||||||
{
|
{
|
||||||
@@ -99,10 +108,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 = - 4.0f / geoToTex + (glyphDesc.Metrics.horiBearingX / 64);
|
||||||
|
adjustToPenX *= (float)fontSize / glyphDesc.Font._fontSize;
|
||||||
|
|
||||||
|
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 + (glyphDesc.Metrics.horiBearingX / 64) * scale,
|
penPosition + adjustToPenX,
|
||||||
baseline - (glyphDesc.Metrics.horiBearingY / 64) * scale,
|
baseline - adjustToBaseline,
|
||||||
glyphDesc.SizeX * scale,
|
glyphDesc.SizeX * scale,
|
||||||
glyphDesc.SizeY * scale);
|
glyphDesc.SizeY * scale);
|
||||||
|
|
||||||
@@ -113,9 +133,12 @@ namespace GlitchyEngine.Renderer.Text
|
|||||||
|
|
||||||
texRect /= Vector4(atlasSize, atlasSize);
|
texRect /= Vector4(atlasSize, atlasSize);
|
||||||
|
|
||||||
renderer.Draw(atlas, viewportRect.X, viewportRect.Y, viewportRect.Z, viewportRect.W, glyphColor, 0.0f, texRect);
|
renderer.Draw(atlas, viewportRect.X, viewportRect.Y, viewportRect.Z, viewportRect.W, glyphColor, *(float*)(&depthInt), texRect);
|
||||||
|
|
||||||
penPosition += (glyphDesc.Metrics.horiAdvance / 64) * scale;
|
penPosition += (glyphDesc.Metrics.horiAdvance / 64) * scale;
|
||||||
|
|
||||||
|
// Increase depth for the next glyph
|
||||||
|
depthInt++;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderer.End();
|
renderer.End();
|
||||||
|
|||||||
@@ -43,9 +43,7 @@ float4 PS(PS_Input input) : SV_Target0
|
|||||||
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);
|
//color = mix(bgColor, fgColor, opacity);
|
||||||
|
|
||||||
//float4 color = lerp(float4(0, 0, 0, 0), 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);
|
||||||
|
|||||||
+13
-199
@@ -135,186 +135,13 @@ namespace Sandbox
|
|||||||
emojis.Fallback = mathstuff..ReleaseRefNoDelete();
|
emojis.Fallback = mathstuff..ReleaseRefNoDelete();
|
||||||
|
|
||||||
// Load test text
|
// Load test text
|
||||||
var result = File.ReadAllText("test.txt", testText = new String(), true);
|
File.ReadAllText("test.txt", testText = new String(), true);
|
||||||
//Log.EngineLogger.Assert(result)
|
|
||||||
|
|
||||||
_msdfEffect = _effectLibrary.Load("content\\Shaders\\msdfShader.hlsl");
|
_msdfEffect = _effectLibrary.Load("content\\Shaders\\msdfShader.hlsl");
|
||||||
|
|
||||||
_textureViewer = new TextureViewer(_context, Renderer2D, _effectLibrary);
|
_textureViewer = new TextureViewer(_context, Renderer2D, _effectLibrary);
|
||||||
}
|
}
|
||||||
|
|
||||||
double F26Dot6ToDouble(uint16 value)
|
|
||||||
{
|
|
||||||
return (double(value) / 64.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DoTheThing(FreeType.FT_Face fontFace, double pixelSizeY)
|
|
||||||
{
|
|
||||||
double unitsPerEm = F26Dot6ToDouble(fontFace.units_per_EM);
|
|
||||||
|
|
||||||
// How much we have to scale the geometry to fit it into our desired pixels
|
|
||||||
double geometryScaler = pixelSizeY / unitsPerEm;
|
|
||||||
|
|
||||||
// prepare shape
|
|
||||||
|
|
||||||
double advance = 0;
|
|
||||||
|
|
||||||
var glyphIndex = FreeType.FreeType.Get_Char_Index(fonty.[Friend]_face, 'ß');
|
|
||||||
|
|
||||||
Shape shape;
|
|
||||||
msdfgen.LoadGlyph(out shape, &fonty.[Friend]_face, glyphIndex, out advance);
|
|
||||||
|
|
||||||
shape.Normalize();
|
|
||||||
|
|
||||||
var bounds = shape.GetBounds();
|
|
||||||
|
|
||||||
msdfgen.EdgeColoringSimple(shape, 3.0);
|
|
||||||
|
|
||||||
// prepare projection
|
|
||||||
|
|
||||||
double scale = geometryScaler;
|
|
||||||
double range = 4.0 / geometryScaler;
|
|
||||||
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
|
|
||||||
double translationX;
|
|
||||||
double translationY;
|
|
||||||
|
|
||||||
if(bounds.Left < bounds.Right && bounds.Bottom < bounds.Top)
|
|
||||||
{
|
|
||||||
double l = bounds.Left;
|
|
||||||
double r = bounds.Right;
|
|
||||||
double b = bounds.Bottom;
|
|
||||||
double t = bounds.Top;
|
|
||||||
|
|
||||||
l -= 0.5 * range;
|
|
||||||
b -= 0.5 * range;
|
|
||||||
r += 0.5 * range;
|
|
||||||
t += 0.5 * range;
|
|
||||||
|
|
||||||
// TODO: miter?!
|
|
||||||
|
|
||||||
double w = scale * (r - l);
|
|
||||||
double h = scale * (t - b);
|
|
||||||
|
|
||||||
width = (int)Math.Ceiling(w) + 1;
|
|
||||||
height = (int)Math.Ceiling(h) + 1;
|
|
||||||
|
|
||||||
translationX = -l + 0.5 * (width - w) / scale;
|
|
||||||
translationY = -b + 0.5 * (height - h) / scale;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
width = 0;
|
|
||||||
height = 0;
|
|
||||||
translationX = 0;
|
|
||||||
translationY = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
msdfgen.Projection projection = .();
|
|
||||||
projection.ScaleX = scale;
|
|
||||||
projection.ScaleY = scale;
|
|
||||||
|
|
||||||
projection.TranslationX = translationX;
|
|
||||||
projection.TranslationY = translationY;
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
void GlyphGeometry::wrapBox(double scale, double range, double miterLimit) {
|
|
||||||
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 = (int)pixelSizeY;
|
|
||||||
int y = (int)pixelSizeY;
|
|
||||||
|
|
||||||
Bitmap<ColorRGB, const 1> bitmap = .((.)x, (.)y);
|
|
||||||
|
|
||||||
MSDFGeneratorConfig config = .();
|
|
||||||
|
|
||||||
msdfgen.GenerateMSDF(*(Bitmap<float, const 3>*)&bitmap, shape, projection, range, config);
|
|
||||||
|
|
||||||
// Save png
|
|
||||||
/*
|
|
||||||
uint8[] data = new uint8[x * y * 3];
|
|
||||||
|
|
||||||
for(int i = 0; i < x * y * 3; i++)
|
|
||||||
{
|
|
||||||
float f = bitmap.Pixels[i / 3].Red / 2f + 0.5f;
|
|
||||||
float f2 = bitmap.Pixels[i / 3].Green / 2f + 0.5f;
|
|
||||||
float f3 = bitmap.Pixels[i / 3].Blue / 2f + 0.5f;
|
|
||||||
|
|
||||||
data[i++] = (uint8)Math.Clamp(256.0f * f, 0, 255);
|
|
||||||
data[i++] = (uint8)Math.Clamp(256.0f * f2, 0, 255);
|
|
||||||
data[i] = (uint8)Math.Clamp(256.0f * f3, 0, 255);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: free shape
|
|
||||||
|
|
||||||
LodePng.LodePng.EncodeFile("test.png", data.Ptr, (.)x, (.)y, .RGB, 8);
|
|
||||||
*/
|
|
||||||
Texture2DDesc desc;
|
|
||||||
desc.Width = (.)x;
|
|
||||||
desc.Height = (.)y;
|
|
||||||
desc.Format = .R8G8B8A8_SNorm;
|
|
||||||
desc.MipLevels = 1;
|
|
||||||
desc.Usage = .Immutable;
|
|
||||||
desc.CpuAccess = .None;
|
|
||||||
desc.ArraySize = 1;
|
|
||||||
|
|
||||||
_dasTestA = new Texture2D(_context, desc);
|
|
||||||
|
|
||||||
int8[] pixels = new int8[x * y * 4];
|
|
||||||
|
|
||||||
int8 ToInt8(float f) => (.)Math.Clamp(127f * f, int8.MinValue, int8.MaxValue);
|
|
||||||
|
|
||||||
//for(int i = 0; i < x * y; i++)
|
|
||||||
for(int iy = 0; iy < y; iy++)
|
|
||||||
for(int ix = 0; ix < x; ix++)
|
|
||||||
{
|
|
||||||
ColorRGB pixel = bitmap.Pixels[iy * y + ix];
|
|
||||||
|
|
||||||
float f = pixel.Red;// / 2f + 0.5f;
|
|
||||||
float f2 = pixel.Green;// / 2f + 0.5f;
|
|
||||||
float f3 = pixel.Blue;// / 2f + 0.5f;
|
|
||||||
|
|
||||||
int index = ((y - iy - 1) * y + ix) * 4;
|
|
||||||
|
|
||||||
pixels[index + 0] = ToInt8(f);
|
|
||||||
pixels[index + 1] = ToInt8(f2);
|
|
||||||
pixels[index + 2] = ToInt8(f3);
|
|
||||||
pixels[index + 3] = 0;
|
|
||||||
|
|
||||||
//pixels[(y - iy - 1) * y + ix] = Color(f, f2, f3);
|
|
||||||
}
|
|
||||||
|
|
||||||
_dasTestA.SetData<Color>((.)pixels.Ptr, 0, 0, (.)x, (.)y);
|
|
||||||
//_dasTestA.SetData<uint8>(pixelData.Ptr, 0, 0, (.)x, (.)y);
|
|
||||||
}
|
|
||||||
|
|
||||||
Texture2D _dasTestA ~ _.ReleaseRef();
|
|
||||||
|
|
||||||
Font fonty ~ _.ReleaseRef();
|
Font fonty ~ _.ReleaseRef();
|
||||||
|
|
||||||
VertexLayout layout ~ _?.ReleaseRef();
|
VertexLayout layout ~ _?.ReleaseRef();
|
||||||
@@ -395,6 +222,11 @@ 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)
|
||||||
@@ -496,20 +328,19 @@ 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, 502, 502, .Black, 5);
|
//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.
|
// 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);
|
//FontRenderer.DrawText(Renderer2D, fonty, testText, 0, 0, .White, .White, 32);
|
||||||
|
|
||||||
Renderer2D.End();
|
Renderer2D.End();
|
||||||
|
|
||||||
|
_msdfEffect.Variables["screenPixelRange"].SetData(size / 64f * 4.0f);
|
||||||
|
|
||||||
|
_alphaBlendState.Bind();
|
||||||
Renderer2D.Begin(.FrontToBack, .(_context.SwapChain.Width, _context.SwapChain.Height), 100, _msdfEffect);
|
Renderer2D.Begin(.FrontToBack, .(_context.SwapChain.Width, _context.SwapChain.Height), 100, _msdfEffect);
|
||||||
|
|
||||||
_msdfEffect.Variables["screenPixelRange"].SetData(size / 64f * 4.0f);
|
FontRenderer.DrawText(Renderer2D, fonty, "Hallo", 0, 0, .White, .White, 256);
|
||||||
|
|
||||||
Renderer2D.Draw(_dasTestA, 1, 1, size, size);
|
|
||||||
|
|
||||||
// TODO: SDFs in Font Atlas packen
|
|
||||||
|
|
||||||
Renderer2D.End();
|
Renderer2D.End();
|
||||||
}
|
}
|
||||||
@@ -570,23 +401,6 @@ namespace Sandbox
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
float zoom = 1.0f;
|
|
||||||
|
|
||||||
private void TextureViewer()
|
|
||||||
{
|
|
||||||
ImGui.Begin("Texture Viewer");
|
|
||||||
|
|
||||||
ImGui.SliderFloat("Zoom", &zoom, 0.01f, 100.0f);
|
|
||||||
|
|
||||||
ImGui.BeginChild("imageChild", default, true, .HorizontalScrollbar);
|
|
||||||
|
|
||||||
ImGui.Image(fonty.[Friend]_atlas.[Friend]nativeResourceView, .(fonty.[Friend]_atlas.Width * zoom, fonty.[Friend]_atlas.Height * zoom), .(0.0f, 0.0f), .(1.0f, 1.0f), .(1.0f, 1.0f, 1.0f, 1.0f), .(1.0f, 0, 0, 1));
|
|
||||||
|
|
||||||
ImGui.EndChild();
|
|
||||||
|
|
||||||
ImGui.End();
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool OnWindowResize(WindowResizeEvent e)
|
private bool OnWindowResize(WindowResizeEvent e)
|
||||||
{
|
{
|
||||||
_depthTarget.ReleaseRef();
|
_depthTarget.ReleaseRef();
|
||||||
|
|||||||
Reference in New Issue
Block a user