From ad77fec3a2d977c3fe7107be5a894f2c38454058 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 3 Jul 2020 18:07:55 -0500 Subject: [PATCH] Bitmap font system works. --- .../Graphics/OpenGL/GLContext.cs | 6 +- .../Graphics/Render/MeshBatch.cs | 15 +- .../Graphics/Render/RectangleMesh.cs | 20 +- .../Graphics/Text/BitmapFont.cs | 427 +++++++++--------- .../Graphics/Textures/ITexture.cs | 1 + .../Graphics/Textures/Texture.cs | 2 + .../Resources/default.frag | 9 +- .../Utilities/Playground/MainState.cs | 10 +- 8 files changed, 268 insertions(+), 222 deletions(-) diff --git a/src/SlatedGameToolkit.Framework/Graphics/OpenGL/GLContext.cs b/src/SlatedGameToolkit.Framework/Graphics/OpenGL/GLContext.cs index de571a3..a4f2ebd 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/OpenGL/GLContext.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/OpenGL/GLContext.cs @@ -106,6 +106,7 @@ namespace SlatedGameToolkit.Framework.Graphics.OpenGL public void TexParameteri(TextureTarget target, TextureParameterName pname, int param) { glTexParameteri.Invoke(target, pname, param); + DetectGLError(); } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] @@ -382,7 +383,9 @@ namespace SlatedGameToolkit.Framework.Graphics.OpenGL public IntPtr GetString(StringName name) { - return glGetString.Invoke(name); + IntPtr res = glGetString.Invoke(name); + DetectGLError(); + return res; } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] @@ -1459,6 +1462,7 @@ namespace SlatedGameToolkit.Framework.Graphics.OpenGL public void GetUniformfv(uint program, int location, out float parameters) { glGetUniformfv.Invoke(program, location, out parameters); + DetectGLError(); } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] diff --git a/src/SlatedGameToolkit.Framework/Graphics/Render/MeshBatch.cs b/src/SlatedGameToolkit.Framework/Graphics/Render/MeshBatch.cs index aac0cae..cd3878c 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/Render/MeshBatch.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/Render/MeshBatch.cs @@ -16,7 +16,7 @@ namespace SlatedGameToolkit.Framework.Graphics.Render private bool disposed; private float batchDelta; public GLContext GLContext {get; private set; } - private int projALoc, viewALoc, modelALoc, texturedALoc, singleChanneledALoc; + private int projALoc, viewALoc, modelALoc, texturedALoc, singleChanneledALoc, flippedALoc; private Camera camera; private RenderProgram renderProgram; private ITexture texture; @@ -56,6 +56,7 @@ namespace SlatedGameToolkit.Framework.Graphics.Render projALoc = GLContext.GetUniformLocation(renderProgram.Handle, "projection"); texturedALoc = GLContext.GetUniformLocation(renderProgram.Handle, "textured"); singleChanneledALoc = GLContext.GetUniformLocation(renderProgram.Handle, "singleChanneled"); + flippedALoc = GLContext.GetUniformLocation(renderProgram.Handle, "flipped"); vertexBuffers.defineVertexAttributes(definitions: definitions); GLContext.UniformMatrix4fv(projALoc, 1, false, camera.ProjectionMatrix.ToColumnMajorArray()); @@ -91,9 +92,10 @@ namespace SlatedGameToolkit.Framework.Graphics.Render Flush(); this.texture = mesh.Texture; GLContext.Uniform1i(texturedALoc, texture == null ? 0 : 1); - if (texture != null) { - GLContext.Uniform1i(singleChanneledALoc, texture.SingleChanneled ? 1 : 0); - } + } + if (texture != null) { + GLContext.Uniform1i(singleChanneledALoc, texture.SingleChanneled ? 1 : 0); + GLContext.Uniform1i(flippedALoc, texture.Flipped ? 1 : 0); } ValueTuple[] vertices = mesh.Vertices; uint[] indices = mesh.Elements; @@ -131,14 +133,15 @@ namespace SlatedGameToolkit.Framework.Graphics.Render lengths[offsetIndex] = elementsCount; offsetIndex++; } - + public virtual void End() { if (!Batching) throw new InvalidOperationException("This batch was never started."); this.Batching = false; Flush(); } - protected virtual void Flush() { + public virtual void Flush() { + if (offsetIndex == 0) return; if (texture != null) { GLContext.BindTexture(TextureTarget.Texture2D, texture.Handle); } diff --git a/src/SlatedGameToolkit.Framework/Graphics/Render/RectangleMesh.cs b/src/SlatedGameToolkit.Framework/Graphics/Render/RectangleMesh.cs index f71fdd0..8bb2f07 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/Render/RectangleMesh.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/Render/RectangleMesh.cs @@ -5,14 +5,14 @@ using SlatedGameToolkit.Framework.Graphics.Textures; namespace SlatedGameToolkit.Framework.Graphics.Render { - public class RectangleMesh : IMesh + public struct RectangleMesh : IMesh { - private Matrix4x4 matRot = Matrix4x4.Identity; + private Matrix4x4 matRot; private bool changed; private Vector3 rotation; private Vector2 origin, dimensions; - private ValueTuple[] vertices = new ValueTuple[4]; - private uint[] indices = new uint[] {0, 1, 3, 1, 2, 3}; + private ValueTuple[] vertices; + private uint[] indices; public ValueTuple[] Vertices { @@ -113,9 +113,15 @@ namespace SlatedGameToolkit.Framework.Graphics.Render public RectangleMesh(RectangleF textureRegion, ITexture texture, Color color) { + this.changed = true; + this.rotation = Vector3.Zero; + this.origin = Vector2.Zero; + this.dimensions = Vector2.Zero; this.Texture = texture; this.Color = color; - + this.indices = new uint[] {0, 1, 3, 1, 2, 3}; + vertices = new ValueTuple[4]; + this.vertices[0].Item2.X = textureRegion.X; this.vertices[0].Item2.Y = textureRegion.Y; @@ -127,6 +133,10 @@ namespace SlatedGameToolkit.Framework.Graphics.Render this.vertices[3].Item2.X = textureRegion.X; this.vertices[3].Item2.Y = textureRegion.Y + textureRegion.Height; + + this.matRot = Matrix4x4.Identity; + + } public RectangleMesh(RectangleF meshBounds, RectangleF textureRegion, ITexture texture, Color color) : this(textureRegion, texture, color) { diff --git a/src/SlatedGameToolkit.Framework/Graphics/Text/BitmapFont.cs b/src/SlatedGameToolkit.Framework/Graphics/Text/BitmapFont.cs index 0d474a7..094efe2 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/Text/BitmapFont.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/Text/BitmapFont.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Drawing; using System.IO; using System.Numerics; -using System.Runtime.InteropServices; +using SlatedGameToolkit.Framework.Exceptions; using SlatedGameToolkit.Framework.Graphics.OpenGL; using SlatedGameToolkit.Framework.Graphics.Render; using SlatedGameToolkit.Framework.Graphics.Textures; @@ -16,130 +16,245 @@ namespace SlatedGameToolkit.Framework.Graphics.Text { public class BitmapFont : IDisposable { - private StbTrueType.stbtt_fontinfo info; - private byte[] fontData; - private bool disposed; + private GLContext context; + LRUCache glyphIndices; + FontTexture[] textures; + StbTrueType.stbtt_fontinfo info; + private int spaceAdvance; private float scale; - private int bitmapLength; - private int pixelHeight; - public int PixelHeight { - get { + private float pixelHeight; + public float PixelHeight + { + get + { return pixelHeight; } - - set { - pixelHeight = value; - scale = StbTrueType.stbtt_ScaleForPixelHeight(info, value); - } - } - - private float kerning; - public float Kerning { - get { - return kerning; - } - - set { - kerning = value; - } - } - private Dictionary<(float, char), int> bitmapLocations; - private LRUCache glyphIndexCache; - private FontBitmap[] textureBuffers; - private int drawingTo = 0; - - GLContext glContext; - - /// - /// Creates a font with the given data. - /// - /// The byte array containing all ttf data. - /// The OpenGL context to use to associate with. - /// The size of the caches. - /// The length of one side of a bitmap, therefore, the total would be this squared. - /// The number of backing textures to use. - /// An initial set of characters to load. - public unsafe BitmapFont(byte[] fontData, GLContext glContext = null, int cacheSize = 1024, int bitmapLength = 512, int numberOfTextures = 2, params char[] initialCharacters) { - this.glContext = glContext ?? WindowContextsManager.CurrentGL; - if (bitmapLength < 0) throw new ArgumentOutOfRangeException("bitmapLength"); - if (fontData == null || fontData.Length <= 0) throw new ArgumentException("fontData must be an array of bytes representing a TTF file."); - glyphIndexCache = new LRUCache(cacheSize); - bitmapLocations = new Dictionary<(float, char), int>(); - this.fontData = fontData; - - textureBuffers = new FontBitmap[numberOfTextures]; - this.bitmapLength = bitmapLength; - for (int i = 0; i < textureBuffers.Length; i++) + set { - textureBuffers[i] = new FontBitmap(this.glContext, bitmapLength); + this.scale = StbTrueType.stbtt_ScaleForPixelHeight(info, value); + this.pixelHeight = value; } + } + private Dictionary<(char, float), int> glyphTexLocations = new Dictionary<(char, float), int>(); + private LRUCache metricsCache; + private int drawingTo; + private bool disposed; + public unsafe BitmapFont(byte[] data, GLContext glContext = null, int cacheSize = 1024, int textures = 2, uint textureSizes = 512) { info = new StbTrueType.stbtt_fontinfo(); - - fixed (byte* data = &fontData[0]) { - StbTrueType.stbtt_InitFont(info, data, 0); + fixed(byte* dataPtr = &data[0]) { + StbTrueType.stbtt_InitFont(info, dataPtr, 0); } + this.glyphIndices = new LRUCache(cacheSize); + this.metricsCache = new LRUCache(cacheSize); + this.textures = new FontTexture[textures]; + + this.context = glContext ?? WindowContextsManager.CurrentGL; + for (int i = 0; i < this.textures.Length; i++) { + this.textures[i] = new FontTexture(info, context, glyphIndices, textureSizes); + } PixelHeight = 64; + int spaceAdvance, leftSideBearing; + StbTrueType.stbtt_GetCodepointHMetrics(info, ' ', &spaceAdvance, &leftSideBearing); + this.spaceAdvance = spaceAdvance; } - public unsafe BitmapFont(string path, GLContext glContext = null, int cacheSize = 1024, int bitmapLength = 512, int numberOfTextures = 2, params char[] initialCharacters) : - this(File.ReadAllBytes(path), glContext, cacheSize, bitmapLength, numberOfTextures, initialCharacters) - { + public BitmapFont(string path, GLContext glContext = null, int cacheSize = 1024, int textures = 2, uint textureSizes = 512) : this(File.ReadAllBytes(path), glContext, cacheSize, textures, textureSizes) { } - /// - /// Loads the requested character. - /// - /// The character to load. - /// True if already loaded or successfully loaded, false if could not load. - public unsafe bool LoadGlyph(char character) { - if (!(bitmapLocations.ContainsKey((scale, character)) && textureBuffers[bitmapLocations[(scale, character)]].Contains(scale, character))) { - bitmapLocations.Remove((scale, character)); - int glyphIndex = glyphIndexCache.ComputeIfNonExistent(character, (c) => StbTrueType.stbtt_FindGlyphIndex(info, c)); - int x0 = 0, y0 = 0, x1 = 0, y1 = 0; + public ITexture GetTextureBacking(int index) { + return textures[index]; + } + + public unsafe void Draw(MeshBatch batch, float x, float baseLine, string text, Color color) { + int textureChanges = 0; + + float currentPoint = x; + char[] chars = text.ToCharArray(); + for (int i = 0; i < chars.Length; i++) { + char c = chars[i]; + int glyphIndex = glyphIndices.ComputeIfNonExistent(c, (p) => StbTrueType.stbtt_FindGlyphIndex(info, p)); + CharacterMetrics metrics = metricsCache.ComputeIfNonExistent(c, (p) => new CharacterMetrics(glyphIndex, info)); + int viewWidth, viewHeight, vX, vY; + context.GetViewport(out vX, out vY, out viewWidth, out viewHeight); + + if (c != ' ') { + //Check if glyph is loaded, if not, load it. + if (!glyphTexLocations.ContainsKey((c, scale)) || !textures[glyphTexLocations[(c, scale)]].ContainsChar(c, scale)) { + glyphTexLocations.Remove((c, scale)); + if (!textures[drawingTo].Upload(scale, c)) { + drawingTo++; + if (drawingTo >= textures.Length) drawingTo = 0; + FontTexture fontTexture = textures[drawingTo]; + fontTexture.Clear(); + fontTexture.Upload(scale, c); + textureChanges++; + if (textureChanges > textures.Length) throw new FrameworkUsageException(string.Format("String \"{0}\" takes up too much texture space! Consider increasing decreasing font size, or increasing texture lengths, or number of backing textures. Attempted to swap {1} times within draw call.", text, textureChanges)); + } + glyphTexLocations.Add((c, scale), drawingTo); + } + + FontTexture texture = textures[glyphTexLocations[(c, scale)]]; + RectangleF texBounds = texture.GetGlyphBoundaries(c, scale); + RectangleF transformedTexBounds = texBounds.MultiplyBy(1f/texture.Length); + transformedTexBounds.Y += transformedTexBounds.Height; + transformedTexBounds.Height *= -1; + RectangleF glyphBounds = new RectangleF(currentPoint, baseLine, texBounds.Width / viewWidth, texBounds.Height / viewHeight); + glyphBounds.X += (metrics.leftSideBearing * scale) / viewWidth; + glyphBounds.Y += (metrics.vertOffset * scale) / viewHeight; + + batch.Draw(new RectangleMesh(glyphBounds, transformedTexBounds, texture, color)); + currentPoint += (metrics.advanceWidth * scale) / viewWidth; + if (i + 1 < chars.Length) { + int nextGlyph = glyphIndices.ComputeIfNonExistent(chars[i + 1], (p) => StbTrueType.stbtt_FindGlyphIndex(info, p)); + currentPoint += (StbTrueType.stbtt_GetGlyphKernAdvance(info, glyphIndex, nextGlyph) * scale) / viewWidth; + } + } else { + currentPoint += (scale * spaceAdvance) / viewWidth; + } + } + } + + private struct CharacterMetrics { + public readonly int vertOffset; + public readonly int leftSideBearing; + public readonly int advanceWidth; + + public unsafe CharacterMetrics(int glyphIndex, StbTrueType.stbtt_fontinfo info) { + int x0, y0, leftSideBearing, advanceWidth; + StbTrueType.stbtt_GetGlyphHMetrics(info, glyphIndex, &advanceWidth, &leftSideBearing); + StbTrueType.stbtt_GetGlyphBox(info, glyphIndex, &x0, &y0, null, null); + this.vertOffset = y0; + this.leftSideBearing = leftSideBearing; + this.advanceWidth = advanceWidth; + } + } + + private class FontTexture : ITexture { + private LRUCache glyphIndices; + public uint Length {get; private set;} + StbTrueType.stbtt_fontinfo info; + GLContext context; + private bool disposed; + + public bool SingleChanneled => true; + + public uint Handle {get; private set;} + + public uint Width => Length; + + public uint Height => Length; + + public bool Flipped => false; + private Dictionary<(char, float), Rectangle> glyphBounds = new Dictionary<(char, float), Rectangle>(); + private int row, column, previousRow; + + public FontTexture(StbTrueType.stbtt_fontinfo info, GLContext context, LRUCache glyphIndices, uint length) { + this.context = context; + this.Length = length; + this.info = info; + this.glyphIndices = glyphIndices; + context.DetectGLError(); + uint[] handles = new uint[1]; + context.GenTextures(handles.Length, handles); + Handle = handles[0]; + + context.BindTexture(TextureTarget.Texture2D, Handle); + context.TexImage2D(TextureTarget.Texture2D, 0, InternalFormat.Red, length, length, 0, PixelFormat.Red, PixelType.UnsignedByte, IntPtr.Zero); + context.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge); + context.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge); + context.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); + context.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); + + } + + public bool ContainsChar(char c, float scale) { + return glyphBounds.ContainsKey((c, scale)); + } + + public Rectangle GetGlyphBoundaries(char c, float scale) { + return glyphBounds[(c, scale)]; + } + + public unsafe bool Upload(float scale, char c) { + int x0, y0, x1, y1; + int glyphIndex = glyphIndices.ComputeIfNonExistent(c, (p) => StbTrueType.stbtt_FindGlyphIndex(info, p)); StbTrueType.stbtt_GetGlyphBitmapBox(info, glyphIndex, scale, scale, &x0, &y0, &x1, &y1); - if (!textureBuffers[drawingTo].CanAdd(x1 - x0, y1 - y0)) { - return false; + int width = x1 - x0, height = y1 - y0; + if (width == 0 || height == 0) throw new InternalFrameworkException("Glyph width or height was 0. Character was: " + c); + if (column + width >= Length) { + column = 0; + if (row + height + 1 >= Length) { + return false; + } else { + previousRow = row; + } } - - textureBuffers[drawingTo].Upload(character, x1 - x0, y1 - y0, scale, info, glyphIndex); - bitmapLocations.Add((scale, character), drawingTo); - } - return true; - } + if (row - height <= previousRow) { + row = previousRow + height + 1; + } + column += 1 + width; - public unsafe void Draw(MeshBatch batch, double delta, float x, float y, string characters, Color color) { - float horizontal = x; - foreach (char c in characters) + context.BindTexture(TextureTarget.Texture2D, Handle); + int alignment; + int[] alignments = new int[1]; + context.GetIntegerv(GetPName.UnpackAlignment, alignments); + alignment = alignments[0]; + if (alignment != 1) { + context.PixelStorei(PixelStoreParameter.UnpackAlignment, 1); + } + byte[] bitmap = new byte[width * height]; + fixed(byte* data = &bitmap[0]) { + StbTrueType.stbtt_MakeGlyphBitmap(info, data, width, height, width, scale, scale, glyphIndex); + context.TexSubImage2D(TextureTarget.Texture2D, 0, column - width, row - height, width, height, PixelFormat.Red, PixelType.UnsignedByte, new IntPtr(data)); + } + if (alignment != 1) { + context.PixelStorei(PixelStoreParameter.UnpackAlignment, alignment); + } + + glyphBounds.Add((c, scale), new Rectangle(column - width, row - height, width, height)); + return true; + } + + public bool CanFit(int width, int height) { + if (row + height < Length) { + return true; + } else if (column + width < Length) { + return true; + } + return false; + } + + public void Clear() { + glyphBounds.Clear(); + row = 0; + column = 0; + previousRow = 0; + } + + protected virtual void Dispose(bool disposing) { - int used = 0; - while (!LoadGlyph(c)) { - used++; - drawingTo++; - if (drawingTo > textureBuffers.Length) { - drawingTo = 0; - } - if (used >= textureBuffers.Length) { - batch.End(); - batch.Begin(Matrix4x4.Identity, delta); - used = 0; + if (!disposed) + { + if (disposing) + { } + context.DeleteTextures(1, new uint[] {Handle}); + disposed = true; } - ITexture texture = textureBuffers[bitmapLocations[(scale, c)]]; - RectangleF glyphBounds = textureBuffers[bitmapLocations[(scale, c)]].GetGlyphTextureBounds(scale, c); - int width, height, vX, vY; - glContext.GetViewport(out vX, out vY, out width, out height); - float glyphWidth = glyphBounds.Width / width; - float glyphHeight = glyphBounds.Height / height; - RectangleMesh mesh = new RectangleMesh(new RectangleF(horizontal, y, glyphWidth, glyphHeight), glyphBounds.MultiplyBy(1f/bitmapLength), texture, color); - horizontal += (glyphBounds.Width - kerning) / width; - batch.Draw(mesh); } - } - public ITexture GetActiveTextureBacking() { - return textureBuffers[drawingTo]; + ~FontTexture() + { + Dispose(disposing: false); + } + + public void Dispose() + { + Dispose(disposing: true); + System.GC.SuppressFinalize(this); + } } protected virtual void Dispose(bool disposing) @@ -148,10 +263,11 @@ namespace SlatedGameToolkit.Framework.Graphics.Text { if (disposing) { + // TODO: dispose managed state (managed objects) } - foreach (FontBitmap bitmap in textureBuffers) + foreach (FontTexture texture in this.textures) { - bitmap.Dispose(); + texture.Dispose(); } disposed = true; } @@ -167,106 +283,5 @@ namespace SlatedGameToolkit.Framework.Graphics.Text Dispose(disposing: true); GC.SuppressFinalize(this); } - private class FontBitmap : ITexture - { - private GLContext glContext; - private bool disposed; - private int bitmapLength; - private int lastRow; - private Point bitmapPosition; - private Dictionary<(float, char), RectangleF> glyphBitmaps = new Dictionary<(float, char), RectangleF>(); - public uint Handle {get; private set;} - - public uint Width {get; private set;} - - public uint Height {get; private set;} - - public bool SingleChanneled => true; - - public unsafe FontBitmap(GLContext glContext, int length) { - uint[] handles = new uint[1]; - this.glContext = glContext; - glContext.GenTextures(handles.Length, handles); - Handle = handles[0]; - glContext.BindTexture(TextureTarget.Texture2D, Handle); - glContext.TexImage2D(TextureTarget.Texture2D, 0, InternalFormat.Red, (uint)length, (uint)length, 0, PixelFormat.Red, PixelType.UnsignedByte, IntPtr.Zero); - glContext.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge); - glContext.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge); - glContext.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest); - glContext.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); - this.bitmapLength = length; - } - - public bool Contains(float scale, char character) { - return glyphBitmaps.ContainsKey((scale, character)); - } - - public RectangleF GetGlyphTextureBounds(float scale, char character) { - return glyphBitmaps[(scale, character)]; - } - - public unsafe void Upload(char character, int width, int height, float scale, StbTrueType.stbtt_fontinfo info, int glyphIndex) { - if (bitmapPosition.X + width < bitmapLength) { // Within the first row - if (bitmapPosition.Y - lastRow < height) { - bitmapPosition.Y = lastRow + height + 1; - if (bitmapPosition.Y >= bitmapLength) { - bitmapPosition = Point.Empty; - glyphBitmaps.Clear(); - } - } // the prev row + the current bitmaps height will be the maximum height. - bitmapPosition.X += width + 1; // move the x position. - glContext.BindTexture(TextureTarget.Texture2D, Handle); - int w = 0, h = 0, xoff = 0, yoff = 0; - byte* dataPtr = StbTrueType.stbtt_GetGlyphBitmap(info, scale, scale, glyphIndex, &w, &h, &xoff, &yoff); - IntPtr data = new IntPtr(dataPtr); - Rectangle drawLocation = new Rectangle(bitmapPosition.X - width, bitmapPosition.Y - height, width, height); - int prevAlignment = 0; - int[] prevAlignArr = new int[1]; - glContext.GetIntegerv(GetPName.UnpackAlignment, prevAlignArr); - prevAlignment = prevAlignArr[0]; - if (prevAlignment != 1) { - glContext.PixelStorei(PixelStoreParameter.UnpackAlignment, 1); - } - glContext.TexSubImage2D(TextureTarget.Texture2D, 0, drawLocation.X, drawLocation.Y, drawLocation.Width, drawLocation.Height, PixelFormat.Red, PixelType.UnsignedByte, data); - if (prevAlignment != 1) { - glContext.PixelStorei(PixelStoreParameter.UnpackAlignment, prevAlignment); - } - StbTrueType.stbtt_FreeBitmap(dataPtr, null); - glyphBitmaps.Add((scale, character), new Rectangle(drawLocation.X, bitmapLength - drawLocation.Y - drawLocation.Height, drawLocation.Width, drawLocation.Height)); - - } else { // Next row - lastRow = bitmapPosition.Y; // sets the previous row var to the previous row. - bitmapPosition.X = 0; - Upload(character, width, height, scale, info, glyphIndex); - } - } - - public bool CanAdd(int width, int height) { - return (bitmapPosition.X + width < bitmapLength) && (bitmapPosition.Y + height < bitmapLength); - } - - protected virtual void Dispose(bool disposing) - { - if (!disposed) - { - if (disposing) - { - } - glContext.DeleteTextures(1, new uint[] {Handle}); - disposed = true; - } - } - - ~FontBitmap() - { - Dispose(disposing: false); - } - - public void Dispose() - { - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - } } } \ No newline at end of file diff --git a/src/SlatedGameToolkit.Framework/Graphics/Textures/ITexture.cs b/src/SlatedGameToolkit.Framework/Graphics/Textures/ITexture.cs index 9d3b57d..8ad40d6 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/Textures/ITexture.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/Textures/ITexture.cs @@ -9,6 +9,7 @@ namespace SlatedGameToolkit.Framework.Graphics.Textures /// /// true for single channeled. bool SingleChanneled { get; } + bool Flipped { get; } uint Handle {get;} uint Width {get;} uint Height {get;} diff --git a/src/SlatedGameToolkit.Framework/Graphics/Textures/Texture.cs b/src/SlatedGameToolkit.Framework/Graphics/Textures/Texture.cs index b1fc453..6b6fc32 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/Textures/Texture.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/Textures/Texture.cs @@ -20,6 +20,8 @@ namespace SlatedGameToolkit.Framework.Graphics.Textures public bool SingleChanneled => false; + public bool Flipped => true; + /// /// Creates an OpenGL Texture2D in the given GL Context. /// diff --git a/src/SlatedGameToolkit.Framework/Resources/default.frag b/src/SlatedGameToolkit.Framework/Resources/default.frag index 1806fba..805d0f2 100644 --- a/src/SlatedGameToolkit.Framework/Resources/default.frag +++ b/src/SlatedGameToolkit.Framework/Resources/default.frag @@ -5,16 +5,21 @@ in vec2 texCoord; in vec4 color; uniform bool singleChanneled; +uniform bool flipped; uniform bool textured; uniform sampler2D texture0; void main() { + float yVal = texCoord.y; + if (flipped) { + yVal = 1 - texCoord.y; + } if (textured) { if (singleChanneled) { - outputColor = vec4(color.xyz, texture(texture0, vec2(texCoord.x, 1 - texCoord.y)).r); + outputColor = vec4(color.xyz, texture(texture0, vec2(texCoord.x, yVal)).r); } else { - outputColor = texture(texture0, vec2(texCoord.x, 1 - texCoord.y)) * color; + outputColor = texture(texture0, vec2(texCoord.x, yVal)) * color; } } else { outputColor = color; diff --git a/src/SlatedGameToolkit.Tools/Utilities/Playground/MainState.cs b/src/SlatedGameToolkit.Tools/Utilities/Playground/MainState.cs index 38b723f..3569439 100644 --- a/src/SlatedGameToolkit.Tools/Utilities/Playground/MainState.cs +++ b/src/SlatedGameToolkit.Tools/Utilities/Playground/MainState.cs @@ -24,6 +24,7 @@ namespace SlatedGameToolkit.Tools.Utilities.Playground private BitmapFont font; private Texture logoTexture, fillerTexture; private RectangleMesh logo, textureTester, untextured; + private string lastPressed; public WindowContext CurrentWindow { get { return window;}} @@ -78,6 +79,7 @@ namespace SlatedGameToolkit.Tools.Utilities.Playground untextured.Y = - 0.15f; font = new BitmapFont("Resources/Playground/earwig_factory_rg.ttf"); + font.PixelHeight = 128; } public void Render(double delta) @@ -86,8 +88,8 @@ namespace SlatedGameToolkit.Tools.Utilities.Playground renderer.Draw(logo); renderer.Draw(textureTester); renderer.Draw(untextured); - font.Draw(renderer, delta, 0.25f, -0.25f, "1234", Color.White); - font.Draw(renderer, delta, 0.25f, -0.35f, "abcd", Color.White); + font.Draw(renderer, 0.25f, -0.35f, "ABCDEFHIJKLMNOPQRSTUVWXYZ1234567890", Color.White); + renderer.Draw(new RectangleMesh(new RectangleF(-1, -1, 0.5f, 0.5f), new RectangleF(0, 1, 1, -1), font.GetTextureBacking(0), Color.White)); renderer.End(); } @@ -106,5 +108,9 @@ namespace SlatedGameToolkit.Tools.Utilities.Playground camera.MoveTo += new Vector2((0.25f) * (float) timeStep, 0); } } + + public void KeyPressed(SDL.SDL_Keycode keycode, bool pressed) { + if (pressed) lastPressed = SDL.SDL_GetKeyName(keycode); + } } } \ No newline at end of file