/// <summary> /// Returns the 2D Bounding box of the text /// </summary> /// <param name="stringValue"></param> /// <returns></returns> public Vector2 GetRenderBounds(string stringValue) { if (isDisposed) { throw new Byt3Exception("Use of Disposed Font"); } int scrW = GameEngine.Instance.Width; int scrH = GameEngine.Instance.Height; Vector2 pos = Vector2.Zero; //Hacked float x = pos.X; float y = pos.Y; for (int i = 0; i < stringValue.Length; i++) { if (stringValue[i] == '\n') { FaceMetrics fm = Metrics; x = pos.X; y -= fm.LineHeight / scrH; continue; } if (stringValue[i] == '\t') { float len = x - pos.X; float count = UiTextRendererComponent.TabToSpaceCount - len % UiTextRendererComponent.TabToSpaceCount; float val = count; x += val; continue; } //x-pos.x if (!TryGetCharacter(stringValue[i], out TextCharacter chr)) { TryGetCharacter('?', out chr); } x += chr.Advance / scrW; } return(new Vector2(x, y + Metrics.LineHeight / scrH / 2)); }
internal GlyphInfo LoadGlyph(char codePoint) { if (!_pages.ContainsKey(codePoint)) { // Load the glyph from the font face Glyph glyph = FontFace.GetGlyph(codePoint, FontSize); if (glyph == null) { if (!_errorChars.Contains(codePoint)) { Engine.Get.Debug.Warning($"Could not load character '{codePoint}' for font '{FontFace.FullName}'"); _errorChars.Add(codePoint); } return(GlyphInfo.Empty); } // Load the glyph into the texture Surface fontSurface = new Surface { Bits = Marshal.AllocHGlobal(glyph.RenderWidth * glyph.RenderHeight), Width = glyph.RenderWidth, Height = glyph.RenderHeight, Pitch = glyph.RenderWidth }; // Clear the memory region of the surface bits Marshal.Copy(new byte[glyph.RenderWidth * glyph.RenderHeight], 0, fontSurface.Bits, glyph.RenderWidth * glyph.RenderHeight); // Render the glyph to the surface glyph.RenderTo(fontSurface); int width = fontSurface.Width; int height = fontSurface.Height; int len = width * height; byte[] data = new byte[len]; // Copy the bits into the data bytes array Marshal.Copy(fontSurface.Bits, data, 0, len); // Free the memory of the surface bits Marshal.FreeHGlobal(fontSurface.Bits); // Create RGBA version of data bytes byte[] pixels = new byte[len * 4]; int index = 0; // Fill the RGBA pixels with the data for (int i = 0; i < len; i++) { byte c = data[i]; pixels[index++] = c; pixels[index++] = c; pixels[index++] = c; pixels[index++] = c; } FaceMetrics faceMetrics = FontFace.GetFaceMetrics(FontSize); // padding between glyphs to avoid bleeding in rotated text const int padding = 3; // While the glyph does not fit on the x-axis, keep resizing the texture in the x-axis while (Texture.Size.X <= (_nextFreeColumnPosition + glyph.RenderWidth + padding + RowOffset)) { int newSizeX = Texture.Size.X * 2; // Check if the glyph does not fit on the row if (newSizeX >= Texture.MaxTextureSize) { AddTextureRow(); // Move row to the next available _nextFreeRowPosition = RowOffset; // Reset the column to the start of the line _nextFreeColumnPosition = RowOffset; } else { // Make the texture wider ResizeTexture(newSizeX, Texture.Size.Y); } } // While the glyph does not fit on the y-axis, keep resizing the texture in the y-axis while (Texture.Size.Y <= (_nextFreeRowPosition + glyph.RenderHeight + padding)) { AddTextureRow(); } // Update texture with the glyph Texture.Update(pixels, new IntRect((int)_nextFreeColumnPosition + RowOffset, (int)_nextFreeRowPosition, glyph.RenderWidth, glyph.RenderHeight)); float sourceX = Math.Max(RowOffset, (_nextFreeColumnPosition + RowOffset) - ((float)padding / 2)); // Create the rect representing the region for this glyph on the updated texture FloatRect sourceRect = new FloatRect(sourceX, _nextFreeRowPosition, glyph.RenderWidth + padding, glyph.RenderHeight); // Move column to next available _nextFreeColumnPosition += glyph.RenderWidth + padding; // Create and add glyph info to the pages GlyphInfo glyphInfo = new GlyphInfo { Advance = glyph.HorizontalMetrics.Advance, BearingX = glyph.HorizontalMetrics.Bearing.X, BearingY = glyph.HorizontalMetrics.Bearing.Y, SourceRect = sourceRect }; _pages.Add(codePoint, glyphInfo); } return(_pages[codePoint]); }
/// <summary> /// Renders the Text in Display Text to the screen /// </summary> /// <param name="viewMat">View matrix of the camera(unused)</param> /// <param name="projMat">View matrix of the camera(unused)</param> public override void Render(Matrix4 viewMat, Matrix4 projMat) { if (!_init) { SetUpTextResources(); } int scrW = GameEngine.Instance.Width; int scrH = GameEngine.Instance.Height; //GL.Disable(EnableCap.Blend); GL.Enable(EnableCap.Blend); GL.Disable(EnableCap.DepthTest); GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.One); Program.Use(); Matrix4 trmat = Matrix4.CreateTranslation(Position.X, Position.Y, 0); Matrix4 m = trmat; GL.UniformMatrix4(Program.GetUniformLocation("transform"), false, ref m); GL.Uniform3(Program.GetUniformLocation("textColor"), 1f, 0f, 0f); GL.Uniform1(Program.GetUniformLocation("sourceTexture"), 0); GL.ActiveTexture(TextureUnit.Texture0); GL.BindVertexArray(_vao); float x = Position.X; float y = Position.Y; for (int i = 0; i < DisplayText.Length; i++) { if (DisplayText[i] == '\n') { FaceMetrics fm = FontFace.Metrics; x = Position.X; y -= fm.LineHeight / scrH * Scale.Y; continue; } if (DisplayText[i] == '\t') { float len = x - Position.X; float count = TabToSpaceCount - len % TabToSpaceCount; float val = count; x += val; continue; } //x-pos.x if (!FontFace.TryGetCharacter(DisplayText[i], out TextCharacter chr)) { FontFace.TryGetCharacter('?', out chr); } float xpos = x + chr.BearingX / scrW * Scale.X; float ypos = y - (chr.Height - chr.BearingY) / scrH * Scale.Y; float w = chr.Width / (float)scrW * Scale.X; float h = chr.Height / (float)scrH * Scale.Y; //Remove Scale And initial position(start at (x,y) = 0) //Add Translation to Make text be centered at origin(-TotalTextWidth/2,-TotalTextHeight/2) //Multiply Verts by matrix or pass it in shader float[] verts = { xpos, ypos + h, 0.0f, 1.0f, xpos, ypos, 0.0f, 0.0f, xpos + w, ypos, 1.0f, 0.0f, xpos, ypos + h, 0.0f, 1.0f, xpos + w, ypos, 1.0f, 0.0f, xpos + w, ypos + h, 1.0f, 1.0f }; if (chr.GlTexture != null) { GL.BindTexture(TextureTarget.Texture2D, chr.GlTexture.TextureId); GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo); GL.BufferSubData(BufferTarget.ArrayBuffer, IntPtr.Zero, (IntPtr)(sizeof(float) * verts.Length), verts); GL.DrawArrays(PrimitiveType.Triangles, 0, 6); } x += chr.Advance / scrW * Scale.X; } GL.BindBuffer(BufferTarget.ArrayBuffer, 0); GL.BindVertexArray(0); GL.BindTexture(TextureTarget.Texture2D, 0); GL.Enable(EnableCap.DepthTest); GL.Disable(EnableCap.Blend); }