diff --git a/GraphicsManager/Enums/ObjectAnchor.cs b/GraphicsManager/Enums/ObjectAnchor.cs new file mode 100644 index 0000000..f693894 --- /dev/null +++ b/GraphicsManager/Enums/ObjectAnchor.cs @@ -0,0 +1,10 @@ +namespace GraphicsManager.Enums; + +public enum ObjectAnchor +{ + Left = 0b_0001, + Top = 0b_0010, + Right = 0b_0100, + Bottom = 0b_1000, + All = Left | Top | Right | Bottom, +} diff --git a/GraphicsManager/GraphicsManager.csproj b/GraphicsManager/GraphicsManager.csproj new file mode 100644 index 0000000..fd26c9e --- /dev/null +++ b/GraphicsManager/GraphicsManager.csproj @@ -0,0 +1,20 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + + + diff --git a/GraphicsManager/Interfaces/IParent.cs b/GraphicsManager/Interfaces/IParent.cs new file mode 100644 index 0000000..6ac1561 --- /dev/null +++ b/GraphicsManager/Interfaces/IParent.cs @@ -0,0 +1,18 @@ +using OpenTK.Mathematics; +using OpenTK.Windowing.Common; + +namespace GraphicsManager.Interfaces; + +public interface IParent +{ + public Vector2i Size { get; } + public void Resize(ResizeEventArgs e); + public float[] RctToFloat(int x, int y, int Width, int Height, bool hastexture = false, float z = 0.0f); + public float IntToFloat(int p, bool Invert = false); + public float FloatToInt(float p, bool Invert = false); + public event Action MouseDown; + public event Action KeyDown; + public Vector2 MousePosition { get; } + + public Vector2i Position { get; } +} diff --git a/GraphicsManager/Interfaces/IRenderObject.cs b/GraphicsManager/Interfaces/IRenderObject.cs new file mode 100644 index 0000000..310e380 --- /dev/null +++ b/GraphicsManager/Interfaces/IRenderObject.cs @@ -0,0 +1,23 @@ +using GraphicsManager.Enums; +using OpenTK.Mathematics; + +namespace GraphicsManager.Interfaces; + +public interface IRenderObject +{ + public ObjectAnchor Anchor { get; set; } + public bool Loaded { get; } + public void LoadToParent(IParent Parent, Window Window); + public void Draw(); + public void Clean(); + public Vector2i Size { get; set; } + public Vector2i Location { get; set; } + public Vector2 SizeAsFloat { get; } + public Vector2 LocationAsFloat { get; } + public Vector2i Distance { get; } + public IParent? Parent { get; } + public Window? Window { get; } + public bool Visible { get; set; } + + public event Func? Clicked; +} diff --git a/GraphicsManager/Interfaces/ITextureObject.cs b/GraphicsManager/Interfaces/ITextureObject.cs new file mode 100644 index 0000000..4fb3848 --- /dev/null +++ b/GraphicsManager/Interfaces/ITextureObject.cs @@ -0,0 +1,13 @@ +using GraphicsManager.Objects.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GraphicsManager.Interfaces; + +public interface ITextureObject : IRenderObject +{ + public Texture? Texture { get; } +} diff --git a/GraphicsManager/Objects/Core/Font.cs b/GraphicsManager/Objects/Core/Font.cs new file mode 100644 index 0000000..35918a7 --- /dev/null +++ b/GraphicsManager/Objects/Core/Font.cs @@ -0,0 +1,59 @@ +using System.Reflection; + +namespace GraphicsManager.Objects.Core; + +public class Font +{ + public void SetEmbeddedFont(string Font, Assembly? Assembly = null) + { + _ = Font ?? throw new ArgumentNullException(nameof(Font)); + this.Assembly = Assembly; + this.Embeded = true; + this.Name = Font; + } + + public static Font MakeEmbeddedFont(string Font, Assembly? Assembly = null) + { + _ = Font ?? throw new ArgumentNullException(nameof(Font)); + return new Font() + { + Assembly = Assembly, + Embeded = true, + Name = Font, + }; + } + + public static Font MakeFontFromFile(string Font) + { + _ = Font ?? throw new ArgumentNullException(nameof(Font)); + return new Font() + { + Assembly = null, + Embeded = false, + Name = Font, + }; + } + + public void SetFontFile(string Font) + { + _ = Font ?? throw new ArgumentNullException(nameof(Font)); + this.Assembly = null; + this.Embeded = false; + this.Name = Font; + } + + public byte[] GetData() + { + if (Embeded) + { + string Base = "GraphicsManager.Resources.Fonts."; + if (Assembly is not null) Base = string.Empty; + return (Assembly is null ? Tools.GetResourceBytes(Base + Name) : Tools.GetResourceBytes(Assembly!, $"{Base}{Name}")); + } + return File.ReadAllBytes(Name); + } + + public string Name { get; private set; } = "shal be default"; + public bool Embeded { get; private set; } + public Assembly? Assembly { get; private set; } +} diff --git a/GraphicsManager/Objects/Core/Shader.cs b/GraphicsManager/Objects/Core/Shader.cs new file mode 100644 index 0000000..8d79551 --- /dev/null +++ b/GraphicsManager/Objects/Core/Shader.cs @@ -0,0 +1,129 @@ +using OpenTK.Graphics.OpenGL4; +using System.Reflection; + +namespace GraphicsManager.Objects.Core; + +public class Shader : IDisposable +{ + public int Handle { get; } + private readonly int VertexShader; + private readonly int FragmentShader; + private bool disposedValue = false; + + public Shader(string VertexShaderSource, string FragmentShaderSource, bool VertextBuiltIn = false, bool FragmentShaderBuiltIn = false, Assembly? Assembly = null) + { + VertexShader = GL.CreateShader(ShaderType.VertexShader); + string Base = "GraphicsManager.Resources.Shaders."; + if (Assembly is not null) Base = string.Empty; + string vss = (VertextBuiltIn ? Tools.GetResourceString((Assembly == null ? typeof(Tools).Assembly : Assembly), $"{Base}{VertexShaderSource}") : File.ReadAllText(VertexShaderSource))!; + string fss = (FragmentShaderBuiltIn ? Tools.GetResourceString((Assembly ?? typeof(Tools).Assembly), $"{Base}{FragmentShaderSource}") : File.ReadAllText(FragmentShaderSource))!; + GL.ShaderSource(VertexShader, vss); + + FragmentShader = GL.CreateShader(ShaderType.FragmentShader); + GL.ShaderSource(FragmentShader, fss); + + GL.CompileShader(VertexShader); + + string infoLogVert = GL.GetShaderInfoLog(VertexShader); + if (infoLogVert != string.Empty) + Console.WriteLine(infoLogVert); + + GL.CompileShader(FragmentShader); + + string infoLogFrag = GL.GetShaderInfoLog(FragmentShader); + + if (infoLogFrag != string.Empty) + Console.WriteLine(infoLogFrag); + + Handle = GL.CreateProgram(); + + GL.AttachShader(Handle, VertexShader); + GL.AttachShader(Handle, FragmentShader); + + GL.LinkProgram(Handle); + + GL.DetachShader(Handle, VertexShader); + GL.DetachShader(Handle, FragmentShader); + GL.DeleteShader(FragmentShader); + GL.DeleteShader(VertexShader); + } + + public Shader(string ShaderSource, bool Embeded = false, Assembly? Assembly = null) + { + VertexShader = GL.CreateShader(ShaderType.VertexShader); + string Base = "GraphicsManager.Resources.Shaders."; + if (Assembly is not null) Base = string.Empty; + GL.ShaderSource(VertexShader, (Embeded ? Tools.GetResourceString((Assembly ?? typeof(Tools).Assembly), $"{Base}{ShaderSource}.vert") : File.ReadAllText($"{ShaderSource}.vert"))); + + FragmentShader = GL.CreateShader(ShaderType.FragmentShader); + GL.ShaderSource(FragmentShader, (Embeded ? Tools.GetResourceString((Assembly ?? typeof(Tools).Assembly), $"{Base}{ShaderSource}.frag") : File.ReadAllText($"{ShaderSource}.frag"))); + + GL.CompileShader(VertexShader); + + string infoLogVert = GL.GetShaderInfoLog(VertexShader); + if (infoLogVert != string.Empty) + Console.WriteLine(infoLogVert); + + GL.CompileShader(FragmentShader); + + string infoLogFrag = GL.GetShaderInfoLog(FragmentShader); + + if (infoLogFrag != string.Empty) + Console.WriteLine(infoLogFrag); + + Handle = GL.CreateProgram(); + + GL.AttachShader(Handle, VertexShader); + GL.AttachShader(Handle, FragmentShader); + + GL.LinkProgram(Handle); + + GL.DetachShader(Handle, VertexShader); + GL.DetachShader(Handle, FragmentShader); + GL.DeleteShader(FragmentShader); + GL.DeleteShader(VertexShader); + } + + public void Use() + { + GL.UseProgram(Handle); + } + + public int GetAttribLocation(string attribName) + { + return GL.GetAttribLocation(Handle, attribName); + } + + public void SetInt(string name, int value) + { + int location = GL.GetUniformLocation(Handle, name); + + GL.Uniform1(location, value); + } + + public Shader Clone() + { + return (Shader)MemberwiseClone(); + } + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + GL.DeleteProgram(Handle); + + disposedValue = true; + } + } + + ~Shader() + { + GL.DeleteProgram(Handle); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } +} diff --git a/GraphicsManager/Objects/Core/Texture.cs b/GraphicsManager/Objects/Core/Texture.cs new file mode 100644 index 0000000..d0e95a4 --- /dev/null +++ b/GraphicsManager/Objects/Core/Texture.cs @@ -0,0 +1,106 @@ +using GraphicsManager.Structs; +using OpenTK.Graphics.OpenGL4; +using OpenTK.Mathematics; +using SharpFont; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using Image = SixLabors.ImageSharp.Image; + +namespace GraphicsManager.Objects.Core; + +public class Texture +{ + public static readonly Shader TextureShader = new("RectangleTexture", true); + + public int handel; + public Texture(byte[] File) + { + Image image = Image.Load(File); + image.Mutate(x => x.Flip(FlipMode.Vertical)); + + var pixels = new List(4 * image.Width * image.Height); + + for (int y = 0; y < image.Height; y++) + { + var row = image.GetPixelRowSpan(y); + + for (int x = 0; x < image.Width; x++) + { + pixels.Add(row[x].R); + pixels.Add(row[x].G); + pixels.Add(row[x].B); + pixels.Add(row[x].A); + } + } + + handel = GL.GenTexture(); + GL.BindTexture(TextureTarget.Texture2D, handel); + GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, image.Width, image.Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, pixels.ToArray()); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); + } + + internal Texture(Label l, char charter, uint PixelHeight, Face face) + { + if (!Label._characters.ContainsKey(l)) Label._characters.Add(l, new Dictionary()); + face.SetPixelSizes(0, PixelHeight); + + GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1); + + GL.ActiveTexture(TextureUnit.Texture0); + face.SelectCharmap(Encoding.Unicode); + ushort temp = ((ushort)charter); + + try + { + face.LoadChar(temp, LoadFlags.Render, LoadTarget.Normal); + GlyphSlot glyph = face.Glyph; + FTBitmap bitmap = glyph.Bitmap; + + handel = GL.GenTexture(); + GL.BindTexture(TextureTarget.Texture2D, handel); + GL.TexImage2D(TextureTarget.Texture2D, 0, + PixelInternalFormat.R8, bitmap.Width, bitmap.Rows, 0, + PixelFormat.Red, PixelType.UnsignedByte, bitmap.Buffer); + + + Character cha = new() + { + Size = new Vector2(bitmap.Width, bitmap.Rows), + Bearing = new Vector2(glyph.BitmapLeft, glyph.BitmapTop), + Advance = (int)glyph.Advance.X.Value, + Texture = this, + }; + + Label._characters[l].Add(temp, cha); + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + } + + public void LoadText() + { + GL.TextureParameter(handel, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); + GL.TextureParameter(handel, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); + GL.TextureParameter(handel, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge); + GL.TextureParameter(handel, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge); + } + + public void Load(int loc) + { + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest); + GL.EnableVertexAttribArray(loc); + GL.VertexAttribPointer(loc, 2, VertexAttribPointerType.Float, false, 5 * sizeof(float), 3 * sizeof(float)); + } + + public void Use(TextureUnit unit = TextureUnit.Texture0) + { + GL.ActiveTexture(unit); + GL.BindTexture(TextureTarget.Texture2D, handel); + } +} diff --git a/GraphicsManager/Objects/Label.cs b/GraphicsManager/Objects/Label.cs new file mode 100644 index 0000000..12e6dc8 --- /dev/null +++ b/GraphicsManager/Objects/Label.cs @@ -0,0 +1,214 @@ +using GraphicsManager.Enums; +using GraphicsManager.Interfaces; +using GraphicsManager.Objects.Core; +using GraphicsManager.Structs; +using OpenTK.Graphics.OpenGL4; +using OpenTK.Mathematics; +using SharpFont; +using System.Reflection; +using Encoding = SharpFont.Encoding; + +namespace GraphicsManager.Objects; + +public class Label : IRenderObject +{ + public static readonly Shader DefaultTextShader = new("Label", true); + public IParent? Parent { get; private set; } + public ObjectAnchor Anchor { get; set; } = ObjectAnchor.Left | ObjectAnchor.Top; + private Vector2 laf = new(), saf = new(); + + public Vector2 LocationAsFloat { get { return laf; } } + public Vector2 SizeAsFloat { get { return saf; } } + public bool Visible { get; set; } = true; + + public static readonly Dictionary> _characters = new(); + private string text = string.Empty; + public int VAO { get; set; } + public int VBO { get; set; } + public Vector2 DIR { get; set; } = new Vector2(1f, 0f); + public string Text + { + get => text; + set + { + if (Loaded) + { + Library lib = new(); + Face face = new(lib, Font.GetData(), 0); + + face.SetPixelSizes(0, PixelHeight); + + GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1); + + face.SelectCharmap(Encoding.Unicode); + if (!_characters.ContainsKey(this)) _characters.Add(this, new Dictionary()); + foreach (char character in value) + { + if (_characters[this].ContainsKey(character) == false) + { + var f = new Texture(this, character, PixelHeight, face); + f.LoadText(); + } + } + } + text = value; + } + } + public uint PixelHeight { get; set; } = 20; + public float Scale { get; set; } = 1.2f; + public Shader Shader { get; } = DefaultTextShader; + public Font Font { get; set; } = Font.MakeEmbeddedFont("TektonPro-Regular.otf"); + + public Vector4 Color { get; set; } = new Vector4(1, 1, 1, 1); + public Vector2i Distance { get; private set; } + private Vector2i loc_ = new(); + private Vector2i locc_ = new(); + public Vector2i Location + { + get + { + return loc_; + } + set + { + + loc_ = value; + if (Window is null || Parent is null) return; + locc_ = new(value.X + Parent.Position.X, value.Y + Parent.Position.Y); + } + } + public Vector2i Size { get; set; } + + public void Clean() + { + + } + + public void Draw() + { + if (Visible & loadd) + { + Shader.Use(); + GL.Enable(EnableCap.Blend); + GL.Uniform4(2, Color); + Matrix4 projectionM = Matrix4.CreateOrthographicOffCenter(0, Window!.Size.X, Window!.Size.Y, 0, -1.0f, 1.0f); + GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); + GL.BlendFunc(0, BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); + GL.UniformMatrix4(1, false, ref projectionM); + + GL.BindVertexArray(VAO); + + float angle_rad = (float)Math.Atan2(DIR.Y, DIR.X); + Matrix4 rotateM = Matrix4.CreateRotationZ(angle_rad); + Matrix4 transOriginM = Matrix4.CreateTranslation(new Vector3(locc_.X, locc_.Y, 0f)); + + float char_x = 0.0f; + + Library lib = new(); + + Face face = new(lib, Font.GetData(), 0); + + face.SetPixelSizes(0, PixelHeight); + + GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1); + + GL.ActiveTexture(TextureUnit.Texture0); + face.SelectCharmap(Encoding.Unicode); + + + float hhh = 0f; + foreach (char c in Text) + { + if (!_characters[this].ContainsKey(c)) break; + Character ch = _characters[this][c]; + + if (c == '\n') + { + hhh += PixelHeight; + char_x = 0f; + } + else + { + float w = ch.Size.X * Scale; + float h = ch.Size.Y * Scale; + float xrel = char_x + ch.Bearing.X * Scale; + float yrel = (ch.Size.Y - ch.Bearing.Y) * Scale; + yrel += hhh; + char_x += (ch.Advance >> 6) * Scale; + + Matrix4 scaleM = Matrix4.CreateScale(new Vector3(w, h, 1.0f)); + Matrix4 transRelM = Matrix4.CreateTranslation(new Vector3(xrel, yrel, 0.0f)); + + Matrix4 modelM = scaleM * transRelM * rotateM * transOriginM; + GL.UniformMatrix4(0, false, ref modelM); + + ch.Texture.Use(); + + GL.DrawArrays(PrimitiveType.Triangles, 0, 6); + } + } + + GL.Disable(EnableCap.Blend); + } + } + public Window? Window { get; private set; } + + public void LoadToParent(IParent window, Window win) + { + Parent = window; + Window = win; + //X = window.FloatToInt(X, window.Size.X); + //Y = window.FloatToInt(Y, window.Size.Y, true); + Library lib = new(); + + Face face = new(lib, Font.GetData(), 0); + face.SetPixelSizes(0, PixelHeight); + GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1); + face.SelectCharmap(Encoding.Unicode); + + GL.PixelStore(PixelStoreParameter.UnpackAlignment, 4); + + float[] vquad = + { + 0.0f, -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 0.0f, 1.0f, 1.0f, + 0.0f, -1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 1.0f, 0.0f + }; + + VBO = GL.GenBuffer(); + GL.BindBuffer(BufferTarget.ArrayBuffer, VBO); + GL.BufferData(BufferTarget.ArrayBuffer, 4 * 6 * 4, vquad, BufferUsageHint.StaticDraw); + + VAO = GL.GenVertexArray(); + if (!_characters.ContainsKey(this)) _characters.Add(this, new Dictionary()); + foreach (char character in Text) + { + if (_characters[this].ContainsKey(character) == false) + { + var f = new Texture(this, character, PixelHeight, face); + f.LoadText(); + } + } + GL.BindVertexArray(VAO); + GL.EnableVertexAttribArray(0); + GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 4 * 4, 0); + GL.EnableVertexAttribArray(1); + GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 4 * 4, 2 * 4); + + GL.BindBuffer(BufferTarget.ArrayBuffer, 0); + GL.BindVertexArray(0); + Text = text; + loadd = true; + Location = Location; + Distance = new(window.Size.X - Size.X - Location.X, window.Size.Y - Size.Y - Location.Y); + } + + private bool loadd = false; + + public event Func? Clicked; + + public bool Loaded => loadd; +} diff --git a/GraphicsManager/Objects/Rectangle.cs b/GraphicsManager/Objects/Rectangle.cs new file mode 100644 index 0000000..feafdaa --- /dev/null +++ b/GraphicsManager/Objects/Rectangle.cs @@ -0,0 +1,212 @@ +using GraphicsManager.Enums; +using GraphicsManager.Interfaces; +using GraphicsManager.Objects.Core; +using OpenTK.Graphics.OpenGL4; +using OpenTK.Mathematics; + +namespace GraphicsManager.Objects; + +public class Rectangle : ITextureObject +{ + public static readonly Shader DefaultShader = new("Rectangle", true); + + public ObjectAnchor Anchor { get; set; } = ObjectAnchor.Left | ObjectAnchor.Top; + + public Texture? Texture { get; private set; } + + public Rectangle(Texture? texture = null) + { + Texture = texture; + if (Points_ is null) + { + bool tex = (Texture is null); + Points_ = new float[(tex ? 12 : 20)]; + if (!tex) + { + Points[3] = 1.0f; + Points[4] = 1.0f; + Points[8] = 1.0f; + Points[19] = 1.0f; + } + } + } + + public Vector4 Color { get; set; } + + public bool Visible { get; set; } = true; + + public void Draw() + { + if (Visible) + { + if (Texture is not null) Texture.Use(); + Shader.Use(); + GL.Uniform4(0, Color); + if (Texture is not null) + { + GL.Enable(EnableCap.Blend); + GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); + GL.BlendFunc(0, BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); + } + GL.BindVertexArray(ArrayObject); + GL.DrawElements(PrimitiveType.Triangles, Indexs.Length, DrawElementsType.UnsignedInt, 0); + if (Texture is not null) GL.Disable(EnableCap.Blend); + } + } + + public void Clean() + { + GL.BindBuffer(BufferTarget.ArrayBuffer, 0); + GL.DeleteBuffer(BufferObject); + } + + public void LoadToParent(IParent Parent, Window Window) + { + this.Parent = Parent; + this.Window = Window; + int pos = Points.Length - 3; + if (Texture is not null) pos -= 2; + pos = 4; + if (Texture is not null) pos += 2; + + BufferObject = GL.GenBuffer(); + GL.BindBuffer(BufferTarget.ArrayBuffer, BufferObject); + ArrayObject = GL.GenVertexArray(); + GL.BindVertexArray(ArrayObject); + int add = 3; + if (Texture is not null) add = 5; + GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, add * sizeof(float), 0); + if (Texture is not null) + { + Shader = Texture.TextureShader; + Texture.Load(Shader.GetAttribLocation("aTexCoord")); + } + GL.EnableVertexAttribArray(0); + GL.BindBuffer(BufferTarget.ArrayBuffer, BufferObject); + GL.BufferData(BufferTarget.ArrayBuffer, Points.Length * sizeof(float), Points, Hint); + GL.BindVertexArray(ArrayObject); + ElementBufferObject = GL.GenBuffer(); + GL.BindBuffer(BufferTarget.ElementArrayBuffer, ElementBufferObject); + GL.BufferData(BufferTarget.ElementArrayBuffer, Indexs.Length * sizeof(uint), Indexs, Hint); + load = true; + loadd = true; + + Window.MouseDown += Window_MouseDown; + Location = Location; + Distance = new(Parent.Size.X - Size.X - Location.X, Parent.Size.Y - Size.Y - Location.Y); + } + + public IParent? Parent { get; private set; } + public Window? Window { get; private set; } + + private void Window_MouseDown(OpenTK.Windowing.Common.MouseButtonEventArgs e) + { + if (Clicked is not null && e.Button == OpenTK.Windowing.GraphicsLibraryFramework.MouseButton.Button1 && Location.X <= Parent?.MousePosition.X && Size.X + Location.X >= Parent?.MousePosition.X && Location.Y + Size.Y >= Parent?.MousePosition.Y && Location.Y <= Parent?.MousePosition.Y) Clicked.Invoke(this); + } + + ~Rectangle() + { + GL.BindBuffer(BufferTarget.ArrayBuffer, 0); + GL.DeleteBuffer(BufferObject); + } + + public Shader Shader { get; set; } = DefaultShader; + public int ElementBufferObject { get; private set; } + public int BufferObject { get; private set; } + public int ArrayObject { get; private set; } + private float[] Points_; + private Vector2i size_ = new(), loc_ = new(); + bool load = false; + + public float[] Points + { + get + { + return Points_; + } + set + { + Points_ = value; + try + { + if (load) + { + int add = 3; + if (Texture is not null) add = 5; + GL.BindBuffer(BufferTarget.ArrayBuffer, BufferObject); + GL.BindVertexArray(ArrayObject); + GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, add * sizeof(float), 0); + GL.EnableVertexAttribArray(0); + GL.BindBuffer(BufferTarget.ArrayBuffer, BufferObject); + GL.BufferData(BufferTarget.ArrayBuffer, Points_.Length * sizeof(float), Points_, Hint); + GL.BindVertexArray(ArrayObject); + GL.BindBuffer(BufferTarget.ElementArrayBuffer, ElementBufferObject); + GL.BufferData(BufferTarget.ElementArrayBuffer, Indexs.Length * sizeof(uint), Indexs, Hint); + } + } + catch (AccessViolationException v) + { + Console.WriteLine(v.Message); + } + } + } + + public uint[] Indexs { get; set; } = new uint[6] { 0, 1, 3, 1, 2, 3 }; + public BufferUsageHint Hint { get; set; } = BufferUsageHint.StaticDraw; + + private bool loadd = false; + + public event Func? Clicked; + + public bool Loaded => loadd; + public Vector2i Distance { get; private set; } + + public Vector2i Size + { + get + { + return size_; + } + set + { + size_ = value; + if (Window is null || Parent is null) return; + float[] temp = Points; + saf = new Vector2(Window.IntToFloat(value.X + loc_.X + Parent.Position.X, false), Window.IntToFloat(value.Y + loc_.Y + Parent.Position.Y, true)); + temp[0] = saf.X; + temp[(Texture is null ? 3 : 5)] = saf.X; + temp[(Texture is null ? 4 : 6)] = saf.Y; + temp[(Texture is null ? 7 : 11)] = saf.Y; + Points = temp; + } + } + + public Vector2i Location + { + get + { + return loc_; + } + set + { + loc_ = value; + if (Window is null || Parent is null) return; + float[] temp = Points; + laf = new Vector2(Window.IntToFloat(value.X + Parent.Position.X, false), Window.IntToFloat(value.Y + Parent.Position.Y, true)); + temp[(Texture is null ? 6 : 10)] = laf.X; + temp[(Texture is null ? 9 : 15)] = laf.X; + temp[1] = laf.Y; + temp[(Texture is null ? 10 : 16)] = laf.Y; + saf = new Vector2(Window.IntToFloat(Size.X + value.X + Parent.Position.X, false), Window.IntToFloat(Size.Y + value.Y + Parent.Position.Y, true)); + temp[0] = saf.X; + temp[(Texture is null ? 3 : 5)] = saf.X; + temp[(Texture is null ? 4 : 6)] = saf.Y; + temp[(Texture is null ? 7 : 11)] = saf.Y; + Points = temp; + } + } + private Vector2 laf = new(), saf = new(); + + public Vector2 LocationAsFloat { get { return laf; } } + public Vector2 SizeAsFloat { get { return saf; } } +} diff --git a/GraphicsManager/Objects/Textbox.cs b/GraphicsManager/Objects/Textbox.cs new file mode 100644 index 0000000..fa56553 --- /dev/null +++ b/GraphicsManager/Objects/Textbox.cs @@ -0,0 +1,121 @@ +using GraphicsManager.Enums; +using GraphicsManager.Interfaces; +using GraphicsManager.Objects.Core; +using OpenTK.Mathematics; +using OpenTK.Windowing.GraphicsLibraryFramework; + +namespace GraphicsManager.Objects; + +public class Textbox : ITextureObject +{ + public event Func? Clicked; + public Rectangle Bounds; + public Label Label; + public IParent Parent { get; private set; } + public Textbox(Texture texture = null!) + { + Bounds = new Rectangle(texture ?? new Texture(Tools.GetResourceBytes("GraphicsManager.Resources.Textures.Textbox.png"))); + Label = new Label() + { + Text = nameof(Textbox) + }; + } + public Vector2i Size { get { return Bounds.Size; } set { Bounds.Size = value; } } + public Vector2i Location { get { return Bounds.Location; } set { Bounds.Location = value; } } + public Vector2i Distance { get { return Bounds.Distance; }} + public Texture Texture => Bounds.Texture!; + + public string Text { get => Label.Text; set => Label.Text = value; } + public Font Font { get => Label.Font; set => Label.Font = value; } + public Vector4 BackgroundColor { get => Bounds.Color; set => Bounds.Color = value; } + public float[] Points + { + get + { + return Bounds.Points; + } + set + { + if (Label is not null && Parent is not null) + { + Label.Location = new Vector2i((int)Parent.FloatToInt(value[10] + ((-1 - Parent.IntToFloat(10)) * -1)), (int)Parent.FloatToInt(value[6] + (-1 - Parent.IntToFloat(5)) * -1, true)); + } + Bounds.Points = value; + } + } + + public ObjectAnchor Anchor { get; set; } = ObjectAnchor.Left | ObjectAnchor.Top; + + private bool _loaded = false; + public bool Loaded => _loaded; + + public bool Visible { get; set; } = true; + + public void Clean() + { + Bounds.Clean(); + Label.Clean(); + } + + public void Draw() + { + Bounds.Draw(); + Label.Draw(); + } + public Window? Window { get; private set; } + public void LoadToParent(IParent Parent, Window Window) + { + this.Parent = Parent; + this.Window = Window; + this.Parent.MouseDown += Parrent_MouseDown; + this.Parent.KeyDown += Parrent_KeyDown; + Bounds.LoadToParent(Parent, Window); + Points = Points; + Label.LoadToParent(Parent, Window); + _loaded = true; + } + + private void Parrent_KeyDown(OpenTK.Windowing.Common.KeyboardKeyEventArgs obj) + { + if (use) + { + if (obj.Key == Keys.CapsLock || obj.Key == Keys.Menu || obj.Key == Keys.LeftSuper || obj.Key == Keys.RightSuper || obj.Key == Keys.End || obj.Key == Keys.Home || obj.Key == Keys.PageDown || obj.Key == Keys.PageUp || obj.Key == Keys.Insert || obj.Key == Keys.Up || obj.Key == Keys.Down || obj.Key == Keys.Left || obj.Key == Keys.Right) return; + if (obj.Key == Keys.Backspace) + { + if (!(Text.Length > 0)) return; + Text = Text.Remove(Text.Length - 1, 1); + } + else if (obj.Key == Keys.Delete) + { + if (!(Text.Length > 0)) return; + Text = Text.Remove(Text.Length - 1, 1); + } + else if (obj.Shift) + { + if (obj.Key == Keys.Enter || obj.Key == Keys.KeyPadEnter) Text += '\n'; + else if (obj.Key == Keys.LeftShift || obj.Key == Keys.KeyPadEnter || obj.Key == Keys.Enter || obj.Key == Keys.End || obj.Key == Keys.Down) return; + else Text += ((char)obj.Key).ToString().ToUpper(); + } + else if (obj.Command || obj.Alt || obj.Control) { } + else + { + Text += ((char)obj.Key).ToString().ToLower(); + } + } + } + + bool use = false; + + private void Parrent_MouseDown(OpenTK.Windowing.Common.MouseButtonEventArgs e) + { + if (e.Button == MouseButton.Button1 && Location.X <= Parent?.MousePosition.X && Size.X + Location.X >= Parent?.MousePosition.X && Location.Y + Size.Y >= Parent?.MousePosition.Y && Location.Y <= Parent?.MousePosition.Y) + { + use = true; + if (Clicked is not null) Clicked.Invoke(this); + } + else use = false; + } + + public Vector2 LocationAsFloat { get { return Bounds.LocationAsFloat; } } + public Vector2 SizeAsFloat { get { return Bounds.SizeAsFloat; } } +} diff --git a/GraphicsManager/Objects/UserControl.cs b/GraphicsManager/Objects/UserControl.cs new file mode 100644 index 0000000..8dc8604 --- /dev/null +++ b/GraphicsManager/Objects/UserControl.cs @@ -0,0 +1,183 @@ +using GraphicsManager.Enums; +using GraphicsManager.Interfaces; +using OpenTK.Mathematics; +using OpenTK.Windowing.Common; + +namespace GraphicsManager.Objects; + +public class UserControl : IRenderObject, IParent +{ + private Rectangle _bounds; + + public UserControl() + { + _bounds = new Rectangle(); + _bounds.Clicked += _bounds_Clicked; + } + + private Task _bounds_Clicked(IRenderObject arg) + { + _ = Clicked?.Invoke(arg)!; + return Task.CompletedTask; + } + + public ICollection Controls { get; } = new List(); + public ObjectAnchor Anchor { get => _bounds.Anchor; set => _bounds.Anchor = value; } + public Vector4 Color { get => _bounds.Color; set => _bounds.Color = value; } + public bool Visible { get => _bounds.Visible; set => _bounds.Visible = value; } + public Vector2i Size { get => _bounds.Size; set => _bounds.Size = value; } + public Vector2 SizeAsFloat { get => _bounds.SizeAsFloat; } + public Vector2i Location { get => _bounds.Location; set => _bounds.Location = value; } + public Vector2i Position => Location; + public Vector2 LocationAsFloat { get => _bounds.LocationAsFloat; } + public Vector2i Distance { get => _bounds.Distance; } + public event Func? Clicked; + public event Action MouseDown; + public event Action KeyDown; + + public IParent? Parent { get; private set; } + public Window? Window { get; private set; } + public bool Loaded { get; private set; } = false; + + public Vector2 MousePosition => throw new NotImplementedException(); + + public void LoadToParent(IParent Parent, Window Window) + { + this.Parent = Parent; + this.Window = Window; + _bounds.LoadToParent(Parent, Window); + foreach (IRenderObject obj in Controls) + { + obj.LoadToParent(this, Window); + } + Loaded = true; + } + + public void Draw() + { + if (Loaded) + { + _bounds.Draw(); + IEnumerable needload = Controls.Where(a => a.Loaded == false); + + if (needload.Any()) + { + foreach (IRenderObject Control in needload) + { + Control.LoadToParent(this, Window!); + } + } + foreach (IRenderObject Control in Controls) + { + Control.Draw(); + } + } + } + + public void Clean() + { + foreach (IRenderObject Control in Controls) + { + Control.Clean(); + } + _bounds.Clean(); + } + + public void Resize(ResizeEventArgs e) + { + if (e.Width == 0 && e.Height == 0) return; + foreach (IRenderObject Control in Controls) + { + if (Control.Loaded) + { + bool top = (Control.Anchor & ObjectAnchor.Top) == ObjectAnchor.Top; + bool left = (Control.Anchor & ObjectAnchor.Left) == ObjectAnchor.Left; + bool right = (Control.Anchor & ObjectAnchor.Right) == ObjectAnchor.Right; + bool bottom = (Control.Anchor & ObjectAnchor.Bottom) == ObjectAnchor.Bottom; + if (!top && !bottom) { Control.Anchor |= ObjectAnchor.Top; top = true; } + if (!left && !right) { Control.Anchor |= ObjectAnchor.Left; left = true; } + int lx = (left ? Control.Location.X : Size.X - Control.Distance.X - Control.Size.X); + int ly = (top ? Control.Location.Y : Size.Y - Control.Distance.Y - Control.Size.Y); + int sy = (bottom ? Size.Y - Control.Distance.Y - ly : Control.Size.Y); + int sx = (right ? Size.X - Control.Distance.X - lx : Control.Size.X); + Control.Size = new(sx, sy); + Control.Location = new(lx, ly); + } + } + } + + #region Cool Math Things + public float[] RctToFloat(int x, int y, int Width, int Height, bool hastexture = false, float z = 0.0f) + { + if (hastexture) + { + return new float[20] { + IntToFloat(x + Width), IntToFloat(y, true), z, 1.0f, 1.0f,// top r + IntToFloat(x + Width), IntToFloat(y + Height, true), z, 1.0f, 0.0f,//b r + IntToFloat(x), IntToFloat(y + Height, true), z, 0.0f, 0.0f,//bot l + IntToFloat(x), IntToFloat(y, true), z, 0.0f, 1.0f// top l + }; + } + else + { + return new float[12] { + IntToFloat(x + Width), IntToFloat(y, true), z,// top r + IntToFloat(x + Width), IntToFloat(y + Height, true), z, //b r + IntToFloat(x), IntToFloat(y + Height, true), z, //bot l + IntToFloat(x), IntToFloat(y, true), z,// top l + }; + } + } + + public float IntToFloat(int p, bool Invert = false) + { + int Size = (Invert ? this.Size.Y : this.Size.X); + double half = Math.Round((double)Size / (double)2, 1); + double Per = Math.Round((double)1 / half, 15); + if (p == half) return 0.0f; + if (Invert) + { + if (p > half) return (float)(((double)(p - half) * Per) * -1); + else return (float)(1 - (p * Per)); + } + else + { + if (p > half) return (float)((double)(p - half) * Per); + else return (float)((1 - (p * Per)) * -1); + } + } + + public float FloatToInt(float p, bool Invert = false) + { + int Size = (Invert ? this.Size.Y : this.Size.X); + double half = Math.Round((double)Size / (double)2, 15); + if (p == 0) return (int)half; + if (Invert) + { + if (p < 0) + { + p *= -1; + p++; + return (float)(half * p); + } + else + { + return (float)(half - (p * half)); + } + } + else + { + if (p < 0) + { + p *= -1; + p++; + return (float)(Size - (half * p)); + } + else + { + return (float)(p * half + half); + } + } + } + #endregion +} diff --git a/GraphicsManager/Resources/Fonts/TektonPro-Regular.otf b/GraphicsManager/Resources/Fonts/TektonPro-Regular.otf new file mode 100644 index 0000000..1c860f7 Binary files /dev/null and b/GraphicsManager/Resources/Fonts/TektonPro-Regular.otf differ diff --git a/GraphicsManager/Resources/Shaders/Label.frag b/GraphicsManager/Resources/Shaders/Label.frag new file mode 100644 index 0000000..b348052 --- /dev/null +++ b/GraphicsManager/Resources/Shaders/Label.frag @@ -0,0 +1,16 @@ +#version 460 + +in vec2 vUV; + +layout (binding=0) uniform sampler2D u_texture; + +layout (location = 2) uniform vec4 textColor; + +out vec4 fragColor; + +void main() +{ + vec2 uv = vUV.xy; + float text = texture(u_texture, uv).r; + fragColor = vec4(textColor.rgba*text); +} \ No newline at end of file diff --git a/GraphicsManager/Resources/Shaders/Label.vert b/GraphicsManager/Resources/Shaders/Label.vert new file mode 100644 index 0000000..d2ab3c8 --- /dev/null +++ b/GraphicsManager/Resources/Shaders/Label.vert @@ -0,0 +1,15 @@ +#version 460 + +layout (location = 0) in vec2 in_pos; +layout (location = 1) in vec2 in_uv; + +out vec2 vUV; + +layout (location = 0) uniform mat4 model; +layout (location = 1) uniform mat4 projection; + +void main() +{ + vUV = in_uv.xy; + gl_Position = projection * model * vec4(in_pos.xy, 0.0, 1.0); +} \ No newline at end of file diff --git a/GraphicsManager/Resources/Shaders/Rectangle.frag b/GraphicsManager/Resources/Shaders/Rectangle.frag new file mode 100644 index 0000000..c96630f --- /dev/null +++ b/GraphicsManager/Resources/Shaders/Rectangle.frag @@ -0,0 +1,9 @@ +#version 330 core +out vec4 FragColor; + +in vec4 vertexColor; + +void main() +{ + FragColor = vertexColor; +} \ No newline at end of file diff --git a/GraphicsManager/Resources/Shaders/Rectangle.vert b/GraphicsManager/Resources/Shaders/Rectangle.vert new file mode 100644 index 0000000..280766f --- /dev/null +++ b/GraphicsManager/Resources/Shaders/Rectangle.vert @@ -0,0 +1,10 @@ +#version 330 core +layout (location = 0) in vec3 aPos; +uniform vec4 objColor; +out vec4 vertexColor; + +void main() +{ + gl_Position = vec4(aPos, 1.0); + vertexColor = objColor; +} \ No newline at end of file diff --git a/GraphicsManager/Resources/Shaders/RectangleTexture.frag b/GraphicsManager/Resources/Shaders/RectangleTexture.frag new file mode 100644 index 0000000..981cfab --- /dev/null +++ b/GraphicsManager/Resources/Shaders/RectangleTexture.frag @@ -0,0 +1,8 @@ +#version 330 +out vec4 outputColor; +in vec2 texCoord; +uniform sampler2D texture1; +void main(void) +{ + outputColor = texture(texture1, texCoord); +} \ No newline at end of file diff --git a/GraphicsManager/Resources/Shaders/RectangleTexture.vert b/GraphicsManager/Resources/Shaders/RectangleTexture.vert new file mode 100644 index 0000000..013d06b --- /dev/null +++ b/GraphicsManager/Resources/Shaders/RectangleTexture.vert @@ -0,0 +1,9 @@ +#version 330 +layout(location = 0) in vec3 aPosition; +layout(location = 1) in vec2 aTexCoord; +out vec2 texCoord; +void main(void) +{ + texCoord = aTexCoord; + gl_Position = vec4(aPosition, 1.0); +} \ No newline at end of file diff --git a/GraphicsManager/Resources/Textures/Textbox.png b/GraphicsManager/Resources/Textures/Textbox.png new file mode 100644 index 0000000..995ac5d Binary files /dev/null and b/GraphicsManager/Resources/Textures/Textbox.png differ diff --git a/GraphicsManager/Structs/Character.cs b/GraphicsManager/Structs/Character.cs new file mode 100644 index 0000000..2325118 --- /dev/null +++ b/GraphicsManager/Structs/Character.cs @@ -0,0 +1,12 @@ +using GraphicsManager.Objects.Core; +using OpenTK.Mathematics; + +namespace GraphicsManager.Structs; + +public struct Character +{ + public Texture Texture { get; set; } + public Vector2 Size { get; set; } + public Vector2 Bearing { get; set; } + public int Advance { get; set; } +} diff --git a/GraphicsManager/Tools.cs b/GraphicsManager/Tools.cs new file mode 100644 index 0000000..ce3dad6 --- /dev/null +++ b/GraphicsManager/Tools.cs @@ -0,0 +1,31 @@ +using System.Reflection; + +namespace GraphicsManager; + +public class Tools +{ + public static byte[] GetResourceBytes(Assembly Assembly, string Resource) + { + Stream str = Assembly.GetManifestResourceStream(Resource)!; + MemoryStream ms = new(); + str.CopyTo(ms); + str.Dispose(); + byte[] result = ms.ToArray(); + ms.Dispose(); + return result; + } + + public static string GetResourceString(Assembly Assembly, string Resource) + { + Stream str = Assembly.GetManifestResourceStream(Resource)!; + StreamReader sr = new(str); + string result = sr.ReadToEnd(); + sr.Dispose(); + str.Dispose(); + return result; + } + + public static byte[] GetResourceBytes(string Resource) => GetResourceBytes(typeof(Tools).Assembly, Resource); + + public static string GetResourceString(string Resource) => GetResourceString(typeof(Tools).Assembly, Resource); +} diff --git a/GraphicsManager/Window.cs b/GraphicsManager/Window.cs new file mode 100644 index 0000000..01b7e56 --- /dev/null +++ b/GraphicsManager/Window.cs @@ -0,0 +1,165 @@ +using GraphicsManager.Enums; +using GraphicsManager.Interfaces; +using OpenTK.Graphics.OpenGL4; +using OpenTK.Mathematics; +using OpenTK.Windowing.Common; +using OpenTK.Windowing.Desktop; + +namespace GraphicsManager; + +public class Window : NativeWindow , IParent +{ + public Window(NativeWindowSettings nativeWindowSettings) : base(nativeWindowSettings) + { + } + + public Window() : base(new NativeWindowSettings()) + { + + } + public Vector2i Position { get; } = new Vector2i(0, 0); + public Color4 BackgroundColor { get; set; } = new Color4(0, 0, 0, 255); + + public ICollection Controls { get; } = new List(); +#region Cool Math Things + public float[] RctToFloat(int x, int y, int Width, int Height, bool hastexture = false, float z = 0.0f) + { + if (hastexture) + { + return new float[20] { + IntToFloat(x + Width), IntToFloat(y, true), z, 1.0f, 1.0f,// top r + IntToFloat(x + Width), IntToFloat(y + Height, true), z, 1.0f, 0.0f,//b r + IntToFloat(x), IntToFloat(y + Height, true), z, 0.0f, 0.0f,//bot l + IntToFloat(x), IntToFloat(y, true), z, 0.0f, 1.0f// top l + }; + } + else + { + return new float[12] { + IntToFloat(x + Width), IntToFloat(y, true), z,// top r + IntToFloat(x + Width), IntToFloat(y + Height, true), z, //b r + IntToFloat(x), IntToFloat(y + Height, true), z, //bot l + IntToFloat(x), IntToFloat(y, true), z,// top l + }; + } + } + + public float IntToFloat(int p, bool Invert = false) + { + int Size = (Invert ? this.Size.Y : this.Size.X); + double half = Math.Round((double)Size / (double)2, 1); + double Per = Math.Round((double)1 / half, 15); + if (p == half) return 0.0f; + if (Invert) + { + if (p > half) return (float)(((double)(p - half) * Per) * -1); + else return (float)(1 - (p * Per)); + } + else + { + if (p > half) return (float)((double)(p - half) * Per); + else return (float)((1 - (p * Per)) * -1); + } + } + + public float FloatToInt(float p, bool Invert = false) + { + int Size = (Invert ? this.Size.Y : this.Size.X); + double half = Math.Round((double)Size / (double)2, 15); + if (p == 0) return (int)half; + if (Invert) + { + if (p < 0) + { + p *= -1; + p++; + return (float)(half * p); + } + else + { + return (float)(half - (p * half)); + } + } + else + { + if (p < 0) + { + p *= -1; + p++; + return (float)(Size - (half * p)); + } + else + { + return (float)(p * half + half); + } + } + } +#endregion + + public void Resize(ResizeEventArgs e) + { + if (e.Width == 0 && e.Height == 0) return; + base.OnResize(e); + GL.Viewport(0, 0, e.Width, e.Height); + foreach (IRenderObject Control in Controls) + { + if (Control.Loaded) + { + bool top = (Control.Anchor & ObjectAnchor.Top) == ObjectAnchor.Top; + bool left = (Control.Anchor & ObjectAnchor.Left) == ObjectAnchor.Left; + bool right = (Control.Anchor & ObjectAnchor.Right) == ObjectAnchor.Right; + bool bottom = (Control.Anchor & ObjectAnchor.Bottom) == ObjectAnchor.Bottom; + if (!top && !bottom) { Control.Anchor |= ObjectAnchor.Top; top = true; } + if (!left && !right) { Control.Anchor |= ObjectAnchor.Left; left = true; } + int lx = (left ? Control.Location.X : Size.X - Control.Distance.X - Control.Size.X); + int ly = (top ? Control.Location.Y : Size.Y - Control.Distance.Y - Control.Size.Y); + int sy = (bottom ? Size.Y - Control.Distance.Y - ly : Control.Size.Y); + int sx = (right ? Size.X - Control.Distance.X - lx : Control.Size.X); + Control.Size = new(sx, sy); + Control.Location = new(lx, ly); + if (Control is IParent) + { + IParent parent = (IParent)Control; + parent.Resize(e); + } + } + } + } + + protected override void OnResize(ResizeEventArgs e) + { + Resize(e); + } + + public void StartRender() + { + Context.MakeCurrent(); + while (Exists && IsVisible && !IsExiting) + { + DrawFrame(); + } + } + + public void DrawFrame() + { + ProcessEvents(); + GL.ClearColor(BackgroundColor.R, BackgroundColor.G, BackgroundColor.B, (BackgroundColor.A * -1) + 1); + IEnumerable needload = Controls.Where(a => a.Loaded == false); + + if (needload.Any()) + { + foreach (IRenderObject obj in needload) + { + obj.LoadToParent(this, this); + } + } + + GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); + foreach (IRenderObject obj in Controls) + { + if (obj.Loaded) obj.Draw(); + } + Context.SwapBuffers(); + Thread.Sleep(8); + } +} \ No newline at end of file