diff --git a/Sandbox/content/Shaders/msdfShader.hlsl b/Sandbox/content/Shaders/msdfShader.hlsl new file mode 100644 index 0000000..4f2e1b3 --- /dev/null +++ b/Sandbox/content/Shaders/msdfShader.hlsl @@ -0,0 +1,54 @@ +Texture2D Texture : register(t0); +SamplerState Sampler : register(s0); + +cbuffer Constants +{ + float screenPixelRange = 2; +} + +struct VS_Input +{ + float2 Position : POSITION; + float4x4 Tranform : TRANSFORM; + float4 Color : COLOR; + float4 UVTransform : TEXCOORD; +}; + +struct PS_Input +{ + float4 Position : SV_Position; + float2 TexCoord : TEXCOORD0; + float4 Color : COLOR; +}; + +PS_Input VS(VS_Input input) +{ + PS_Input output; + + output.Position = mul(float4(input.Position, 0.0f, 1.0f), input.Tranform); + output.TexCoord = input.UVTransform.xy + input.UVTransform.zw * input.Position; + output.Color = input.Color; + + return output; +} + +float median(float r, float g, float b) { + return max(min(r, g), min(max(r, g), b)); +} + +float4 PS(PS_Input input) : SV_Target0 +{ + float3 msd = Texture.Sample(Sampler, input.TexCoord); + float sd = median(msd.r, msd.g, msd.b); + float screenPxDistance = screenPixelRange * (sd - 0.5); + float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0); + //color = mix(bgColor, fgColor, opacity); + + //float4 color = lerp(float4(0, 0, 0, 0), input.Color, opacity); + + //return color; + + return float4(input.Color.rgb, opacity * input.Color.a); +} + +#effect[VS=VS, PS=PS] \ No newline at end of file diff --git a/Sandbox/src/SandboxApp2D.bf b/Sandbox/src/SandboxApp2D.bf index 9e26d38..16d0141 100644 --- a/Sandbox/src/SandboxApp2D.bf +++ b/Sandbox/src/SandboxApp2D.bf @@ -10,6 +10,7 @@ using GlitchyEngine.Math; using GlitchyEngine.World; using GlitchyEngine.Renderer.Text; using System.IO; +using msdfgen; namespace Sandbox { @@ -63,6 +64,8 @@ namespace Sandbox String testText ~ delete _; + Effect _msdfEffect ~ _.ReleaseRef(); + [AllowAppend] public this() : base("Example") { @@ -73,6 +76,7 @@ namespace Sandbox _effectLibrary = new EffectLibrary(_context); _effectLibrary.LoadNoRefInc("content\\Shaders\\basicShader.hlsl"); + // Create rasterizer state GlitchyEngine.Renderer.RasterizerStateDescription rsDesc = .(.Solid, .Back, true); @@ -132,8 +136,184 @@ namespace Sandbox // Load test text var result = File.ReadAllText("test.txt", testText = new String(), true); //Log.EngineLogger.Assert(result) + + _msdfEffect = _effectLibrary.Load("content\\Shaders\\msdfShader.hlsl"); + + DoTheThing(fonty.[Friend]_face, 64); } + 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 bitmap = .((.)x, (.)y); + + MSDFGeneratorConfig config = .(); + + msdfgen.GenerateMSDF(*(Bitmap*)&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((.)pixels.Ptr, 0, 0, (.)x, (.)y); + //_dasTestA.SetData(pixelData.Ptr, 0, 0, (.)x, (.)y); + } + + Texture2D _dasTestA ~ _.ReleaseRef(); + Font fonty ~ _.ReleaseRef(); VertexLayout layout ~ _?.ReleaseRef(); @@ -244,7 +424,7 @@ namespace Sandbox movement *= (float)(gameTime.FrameTime.TotalSeconds); cameraTransform.Position += .(movement, 0); - Runtime.NotImplemented(); + //Runtime.NotImplemented(); //cameraTransform.Update(); } @@ -252,6 +432,8 @@ namespace Sandbox camera.Aspect = Application.Get().Window.Context.SwapChain.BackbufferViewport.Width / Application.Get().Window.Context.SwapChain.BackbufferViewport.Height; + TransformSystem.Update(_world); + RenderCommand.Clear(null, .(0.2f, 0.2f, 0.2f)); _depthTarget..Clear(1.0f, 0, .Depth).Bind(); @@ -309,16 +491,30 @@ namespace Sandbox Renderer2D.End(); */ - + _alphaBlendState.Bind(); Renderer2D.Begin(.FrontToBack, .(_context.SwapChain.Width, _context.SwapChain.Height)); - + + Renderer2D.Draw(null, 0, 0, 502, 502, .Black, 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); + //FontRenderer.DrawText(Renderer2D, fonty, testText, 0, 0, .White, .White, 32); + + Renderer2D.End(); + + Renderer2D.Begin(.FrontToBack, .(_context.SwapChain.Width, _context.SwapChain.Height), 100, _msdfEffect); + + _msdfEffect.Variables["screenPixelRange"].SetData(size / 64f * 4.0f); + + Renderer2D.Draw(_dasTestA, 1, 1, size, size); + + // TODO: SDFs in Font Atlas packen Renderer2D.End(); } + int32 size = 500; + ColorRGBA _squareColor0 = ColorRGBA.CornflowerBlue; ColorRGBA _squareColor1; @@ -333,6 +529,16 @@ namespace Sandbox private bool OnImGuiRender(ImGuiRenderEvent e) { + ImGui.Begin("SDF Test"); + + ImGui.InputInt("Size", &size); + + float f = (size / 64f * 4.0f); + + ImGui.Text($"SPR: {f}"); + + ImGui.End(); + /* return true; ImGui.Begin("Test"); @@ -348,7 +554,7 @@ namespace Sandbox //ImGui.Image(fonty.[Friend]_atlas.[Friend]nativeView, .(fonty.[Friend]_atlas.Width, fonty.[Friend]_atlas.Height), .(0.0f, 0.0f), .(1.0f, 1.0f), .(1.0f, 1.0f, 1.0f, 1.0f), .(1.0f, 0, 0, 1)); ImGui.End(); - + */ TextureViewer(); return false;