Basic font rendering working.
This commit is contained in:
parent
56eca1b0d6
commit
a881f1a086
@ -177,6 +177,7 @@ namespace SlatedGameToolkit.Framework {
|
|||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Logger.Fatal(e.ToString());
|
Logger.Fatal(e.ToString());
|
||||||
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
stopped = true;
|
stopped = true;
|
||||||
manager.Dispose();
|
manager.Dispose();
|
||||||
|
@ -13,6 +13,8 @@ namespace SlatedGameToolkit.Framework.Graphics.OpenGL
|
|||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
public class GLContext : IDisposable
|
public class GLContext : IDisposable
|
||||||
{
|
{
|
||||||
|
#region OpenGLFunctions
|
||||||
|
|
||||||
public IntPtr Handle { get; private set; }
|
public IntPtr Handle { get; private set; }
|
||||||
private bool disposed;
|
private bool disposed;
|
||||||
|
|
||||||
@ -224,6 +226,7 @@ namespace SlatedGameToolkit.Framework.Graphics.OpenGL
|
|||||||
public void Enable(EnableCap cap)
|
public void Enable(EnableCap cap)
|
||||||
{
|
{
|
||||||
glEnable.Invoke(cap);
|
glEnable.Invoke(cap);
|
||||||
|
DetectGLError();
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
@ -251,6 +254,7 @@ namespace SlatedGameToolkit.Framework.Graphics.OpenGL
|
|||||||
public void BlendFunc(BlendingFactor sfactor, BlendingFactor dfactor)
|
public void BlendFunc(BlendingFactor sfactor, BlendingFactor dfactor)
|
||||||
{
|
{
|
||||||
glBlendFunc.Invoke(sfactor, dfactor);
|
glBlendFunc.Invoke(sfactor, dfactor);
|
||||||
|
DetectGLError();
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
@ -305,6 +309,7 @@ namespace SlatedGameToolkit.Framework.Graphics.OpenGL
|
|||||||
public void PixelStorei(PixelStoreParameter pname, int param)
|
public void PixelStorei(PixelStoreParameter pname, int param)
|
||||||
{
|
{
|
||||||
glPixelStorei.Invoke(pname, param);
|
glPixelStorei.Invoke(pname, param);
|
||||||
|
DetectGLError();
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
@ -362,12 +367,13 @@ namespace SlatedGameToolkit.Framework.Graphics.OpenGL
|
|||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
private delegate void PFNGLGETINTEGERVPROC(GetPName pname, out int data);
|
private delegate void PFNGLGETINTEGERVPROC(GetPName pname, int[] data);
|
||||||
private PFNGLGETINTEGERVPROC glGetIntegerv;
|
private PFNGLGETINTEGERVPROC glGetIntegerv;
|
||||||
|
|
||||||
public void GetIntegerv(GetPName pname, out int data)
|
public void GetIntegerv(GetPName pname, int[] data)
|
||||||
{
|
{
|
||||||
glGetIntegerv.Invoke(pname, out data);
|
glGetIntegerv.Invoke(pname, data);
|
||||||
|
DetectGLError();
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
@ -530,6 +536,7 @@ namespace SlatedGameToolkit.Framework.Graphics.OpenGL
|
|||||||
public void TexSubImage2D(TextureTarget target, int level, int xoffset, int yoffset, int width, int height, PixelFormat format, PixelType type, IntPtr pixels)
|
public void TexSubImage2D(TextureTarget target, int level, int xoffset, int yoffset, int width, int height, PixelFormat format, PixelType type, IntPtr pixels)
|
||||||
{
|
{
|
||||||
glTexSubImage2D.Invoke(target, level, xoffset, yoffset, width, height, format, type, pixels);
|
glTexSubImage2D.Invoke(target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||||
|
DetectGLError();
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
@ -3153,6 +3160,7 @@ namespace SlatedGameToolkit.Framework.Graphics.OpenGL
|
|||||||
{
|
{
|
||||||
glSampleMaski.Invoke(maskNumber, mask);
|
glSampleMaski.Invoke(maskNumber, mask);
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
public GLContext(IntPtr windowHandle)
|
public GLContext(IntPtr windowHandle)
|
||||||
{
|
{
|
||||||
@ -3162,6 +3170,7 @@ namespace SlatedGameToolkit.Framework.Graphics.OpenGL
|
|||||||
throw new FrameworkSDLException();
|
throw new FrameworkSDLException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region OpenGLDelegateAssignment
|
||||||
glCullFace = Marshal.GetDelegateForFunctionPointer<PFNGLCULLFACEPROC>(loader.Invoke("glCullFace"));
|
glCullFace = Marshal.GetDelegateForFunctionPointer<PFNGLCULLFACEPROC>(loader.Invoke("glCullFace"));
|
||||||
glFrontFace = Marshal.GetDelegateForFunctionPointer<PFNGLFRONTFACEPROC>(loader.Invoke("glFrontFace"));
|
glFrontFace = Marshal.GetDelegateForFunctionPointer<PFNGLFRONTFACEPROC>(loader.Invoke("glFrontFace"));
|
||||||
glHint = Marshal.GetDelegateForFunctionPointer<PFNGLHINTPROC>(loader.Invoke("glHint"));
|
glHint = Marshal.GetDelegateForFunctionPointer<PFNGLHINTPROC>(loader.Invoke("glHint"));
|
||||||
@ -3506,19 +3515,29 @@ namespace SlatedGameToolkit.Framework.Graphics.OpenGL
|
|||||||
glTexImage3DMultisample = Marshal.GetDelegateForFunctionPointer<PFNGLTEXIMAGE3DMULTISAMPLEPROC>(loader.Invoke("glTexImage3DMultisample"));
|
glTexImage3DMultisample = Marshal.GetDelegateForFunctionPointer<PFNGLTEXIMAGE3DMULTISAMPLEPROC>(loader.Invoke("glTexImage3DMultisample"));
|
||||||
glGetMultisamplefv = Marshal.GetDelegateForFunctionPointer<PFNGLGETMULTISAMPLEFVPROC>(loader.Invoke("glGetMultisamplefv"));
|
glGetMultisamplefv = Marshal.GetDelegateForFunctionPointer<PFNGLGETMULTISAMPLEFVPROC>(loader.Invoke("glGetMultisamplefv"));
|
||||||
glSampleMaski = Marshal.GetDelegateForFunctionPointer<PFNGLSAMPLEMASKIPROC>(loader.Invoke("glSampleMaski"));
|
glSampleMaski = Marshal.GetDelegateForFunctionPointer<PFNGLSAMPLEMASKIPROC>(loader.Invoke("glSampleMaski"));
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks for any issues in this OpenGL Context and throws an exception if it detects one.
|
/// Checks for any issues in this OpenGL Context and throws an exception if it detects one.
|
||||||
/// May throw OpenGLException.
|
/// May throw OpenGLException.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void DetectGLError() {
|
public void DetectGLError() {
|
||||||
OpenGL.ErrorCode code = GetError();
|
OpenGL.ErrorCode code = GetError();
|
||||||
if (code != OpenGL.ErrorCode.NoError) {
|
if (code != OpenGL.ErrorCode.NoError) {
|
||||||
throw new OpenGLErrorException(code);
|
throw new OpenGLErrorException(code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void GetViewport(out int x, out int y, out int width, out int height) {
|
||||||
|
int[] viewport = new int[4];
|
||||||
|
GetIntegerv(GetPName.Viewport, viewport);
|
||||||
|
x = viewport[0];
|
||||||
|
y = viewport[1];
|
||||||
|
width = viewport[2];
|
||||||
|
height = viewport[3];
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (!disposed)
|
if (!disposed)
|
||||||
|
@ -32,5 +32,6 @@ namespace SlatedGameToolkit.Framework.Graphics.Render
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>A color for this mesh.</value>
|
/// <value>A color for this mesh.</value>
|
||||||
Color Color { get; }
|
Color Color { get; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,7 +15,7 @@ namespace SlatedGameToolkit.Framework.Graphics.Render
|
|||||||
private bool disposed;
|
private bool disposed;
|
||||||
private float batchDelta;
|
private float batchDelta;
|
||||||
public GLContext GLContext {get; private set; }
|
public GLContext GLContext {get; private set; }
|
||||||
private int projALoc, viewALoc, modelALoc, texturedALoc;
|
private int projALoc, viewALoc, modelALoc, texturedALoc, singleChanneledALoc;
|
||||||
private Camera camera;
|
private Camera camera;
|
||||||
private RenderProgram renderProgram;
|
private RenderProgram renderProgram;
|
||||||
private ITexture texture;
|
private ITexture texture;
|
||||||
@ -54,6 +54,7 @@ namespace SlatedGameToolkit.Framework.Graphics.Render
|
|||||||
viewALoc = GLContext.GetUniformLocation(renderProgram.Handle, "view");
|
viewALoc = GLContext.GetUniformLocation(renderProgram.Handle, "view");
|
||||||
projALoc = GLContext.GetUniformLocation(renderProgram.Handle, "projection");
|
projALoc = GLContext.GetUniformLocation(renderProgram.Handle, "projection");
|
||||||
texturedALoc = GLContext.GetUniformLocation(renderProgram.Handle, "textured");
|
texturedALoc = GLContext.GetUniformLocation(renderProgram.Handle, "textured");
|
||||||
|
singleChanneledALoc = GLContext.GetUniformLocation(renderProgram.Handle, "singleChanneled");
|
||||||
vertexBuffers.defineVertexAttributes(definitions: definitions);
|
vertexBuffers.defineVertexAttributes(definitions: definitions);
|
||||||
|
|
||||||
GLContext.UniformMatrix4fv(projALoc, 1, false, camera.ProjectionMatrix.ToColumnMajorArray());
|
GLContext.UniformMatrix4fv(projALoc, 1, false, camera.ProjectionMatrix.ToColumnMajorArray());
|
||||||
@ -80,15 +81,18 @@ namespace SlatedGameToolkit.Framework.Graphics.Render
|
|||||||
IMoveable moveable = mesh as IMoveable;
|
IMoveable moveable = mesh as IMoveable;
|
||||||
if (moveable != null) {
|
if (moveable != null) {
|
||||||
if (GameEngine.UpdatesPerSecond <= 0) {
|
if (GameEngine.UpdatesPerSecond <= 0) {
|
||||||
camera.Position = camera.MoveTo;
|
moveable.Position = moveable.MoveTo;
|
||||||
} else {
|
} else {
|
||||||
camera.Position = camera.Position + (camera.MoveTo - camera.Position) * batchDelta;
|
moveable.Position = moveable.Position + (moveable.MoveTo - moveable.Position) * batchDelta;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mesh.Texture?.Handle != this.texture?.Handle) {
|
if (mesh.Texture?.Handle != this.texture?.Handle) {
|
||||||
Flush();
|
Flush();
|
||||||
this.texture = mesh.Texture;
|
this.texture = mesh.Texture;
|
||||||
GLContext.Uniform1i(texturedALoc, texture == null ? 0 : 1);
|
GLContext.Uniform1i(texturedALoc, texture == null ? 0 : 1);
|
||||||
|
if (texture != null) {
|
||||||
|
GLContext.Uniform1i(singleChanneledALoc, texture.SingleChanneled ? 1 : 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ValueTuple<Vector3, Vector2>[] vertices = mesh.Vertices;
|
ValueTuple<Vector3, Vector2>[] vertices = mesh.Vertices;
|
||||||
uint[] indices = mesh.Elements;
|
uint[] indices = mesh.Elements;
|
||||||
@ -134,7 +138,9 @@ namespace SlatedGameToolkit.Framework.Graphics.Render
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Flush() {
|
protected virtual void Flush() {
|
||||||
texture?.Use();
|
if (texture != null) {
|
||||||
|
GLContext.BindTexture(TextureTarget.Texture2D, texture.Handle);
|
||||||
|
}
|
||||||
renderProgram.Use();
|
renderProgram.Use();
|
||||||
if (GameEngine.UpdatesPerSecond <= 0) {
|
if (GameEngine.UpdatesPerSecond <= 0) {
|
||||||
camera.Position = camera.MoveTo;
|
camera.Position = camera.MoveTo;
|
||||||
|
@ -11,8 +11,7 @@ namespace SlatedGameToolkit.Framework.Graphics.Render
|
|||||||
private bool changed;
|
private bool changed;
|
||||||
private Vector3 rotation;
|
private Vector3 rotation;
|
||||||
private Vector2 origin, dimensions;
|
private Vector2 origin, dimensions;
|
||||||
public readonly Vector2[] textureCoords = new Vector2[4];
|
private ValueTuple<Vector3, Vector2>[] vertices = new ValueTuple<Vector3, Vector2>[4];
|
||||||
public ValueTuple<Vector3, Vector2>[] vertices = new ValueTuple<Vector3, Vector2>[4];
|
|
||||||
private uint[] indices = new uint[] {0, 1, 3, 1, 2, 3};
|
private uint[] indices = new uint[] {0, 1, 3, 1, 2, 3};
|
||||||
|
|
||||||
public ValueTuple<Vector3, Vector2>[] Vertices
|
public ValueTuple<Vector3, Vector2>[] Vertices
|
||||||
@ -78,6 +77,19 @@ namespace SlatedGameToolkit.Framework.Graphics.Render
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RectangleF Bounds {
|
||||||
|
get {
|
||||||
|
return new RectangleF(X, Y, Width, Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
set {
|
||||||
|
X = value.X;
|
||||||
|
Y = value.Y;
|
||||||
|
Width = value.Width;
|
||||||
|
Height = value.Height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public uint[] Elements { get {return indices; } }
|
public uint[] Elements { get {return indices; } }
|
||||||
|
|
||||||
public Vector3 Rotation
|
public Vector3 Rotation
|
||||||
@ -99,24 +111,33 @@ namespace SlatedGameToolkit.Framework.Graphics.Render
|
|||||||
|
|
||||||
public Color Color { get; set; }
|
public Color Color { get; set; }
|
||||||
|
|
||||||
public RectangleMesh(ITexture texture, Color color)
|
public RectangleMesh(RectangleF textureRegion, ITexture texture, Color color)
|
||||||
{
|
{
|
||||||
this.Texture = texture;
|
this.Texture = texture;
|
||||||
this.Color = color;
|
this.Color = color;
|
||||||
|
|
||||||
this.textureCoords[0].X = 0;
|
this.vertices[0].Item2.X = textureRegion.X;
|
||||||
this.textureCoords[0].Y = 1;
|
this.vertices[0].Item2.Y = textureRegion.Y;
|
||||||
|
|
||||||
this.textureCoords[1].X = 1;
|
this.vertices[1].Item2.X = textureRegion.X + textureRegion.Width;
|
||||||
this.textureCoords[1].Y = 1;
|
this.vertices[1].Item2.Y = textureRegion.Y;
|
||||||
|
|
||||||
this.textureCoords[2].X = 1;
|
this.vertices[2].Item2.X = textureRegion.X + textureRegion.Width;
|
||||||
this.textureCoords[2].Y = 0;
|
this.vertices[2].Item2.Y = textureRegion.Y + textureRegion.Height;
|
||||||
|
|
||||||
this.textureCoords[3].X = 0;
|
this.vertices[3].Item2.X = textureRegion.X;
|
||||||
this.textureCoords[3].Y = 0;
|
this.vertices[3].Item2.Y = textureRegion.Y + textureRegion.Height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RectangleMesh(RectangleF meshBounds, RectangleF textureRegion, ITexture texture, Color color) : this(textureRegion, texture, color) {
|
||||||
|
this.Bounds = meshBounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RectangleMesh(ITexture texture, Color color) : this(new Rectangle(0, 0, 1, 1), texture, color) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void CalculateVertices() {
|
private void CalculateVertices() {
|
||||||
if (!changed) return;
|
if (!changed) return;
|
||||||
@ -128,7 +149,7 @@ namespace SlatedGameToolkit.Framework.Graphics.Render
|
|||||||
|
|
||||||
for (int i = 0; i < vertices.Length; i++)
|
for (int i = 0; i < vertices.Length; i++)
|
||||||
{
|
{
|
||||||
vertices[i] = (Vector3.Transform(baseVerts[i], matRot), textureCoords[i]);
|
vertices[i].Item1 = Vector3.Transform(baseVerts[i], matRot);
|
||||||
}
|
}
|
||||||
changed = false;
|
changed = false;
|
||||||
}
|
}
|
||||||
|
272
src/SlatedGameToolkit.Framework/Graphics/Text/BitmapFont.cs
Normal file
272
src/SlatedGameToolkit.Framework/Graphics/Text/BitmapFont.cs
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using SlatedGameToolkit.Framework.Graphics.OpenGL;
|
||||||
|
using SlatedGameToolkit.Framework.Graphics.Render;
|
||||||
|
using SlatedGameToolkit.Framework.Graphics.Textures;
|
||||||
|
using SlatedGameToolkit.Framework.Graphics.Window;
|
||||||
|
using SlatedGameToolkit.Framework.Utilities;
|
||||||
|
using SlatedGameToolkit.Framework.Utilities.Collections;
|
||||||
|
using StbTrueTypeSharp;
|
||||||
|
|
||||||
|
namespace SlatedGameToolkit.Framework.Graphics.Text
|
||||||
|
{
|
||||||
|
public class BitmapFont : IDisposable
|
||||||
|
{
|
||||||
|
private StbTrueType.stbtt_fontinfo info;
|
||||||
|
private byte[] fontData;
|
||||||
|
private bool disposed;
|
||||||
|
private float scale;
|
||||||
|
private int bitmapLength;
|
||||||
|
private int pixelHeight;
|
||||||
|
public int 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<char, int> glyphIndexCache;
|
||||||
|
private FontBitmap[] textureBuffers;
|
||||||
|
private int drawingTo = 0;
|
||||||
|
|
||||||
|
GLContext glContext;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a font with the given data.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fontData">The byte array containing all ttf data.</param>
|
||||||
|
/// <param name="glContext">The OpenGL context to use to associate with.</param>
|
||||||
|
/// <param name="cacheSize">The size of the caches.</param>
|
||||||
|
/// <param name="bitmapLength">The length of one side of a bitmap, therefore, the total would be this squared.</param>
|
||||||
|
/// <param name="numberOfTextures">The number of backing textures to use.</param>
|
||||||
|
/// <param name="initialCharacters">An initial set of characters to load.</param>
|
||||||
|
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<char, int>(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++)
|
||||||
|
{
|
||||||
|
textureBuffers[i] = new FontBitmap(this.glContext, bitmapLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
info = new StbTrueType.stbtt_fontinfo();
|
||||||
|
|
||||||
|
fixed (byte* data = &fontData[0]) {
|
||||||
|
StbTrueType.stbtt_InitFont(info, data, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
PixelHeight = 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the requested character.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="character">The character to load.</param>
|
||||||
|
/// <returns>True if already loaded or successfully loaded, false if could not load.</returns>
|
||||||
|
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;
|
||||||
|
StbTrueType.stbtt_GetGlyphBitmapBox(info, glyphIndex, scale, scale, &x0, &y0, &x1, &y1);
|
||||||
|
if (!textureBuffers[drawingTo].CanAdd(x1 - x0, y1 - y0)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
textureBuffers[drawingTo].Upload(character, x1 - x0, y1 - y0, scale, info, glyphIndex);
|
||||||
|
bitmapLocations.Add((scale, character), drawingTo);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe void Draw(MeshBatch batch, double delta, float x, float y, string characters, Color color) {
|
||||||
|
float horizontal = x;
|
||||||
|
foreach (char c in characters)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!disposed)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
foreach (FontBitmap bitmap in textureBuffers)
|
||||||
|
{
|
||||||
|
bitmap.Dispose();
|
||||||
|
}
|
||||||
|
disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~BitmapFont()
|
||||||
|
{
|
||||||
|
Dispose(disposing: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace SlatedGameToolkit.Framework.Graphics.Text
|
|
||||||
{
|
|
||||||
public class DynamicFont {
|
|
||||||
private readonly Dictionary<char, uint> glyphLocations = new Dictionary<char, uint>();
|
|
||||||
|
|
||||||
public DynamicFont() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
using System.Numerics;
|
|
||||||
|
|
||||||
namespace SlatedGameToolkit.Framework.Graphics.Text
|
|
||||||
{
|
|
||||||
public struct DynamicGlyph {
|
|
||||||
public readonly char character;
|
|
||||||
public readonly (Vector2, Vector2)[] vertices;
|
|
||||||
|
|
||||||
public DynamicGlyph(char character, (Vector2, Vector2)[] vertices) {
|
|
||||||
this.character = character;
|
|
||||||
this.vertices = vertices;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
namespace SlatedGameToolkit.Framework.Graphics.Textures
|
namespace SlatedGameToolkit.Framework.Graphics.Textures
|
||||||
{
|
{
|
||||||
public interface ITexture
|
public interface ITexture : IDisposable
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Whether or not this model uses a texture that is single chanelled.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>true for single channeled.</value>
|
||||||
|
bool SingleChanneled { get; }
|
||||||
uint Handle {get;}
|
uint Handle {get;}
|
||||||
void Use();
|
uint Width {get;}
|
||||||
|
uint Height {get;}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,13 +7,19 @@ using SlatedGameToolkit.Framework.AssetSystem;
|
|||||||
namespace SlatedGameToolkit.Framework.Graphics.Textures
|
namespace SlatedGameToolkit.Framework.Graphics.Textures
|
||||||
{
|
{
|
||||||
public class Texture : IAssetUseable, ITexture {
|
public class Texture : IAssetUseable, ITexture {
|
||||||
public readonly int width, height;
|
public readonly uint width, height;
|
||||||
private bool disposed;
|
private bool disposed;
|
||||||
private GLContext glContext;
|
private GLContext glContext;
|
||||||
private uint handle;
|
private uint handle;
|
||||||
|
|
||||||
public uint Handle {get { return handle; }}
|
public uint Handle {get { return handle; }}
|
||||||
|
|
||||||
|
public uint Width => width;
|
||||||
|
|
||||||
|
public uint Height => height;
|
||||||
|
|
||||||
|
public bool SingleChanneled => false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates an OpenGL Texture2D in the given GL Context.
|
/// Creates an OpenGL Texture2D in the given GL Context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -21,8 +27,8 @@ namespace SlatedGameToolkit.Framework.Graphics.Textures
|
|||||||
/// <param name="context">The openGL context to associate this with. If null, will use the currently active context. Defaults to null.</param>
|
/// <param name="context">The openGL context to associate this with. If null, will use the currently active context. Defaults to null.</param>
|
||||||
public unsafe Texture(TextureData textureData, GLContext context = null) {
|
public unsafe Texture(TextureData textureData, GLContext context = null) {
|
||||||
this.glContext = context ?? WindowContextsManager.CurrentGL;
|
this.glContext = context ?? WindowContextsManager.CurrentGL;
|
||||||
this.width = textureData.width;
|
this.width = (uint)textureData.width;
|
||||||
this.height = textureData.height;
|
this.height = (uint)textureData.height;
|
||||||
uint[] handles = new uint[1];
|
uint[] handles = new uint[1];
|
||||||
glContext.GenTextures(1, handles);
|
glContext.GenTextures(1, handles);
|
||||||
this.handle = handles[0];
|
this.handle = handles[0];
|
||||||
@ -32,6 +38,7 @@ namespace SlatedGameToolkit.Framework.Graphics.Textures
|
|||||||
glContext.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);
|
glContext.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);
|
||||||
glContext.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
glContext.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
||||||
fixed(void* p = &textureData.data[0]) {
|
fixed(void* p = &textureData.data[0]) {
|
||||||
|
glContext.PixelStorei(PixelStoreParameter.UnpackAlignment, 4);
|
||||||
glContext.TexImage2D(TextureTarget.Texture2D, 0, InternalFormat.Rgba, (uint)textureData.width, (uint)textureData.height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, new IntPtr(p));
|
glContext.TexImage2D(TextureTarget.Texture2D, 0, InternalFormat.Rgba, (uint)textureData.width, (uint)textureData.height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, new IntPtr(p));
|
||||||
}
|
}
|
||||||
glContext.GenerateMipmap(OpenGL.TextureTarget.Texture2D);
|
glContext.GenerateMipmap(OpenGL.TextureTarget.Texture2D);
|
||||||
|
@ -16,7 +16,12 @@ namespace SlatedGameToolkit.Framework.Graphics.Window
|
|||||||
get {
|
get {
|
||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set {
|
||||||
|
value.MakeCurrent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Dictionary<uint, WindowContext> existingWindows = new Dictionary<uint, WindowContext>();
|
private static Dictionary<uint, WindowContext> existingWindows = new Dictionary<uint, WindowContext>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -4,13 +4,19 @@ out vec4 outputColor;
|
|||||||
in vec2 texCoord;
|
in vec2 texCoord;
|
||||||
in vec4 color;
|
in vec4 color;
|
||||||
|
|
||||||
|
uniform bool singleChanneled;
|
||||||
uniform bool textured;
|
uniform bool textured;
|
||||||
uniform sampler2D texture0;
|
uniform sampler2D texture0;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
if (textured) {
|
if (textured) {
|
||||||
outputColor = texture(texture0, texCoord) * color;
|
if (singleChanneled) {
|
||||||
|
float singleVal = texture(texture0, vec2(texCoord.x, 1 - texCoord.y)).r;
|
||||||
|
outputColor = vec4(singleVal);
|
||||||
|
} else {
|
||||||
|
outputColor = texture(texture0, vec2(texCoord.x, 1 - texCoord.y)) * color;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
outputColor = color;
|
outputColor = color;
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,5 @@ void main()
|
|||||||
{
|
{
|
||||||
texCoord = aTexCoord;
|
texCoord = aTexCoord;
|
||||||
color = aColor;
|
color = aColor;
|
||||||
|
|
||||||
gl_Position = projection * view * models * vec4(aPosition, 1.0);
|
gl_Position = projection * view * models * vec4(aPosition, 1.0);
|
||||||
}
|
}
|
16
src/SlatedGameToolkit.Framework/Utilities/RectangleUtils.cs
Normal file
16
src/SlatedGameToolkit.Framework/Utilities/RectangleUtils.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace SlatedGameToolkit.Framework.Utilities
|
||||||
|
{
|
||||||
|
public static class RectangleUtils
|
||||||
|
{
|
||||||
|
public static RectangleF MultiplyBy(this RectangleF rectangle, float multiplier) {
|
||||||
|
RectangleF rect = rectangle;
|
||||||
|
rect.X *= multiplier;
|
||||||
|
rect.Y *= multiplier;
|
||||||
|
rect.Width *= multiplier;
|
||||||
|
rect.Height *= multiplier;
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
@ -10,6 +10,7 @@
|
|||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
||||||
<Description>A tool to help with developing a game using SlatedGameToolkit.</Description>
|
<Description>A tool to help with developing a game using SlatedGameToolkit.</Description>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using SDL2;
|
using SDL2;
|
||||||
using SlatedGameToolkit.Commons.Loaders;
|
using SlatedGameToolkit.Commons.Loaders;
|
||||||
using SlatedGameToolkit.Framework.Graphics.Render;
|
using SlatedGameToolkit.Framework.Graphics.Render;
|
||||||
|
using SlatedGameToolkit.Framework.Graphics.Text;
|
||||||
using SlatedGameToolkit.Framework.Graphics.Textures;
|
using SlatedGameToolkit.Framework.Graphics.Textures;
|
||||||
using SlatedGameToolkit.Framework.Graphics.Window;
|
using SlatedGameToolkit.Framework.Graphics.Window;
|
||||||
using SlatedGameToolkit.Framework.Input.Devices;
|
using SlatedGameToolkit.Framework.Input.Devices;
|
||||||
using SlatedGameToolkit.Framework.StateSystem;
|
using SlatedGameToolkit.Framework.StateSystem;
|
||||||
using SlatedGameToolkit.Framework.StateSystem.States;
|
using SlatedGameToolkit.Framework.StateSystem.States;
|
||||||
|
using StbTrueTypeSharp;
|
||||||
|
|
||||||
namespace SlatedGameToolkit.Tools.Utilities.Playground
|
namespace SlatedGameToolkit.Tools.Utilities.Playground
|
||||||
{
|
{
|
||||||
@ -17,6 +21,7 @@ namespace SlatedGameToolkit.Tools.Utilities.Playground
|
|||||||
private WindowContext window;
|
private WindowContext window;
|
||||||
private Camera2D camera;
|
private Camera2D camera;
|
||||||
private MeshBatch renderer;
|
private MeshBatch renderer;
|
||||||
|
private BitmapFont font;
|
||||||
private Texture logoTexture, fillerTexture;
|
private Texture logoTexture, fillerTexture;
|
||||||
private RectangleMesh logo, textureTester, untextured;
|
private RectangleMesh logo, textureTester, untextured;
|
||||||
|
|
||||||
@ -36,6 +41,7 @@ namespace SlatedGameToolkit.Tools.Utilities.Playground
|
|||||||
{
|
{
|
||||||
logoTexture.Dispose();
|
logoTexture.Dispose();
|
||||||
fillerTexture.Dispose();
|
fillerTexture.Dispose();
|
||||||
|
font.Dispose();
|
||||||
renderer.Dispose();
|
renderer.Dispose();
|
||||||
window.Dispose();
|
window.Dispose();
|
||||||
}
|
}
|
||||||
@ -45,13 +51,13 @@ namespace SlatedGameToolkit.Tools.Utilities.Playground
|
|||||||
return "main state";
|
return "main state";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize(StateManager manager)
|
public unsafe void Initialize(StateManager manager)
|
||||||
{
|
{
|
||||||
window = new WindowContext("SlatedGameToolkit Playground");
|
window = new WindowContext("SlatedGameToolkit Playground");
|
||||||
camera = new Camera2D(2, 2);
|
camera = new Camera2D(2, 2);
|
||||||
renderer = new MeshBatch(camera);
|
renderer = new MeshBatch(camera);
|
||||||
|
|
||||||
logoTexture = TextureLoader.Load2DTexture("Resources/Playground/yhdnbgnc.png", null);
|
logoTexture = TextureLoader.Load2DTexture("Resources/Playground/yhdnbgnc.png");
|
||||||
logo = new RectangleMesh(logoTexture, Color.White);
|
logo = new RectangleMesh(logoTexture, Color.White);
|
||||||
logo.Width = 0.5f;
|
logo.Width = 0.5f;
|
||||||
logo.Height = 0.5f;
|
logo.Height = 0.5f;
|
||||||
@ -59,7 +65,7 @@ namespace SlatedGameToolkit.Tools.Utilities.Playground
|
|||||||
logo.Y = -0.25f;
|
logo.Y = -0.25f;
|
||||||
|
|
||||||
|
|
||||||
fillerTexture = TextureLoader.Load2DTexture("Resources/Playground/filler.png", null);
|
fillerTexture = TextureLoader.Load2DTexture("Resources/Playground/filler.png");
|
||||||
textureTester = new RectangleMesh(fillerTexture, Color.White);
|
textureTester = new RectangleMesh(fillerTexture, Color.White);
|
||||||
textureTester.Width = 0.15f;
|
textureTester.Width = 0.15f;
|
||||||
textureTester.Height = 0.15f;
|
textureTester.Height = 0.15f;
|
||||||
@ -70,6 +76,8 @@ namespace SlatedGameToolkit.Tools.Utilities.Playground
|
|||||||
untextured.Height = 0.1f;
|
untextured.Height = 0.1f;
|
||||||
untextured.X = 0.25f;
|
untextured.X = 0.25f;
|
||||||
untextured.Y = - 0.15f;
|
untextured.Y = - 0.15f;
|
||||||
|
|
||||||
|
font = new BitmapFont("Resources/Playground/earwig_factory_rg.ttf");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Render(double delta)
|
public void Render(double delta)
|
||||||
@ -78,6 +86,7 @@ namespace SlatedGameToolkit.Tools.Utilities.Playground
|
|||||||
renderer.Draw(logo);
|
renderer.Draw(logo);
|
||||||
renderer.Draw(textureTester);
|
renderer.Draw(textureTester);
|
||||||
renderer.Draw(untextured);
|
renderer.Draw(untextured);
|
||||||
|
font.Draw(renderer, delta, 0.25f, -0.25f, "123", Color.White);
|
||||||
renderer.End();
|
renderer.End();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,5 +50,25 @@ namespace SlatedGameToolkit.Framework.Tests.Utilities.Collections.Caching
|
|||||||
cache[5] = 5;
|
cache[5] = 5;
|
||||||
Assert.IsTrue(cache.ContainsKey(2));
|
Assert.IsTrue(cache.ContainsKey(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ComputeIfNonexistentTest() {
|
||||||
|
LRUCache<int, int> cache = new LRUCache<int, int>(4);
|
||||||
|
for (int i = 0; i < cache.MaxLength; i++) {
|
||||||
|
cache.ComputeIfNonExistent(i, (a) => a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ComputeIfNonexistentTestExists() {
|
||||||
|
LRUCache<int, int> cache = new LRUCache<int, int>(4);
|
||||||
|
for (int i = 0; i < cache.MaxLength; i++) {
|
||||||
|
cache.ComputeIfNonExistent(i, (a) => a);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < cache.MaxLength; i++) {
|
||||||
|
Assert.AreEqual(i, cache.ComputeIfNonExistent(i, (a) => -a));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user