Removed Subrepository
This commit is contained in:
26
C#/OpenGLTutorial/Rendering/Cameras/Camera2D.cs
Normal file
26
C#/OpenGLTutorial/Rendering/Cameras/Camera2D.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using System.Numerics;
|
||||
using OpenGLTutorial.Rendering.Display;
|
||||
|
||||
namespace OpenGLTutorial.Rendering.Cameras {
|
||||
public class Camera2D {
|
||||
public Vector2 FocusPosition { get; set; }
|
||||
public float Zoom { get; set; }
|
||||
|
||||
public Camera2D(Vector2 focusPosition, float zoom) {
|
||||
FocusPosition = focusPosition;
|
||||
Zoom = zoom;
|
||||
}
|
||||
|
||||
public Matrix4x4 GetProjectionMatrix() {
|
||||
float left = FocusPosition.X - DisplayManager.WindowSize.Width / 2f;
|
||||
float right = FocusPosition.X + DisplayManager.WindowSize.Width / 2f;
|
||||
float top = FocusPosition.Y - DisplayManager.WindowSize.Height / 2f;
|
||||
float bottom = FocusPosition.Y + DisplayManager.WindowSize.Height / 2f;
|
||||
|
||||
Matrix4x4 orthoMatrix = Matrix4x4.CreateOrthographicOffCenter(left, right, bottom, top, 0.01f, 100.0f);
|
||||
Matrix4x4 zoomMatrix = Matrix4x4.CreateScale(Zoom);
|
||||
|
||||
return orthoMatrix * zoomMatrix;
|
||||
}
|
||||
}
|
||||
}
|
||||
66
C#/OpenGLTutorial/Rendering/Display/DisplayManager.cs
Normal file
66
C#/OpenGLTutorial/Rendering/Display/DisplayManager.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using GLFW;
|
||||
using OpenGLTutorial.GameLoop;
|
||||
using StbiSharp;
|
||||
using static OpenGL.GL;
|
||||
using Image = GLFW.Image;
|
||||
|
||||
namespace OpenGLTutorial.Rendering.Display {
|
||||
public static class DisplayManager {
|
||||
|
||||
public static NativeWindow Window { get; set; }
|
||||
public static Size WindowSize { get; set; }
|
||||
|
||||
public static void CreateWindow(int width, int height, string title, bool vsync) {
|
||||
WindowSize = new Size(width, height);
|
||||
Glfw.Init();
|
||||
|
||||
Glfw.WindowHint(Hint.ContextVersionMajor, 3);
|
||||
Glfw.WindowHint(Hint.ContextVersionMinor, 3);
|
||||
Glfw.WindowHint(Hint.OpenglProfile, Profile.Core);
|
||||
|
||||
Glfw.WindowHint(Hint.Focused, true);
|
||||
Glfw.WindowHint(Hint.Resizable, true);
|
||||
|
||||
Window = new NativeWindow(width, height, title);
|
||||
Window.CenterOnScreen();
|
||||
Window.MakeCurrent();
|
||||
Import(Glfw.GetProcAddress);
|
||||
glViewport(0, 0, width, height);
|
||||
Glfw.SwapInterval(vsync ? 1 : 0);
|
||||
|
||||
Window.SizeChanged += OnResize;
|
||||
Window.KeyAction += OnKey;
|
||||
Window.MouseButton += OnMouse;
|
||||
Window.MouseMoved += OnMouseMove;
|
||||
Window.MouseScroll += OnMouseScroll;
|
||||
|
||||
}
|
||||
|
||||
private static void OnResize(object sender, SizeChangeEventArgs e) {
|
||||
WindowSize = e.Size;
|
||||
glViewport(0, 0, WindowSize.Width, WindowSize.Height);
|
||||
}
|
||||
|
||||
private static void OnKey(object sender, KeyEventArgs e) => Input.Keys[e.Key] = e.State != InputState.Release;
|
||||
|
||||
private static void OnMouse(object sender, MouseButtonEventArgs e) => Input.MouseButtons[e.Button] = e.Action != InputState.Release;
|
||||
private static void OnMouseMove(object sender, MouseMoveEventArgs e) => Input.MousePosition = e.Position;
|
||||
private static void OnMouseScroll(object sender, MouseMoveEventArgs e) => Input.MouseScroll = e.Position.Y;
|
||||
|
||||
public static unsafe void SetWindowIcon(this NativeWindow window, string file) {
|
||||
using var stream = File.OpenRead(file);
|
||||
using var memoryStream = new MemoryStream();
|
||||
stream.CopyTo(memoryStream);
|
||||
StbiImage image = Stbi.LoadFromMemory(memoryStream, 0);
|
||||
|
||||
fixed (byte* data = &image.Data[0]) {
|
||||
window.SetIcons(new Image(image.Width, image.Height, new IntPtr(data)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
154
C#/OpenGLTutorial/Rendering/Display/Shape.cs
Normal file
154
C#/OpenGLTutorial/Rendering/Display/Shape.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
using System.Drawing;
|
||||
using static OpenGL.GL;
|
||||
|
||||
namespace OpenGLTutorial.Rendering.Display {
|
||||
public class Shape {
|
||||
|
||||
public int Mode { get; set; }
|
||||
public uint Vao { get; private set; }
|
||||
private uint Vbo { get; set; }
|
||||
private float[] Vertices { get; set; }
|
||||
private bool _loaded = false;
|
||||
|
||||
public Shape(float[] vertices, int mode = GL_STATIC_DRAW) {
|
||||
Vertices = vertices;
|
||||
Mode = mode;
|
||||
}
|
||||
|
||||
public unsafe void Load() {
|
||||
if (_loaded || Vertices.Length == 0) return;
|
||||
_loaded = true;
|
||||
|
||||
Vao = glGenVertexArray();
|
||||
Vbo = glGenBuffer();
|
||||
|
||||
glBindVertexArray(Vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, Vbo);
|
||||
|
||||
fixed (float* v = &Vertices[0]) {
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * Vertices.Length, v, Mode);
|
||||
}
|
||||
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, false, 8 * sizeof(float), (void*)0);
|
||||
glEnableVertexAttribArray(0);
|
||||
|
||||
glVertexAttribPointer(1, 4, GL_FLOAT, false, 8 * sizeof(float), (void*)(2 * sizeof(float)));
|
||||
glEnableVertexAttribArray(1);
|
||||
|
||||
glVertexAttribPointer(2, 2, GL_FLOAT, false, 8 * sizeof(float), (void*)(6 * sizeof(float)));
|
||||
glEnableVertexAttribArray(2);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
public void Delete() {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
|
||||
glDeleteBuffer(Vbo);
|
||||
glDeleteVertexArray(Vao);
|
||||
}
|
||||
|
||||
public unsafe void SetVertices(float[] vertices) {
|
||||
Vertices = vertices;
|
||||
if (vertices.Length == 0) return;
|
||||
glBindVertexArray(Vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, Vbo);
|
||||
|
||||
fixed (float* v = &Vertices[0]) {
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * Vertices.Length, v, Mode);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
public int GetVertexCount() => Vertices.Length / 8;
|
||||
|
||||
}
|
||||
|
||||
public static class Shapes {
|
||||
|
||||
public static float[] Rectangle => new[] {
|
||||
-0.5f, 0.5f, 1f, 1f, 1f, 1f, 0.0f, 1.0f, // top left
|
||||
0.5f, 0.5f, 1f, 1f, 1f, 1f, 1.0f, 1.0f, // top right
|
||||
-0.5f, -0.5f, 1f, 1f, 1f, 1f, 0.0f, 0.0f, // bottom left
|
||||
|
||||
0.5f, 0.5f, 1f, 1f, 1f, 1f, 1.0f, 1.0f, // top right
|
||||
0.5f, -0.5f, 1f, 1f, 1f, 1f, 1.0f, 0.0f, // bottom right
|
||||
-0.5f, -0.5f, 1f, 1f, 1f, 1f, 0.0f, 0.0f, // bottom left
|
||||
};
|
||||
|
||||
public static float[] Triangle => new[] {
|
||||
0.0f, -0.5f, 1f, 1f, 1f, 1f, 0.5f, 1.0f, // top center
|
||||
0.5f, 0.5f, 1f, 1f, 1f, 1f, 1.0f, 0.0f, // bottom right
|
||||
-0.5f, 0.5f, 1f, 1f, 1f, 1f, 0.0f, 0.0f, // bottom left
|
||||
};
|
||||
|
||||
public static float[] CreateRectangle(Dimensions position, Color color, Dimensions uv) => new[] {
|
||||
position.X, position.MaxY, color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha(), uv.X, uv.MaxY, // top left
|
||||
position.MaxX, position.MaxY, color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha(), uv.MaxX, uv.MaxY, // top right
|
||||
position.X, position.Y, color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha(), uv.X, uv.Y, // bottom left
|
||||
|
||||
position.MaxX, position.MaxY, color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha(), uv.MaxX, uv.MaxY, // top right
|
||||
position.MaxX, position.Y, color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha(), uv.MaxX, uv.Y, // bottom right
|
||||
position.X, position.Y, color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha(), uv.X, uv.Y, // bottom left
|
||||
};
|
||||
|
||||
public static float[] CreateTriangle(Dimensions position, Color color, Dimensions uv) => new[] {
|
||||
position.CenterX, position.Y, color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha(), uv.CenterX, uv.MaxY, // top center
|
||||
position.MaxX, position.MaxY, color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha(), uv.MaxX, uv.Y, // bottom right
|
||||
position.X, position.MaxY, color.GetRed(), color.GetGreen(), color.GetBlue(), color.GetAlpha(), uv.X, uv.Y, // bottom left
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
public readonly struct Dimensions {
|
||||
public static Dimensions Identity => new (0.0f, 0.0f, 1.0f, 1.0f);
|
||||
public static Dimensions Centered => new (-0.5f, -0.5f, 1.0f, 1.0f);
|
||||
|
||||
public float X { get; }
|
||||
public float Y { get; }
|
||||
public float Width { get; }
|
||||
public float Height { get; }
|
||||
|
||||
public Dimensions(float x, float y, float width, float height) {
|
||||
X = x;
|
||||
Y = y;
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
|
||||
public float MaxX => X + Width;
|
||||
public float MaxY => Y + Height;
|
||||
|
||||
public float CenterX => X + (Width / 2.0f);
|
||||
public float CenterY => Y + (Height / 2.0f);
|
||||
}
|
||||
|
||||
public static class ColorExtensions {
|
||||
|
||||
private static float Map(float value, float min, float max, float mMin, float mMax) {
|
||||
float norm = (value - min) / (max - min);
|
||||
return (mMax - mMin) * norm + mMin;
|
||||
}
|
||||
|
||||
public static float GetRed(this Color color) {
|
||||
return Map(color.R, 0, 255, 0, 1);
|
||||
}
|
||||
|
||||
public static float GetGreen(this Color color) {
|
||||
return Map(color.G, 0, 255, 0, 1);
|
||||
}
|
||||
|
||||
public static float GetBlue(this Color color) {
|
||||
return Map(color.B, 0, 255, 0, 1);
|
||||
}
|
||||
|
||||
public static float GetAlpha(this Color color) {
|
||||
return Map(color.A, 0, 255, 0, 1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
73
C#/OpenGLTutorial/Rendering/Display/Texture.cs
Normal file
73
C#/OpenGLTutorial/Rendering/Display/Texture.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System.IO;
|
||||
using StbiSharp;
|
||||
using static OpenGL.GL;
|
||||
|
||||
namespace OpenGLTutorial.Rendering.Display {
|
||||
public class Texture {
|
||||
|
||||
public static Texture Empty => new(null);
|
||||
|
||||
public int Width { get; private set; }
|
||||
public int Height { get; private set; }
|
||||
|
||||
private uint Address { get; set; }
|
||||
private string Path { get; }
|
||||
private bool _loaded = false;
|
||||
|
||||
public Texture(string path) {
|
||||
Path = path;
|
||||
}
|
||||
|
||||
public unsafe void Load() {
|
||||
if (_loaded) return;
|
||||
_loaded = true;
|
||||
Address = glGenTexture();
|
||||
glBindTexture(GL_TEXTURE_2D, Address);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
if (string.IsNullOrEmpty(Path)) {
|
||||
LoadEmpty();
|
||||
return;
|
||||
}
|
||||
|
||||
using var stream = File.OpenRead(Path);
|
||||
using var memoryStream = new MemoryStream();
|
||||
stream.CopyTo(memoryStream);
|
||||
StbiImage image = Stbi.LoadFromMemory(memoryStream, 0);
|
||||
int format = image.NumChannels == 3 ? GL_RGB : GL_RGBA;
|
||||
fixed(byte* data = &image.Data[0])
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, format, image.Width, image.Height, 0, format, GL_UNSIGNED_BYTE, data);
|
||||
Width = image.Width;
|
||||
Height = image.Height;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
private unsafe void LoadEmpty() {
|
||||
byte[] pixels = {255, 255, 255, 255};
|
||||
|
||||
fixed (byte* data = &pixels[0]) {
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
}
|
||||
|
||||
Width = 1;
|
||||
Height = 1;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
public void Use() {
|
||||
glBindTexture(GL_TEXTURE_2D, Address);
|
||||
}
|
||||
|
||||
public void Delete() {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glDeleteTexture(Address);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace OpenGLTutorial.Rendering.Objects.Components {
|
||||
public interface IComponent {
|
||||
public void Load(GameObject thisObject);
|
||||
public void Update(GameObject thisObject);
|
||||
public void Destroy(GameObject thisObject);
|
||||
public void Render(GameObject thisObject);
|
||||
}
|
||||
}
|
||||
173
C#/OpenGLTutorial/Rendering/Objects/Components/TextComponent.cs
Normal file
173
C#/OpenGLTutorial/Rendering/Objects/Components/TextComponent.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
#pragma warning disable CA1416
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Drawing.Text;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using OpenGLTutorial.Rendering.Display;
|
||||
using static OpenGL.GL;
|
||||
|
||||
namespace OpenGLTutorial.Rendering.Objects.Components {
|
||||
|
||||
[Serializable]
|
||||
public class TextSettings {
|
||||
public int GlyphsPerLine { get; set; } = 16;
|
||||
public int GlyphLineCount { get; set; } = 16;
|
||||
public int GlyphWidth { get; set; } = 30;
|
||||
public int GlyphHeight { get; set; } = 48;
|
||||
|
||||
public float CharXSpacing { get; set; } = 0.0f;
|
||||
|
||||
// Used to offset rendering glyphs to bitmap
|
||||
public int AtlasOffsetX { get; set; } = -3;
|
||||
public int AtlasOffsetY { get; set; } = -1;
|
||||
public int FontSize { get; set; } = 35;
|
||||
public bool BitmapFont { get; set; } = false;
|
||||
public float TexCoordOffset { get; set; } = 0.005f;
|
||||
|
||||
public void Save(string file) {
|
||||
string json = JsonSerializer.Serialize(this, new JsonSerializerOptions {WriteIndented = true});
|
||||
File.WriteAllText(file, json, Encoding.UTF8);
|
||||
}
|
||||
|
||||
public static TextSettings Load(string file) {
|
||||
string text = File.ReadAllText(file, Encoding.UTF8);
|
||||
return JsonSerializer.Deserialize<TextSettings>(text);
|
||||
}
|
||||
}
|
||||
|
||||
public class TextComponent : IComponent {
|
||||
|
||||
public static TextSettings CompileFont(string fontNameOrUrl, string output, TextSettings settings = null) {
|
||||
if (settings is null)
|
||||
settings = new TextSettings();
|
||||
|
||||
int bitmapWidth = settings.GlyphsPerLine * settings.GlyphWidth;
|
||||
int bitmapHeight = settings.GlyphLineCount * settings.GlyphHeight;
|
||||
|
||||
using Bitmap texture = new Bitmap(bitmapWidth, bitmapHeight, PixelFormat.Format32bppArgb);
|
||||
Font font;
|
||||
if (File.Exists(fontNameOrUrl))
|
||||
{
|
||||
var collection = new PrivateFontCollection();
|
||||
collection.AddFontFile(fontNameOrUrl);
|
||||
var fontFamily = new FontFamily(Path.GetFileNameWithoutExtension(fontNameOrUrl), collection);
|
||||
font = new Font(fontFamily, settings.FontSize);
|
||||
}
|
||||
else {
|
||||
font = new Font(new FontFamily(fontNameOrUrl), settings.FontSize);
|
||||
}
|
||||
|
||||
using (var g = Graphics.FromImage(texture)) {
|
||||
if (settings.BitmapFont) {
|
||||
g.SmoothingMode = SmoothingMode.None;
|
||||
g.TextRenderingHint = TextRenderingHint.SingleBitPerPixel;
|
||||
}
|
||||
else {
|
||||
g.SmoothingMode = SmoothingMode.HighQuality;
|
||||
g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
|
||||
}
|
||||
|
||||
for (int p = 0; p < settings.GlyphLineCount; p++) {
|
||||
for (int n = 0; n < settings.GlyphsPerLine; n++) {
|
||||
char c = (char)(n + p * settings.GlyphsPerLine);
|
||||
g.DrawString(c.ToString(), font, Brushes.White,
|
||||
n * settings.GlyphWidth + settings.AtlasOffsetX,
|
||||
p * settings.GlyphHeight + settings.AtlasOffsetY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
texture.Save(output);
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
public string Text { get; set; }
|
||||
public Vector2 Size { get; private set; }
|
||||
|
||||
public Color TextColor { get; set; } = Color.White;
|
||||
private Shape Shape { get; set; }
|
||||
private Texture Texture { get; set; }
|
||||
private TextSettings Settings { get; set; }
|
||||
private string CurrentText { get; set; }
|
||||
|
||||
public TextComponent(string fontImage, string text, TextSettings settings = null, int renderingMode = GL_STATIC_DRAW) {
|
||||
Texture = new Texture(fontImage);
|
||||
Shape = new Shape(Shapes.Rectangle, renderingMode);
|
||||
Settings = settings is not null ? settings : new TextSettings();
|
||||
}
|
||||
|
||||
public void Load(GameObject thisObject) {
|
||||
Texture.Load();
|
||||
Shape.Load();
|
||||
}
|
||||
|
||||
public void Update(GameObject thisObject) {
|
||||
if (CurrentText == Text) return;
|
||||
CurrentText = Text;
|
||||
List<float> vertices = new List<float>();
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
float uW = Settings.GlyphWidth / (float)Texture.Width;
|
||||
float vH = Settings.GlyphHeight / (float)Texture.Height;
|
||||
float w = Settings.GlyphWidth / (float)Settings.GlyphHeight;
|
||||
const float h = 1.0f;
|
||||
|
||||
for (int n = 0; n < Text.Length; n++) {
|
||||
char idx = Text[n];
|
||||
|
||||
if (idx == '\n') {
|
||||
y += 1;
|
||||
x = 0;
|
||||
continue;
|
||||
}
|
||||
if (idx == '\t') {
|
||||
x += (w + Settings.CharXSpacing) * 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
float u = (idx % Settings.GlyphsPerLine) * uW + Settings.TexCoordOffset;
|
||||
float v = (idx / Settings.GlyphsPerLine) * vH + Settings.TexCoordOffset;
|
||||
|
||||
vertices.AddRange(new[] {
|
||||
x, y + h, TextColor.R, TextColor.G, TextColor.B, TextColor.A, u, v + vH,
|
||||
x + w, y + h, TextColor.R, TextColor.G, TextColor.B, TextColor.A, u + uW, v + vH,
|
||||
x, y, TextColor.R, TextColor.G, TextColor.B, TextColor.A, u, v,
|
||||
|
||||
x + w, y, TextColor.R, TextColor.G, TextColor.B, TextColor.A, u + uW, v,
|
||||
x + w, y + h, TextColor.R, TextColor.G, TextColor.B, TextColor.A, u + uW, v + vH,
|
||||
x, y, TextColor.R, TextColor.G, TextColor.B, TextColor.A, u, v
|
||||
});
|
||||
|
||||
x += w + Settings.CharXSpacing;
|
||||
}
|
||||
|
||||
Shape.SetVertices(vertices.ToArray());
|
||||
Size = new Vector2(x, y);
|
||||
}
|
||||
|
||||
public void Destroy(GameObject thisObject) {
|
||||
Shape.Delete();
|
||||
Texture.Delete();
|
||||
}
|
||||
|
||||
public void Render(GameObject thisObject) {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
Texture.Use();
|
||||
|
||||
glBindVertexArray(Shape.Vao);
|
||||
glDrawArrays(GL_TRIANGLES, 0, Shape.GetVertexCount());
|
||||
glBindVertexArray(0);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
93
C#/OpenGLTutorial/Rendering/Objects/GameObject.cs
Normal file
93
C#/OpenGLTutorial/Rendering/Objects/GameObject.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using static OpenGL.GL;
|
||||
using OpenGLTutorial.Rendering.Cameras;
|
||||
using OpenGLTutorial.Rendering.Display;
|
||||
using OpenGLTutorial.Rendering.Objects.Components;
|
||||
using OpenGLTutorial.Rendering.Shaders;
|
||||
|
||||
namespace OpenGLTutorial.Rendering.Objects {
|
||||
public class GameObject {
|
||||
|
||||
public Shader Shader { get; set; }
|
||||
public Shape Shape { get; set; }
|
||||
public Texture Texture { get; set; }
|
||||
|
||||
public Vector2 Position { get; set; }
|
||||
public Vector2 Scale { get; set; }
|
||||
public float Rotation { get; set; }
|
||||
|
||||
private List<IComponent> _components;
|
||||
|
||||
public GameObject(Shader shader, Shape shape, Texture texture) {
|
||||
Position = Vector2.Zero;
|
||||
Scale = Vector2.One;
|
||||
Rotation = 0;
|
||||
|
||||
Shader = shader;
|
||||
Shape = shape;
|
||||
Texture = texture;
|
||||
|
||||
_components = new List<IComponent>();
|
||||
}
|
||||
|
||||
public void Load() {
|
||||
Shader?.Load();
|
||||
Shape?.Load();
|
||||
Texture?.Load();
|
||||
|
||||
_components.ForEach(component => component.Load(this));
|
||||
}
|
||||
|
||||
public void Update() {
|
||||
_components.ForEach(component => component.Update(this));
|
||||
}
|
||||
|
||||
public void Render(Camera2D cam, int renderMode = GL_TRIANGLES) {
|
||||
Matrix4x4 trans = Matrix4x4.CreateTranslation(Position.X, -Position.Y, 0);
|
||||
Matrix4x4 sca = Matrix4x4.CreateScale(Scale.X, Scale.Y, 1);
|
||||
Matrix4x4 rot = Matrix4x4.CreateRotationZ(Rotation);
|
||||
|
||||
Texture?.Use();
|
||||
Shader?.Use();
|
||||
Shader?.SetMatrix4X4("model", sca * rot * trans);
|
||||
Shader?.SetMatrix4X4("projection", cam.GetProjectionMatrix());
|
||||
|
||||
if (Shape?.Vao != null) {
|
||||
glBindVertexArray(Shape.Vao);
|
||||
glDrawArrays(renderMode, 0, Shape.GetVertexCount());
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
_components.ForEach(component => component.Render(this));
|
||||
}
|
||||
|
||||
public void Destroy() {
|
||||
_components.ForEach(component => component.Destroy(this));
|
||||
Shader?.Delete();
|
||||
Shape?.Delete();
|
||||
Texture?.Delete();
|
||||
}
|
||||
|
||||
public void AddComponent(IComponent component) => _components.Add(component);
|
||||
public T GetComponent<T>() {
|
||||
foreach (var component in _components) {
|
||||
if (component.GetType() == typeof(T)) return (T)component;
|
||||
}
|
||||
|
||||
throw new NullReferenceException("Object does not contain a component of type " + typeof(T));
|
||||
}
|
||||
public void RemoveComponent<T>() {
|
||||
foreach (var component in _components) {
|
||||
if (component.GetType() == typeof(T)) {
|
||||
_components.Remove(component);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new NullReferenceException("Object does not contain a component of type " + typeof(T));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
103
C#/OpenGLTutorial/Rendering/Shaders/Shader.cs
Normal file
103
C#/OpenGLTutorial/Rendering/Shaders/Shader.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using static OpenGL.GL;
|
||||
|
||||
namespace OpenGLTutorial.Rendering.Shaders {
|
||||
public class Shader {
|
||||
private readonly string _vertexCode;
|
||||
private readonly string _fragmentCode;
|
||||
private bool _loaded = false;
|
||||
|
||||
public uint Program { get; private set; }
|
||||
|
||||
public Shader(string vertexPath, string fragmentPath) {
|
||||
using (var vertexReader = new StreamReader(vertexPath, Encoding.UTF8))
|
||||
_vertexCode = vertexReader.ReadToEnd();
|
||||
|
||||
using (var fragmentReader = new StreamReader(fragmentPath, Encoding.UTF8))
|
||||
_fragmentCode = fragmentReader.ReadToEnd();
|
||||
}
|
||||
|
||||
public Shader(string shader, bool isFile = true) {
|
||||
using var reader = isFile ? new StreamReader(shader, Encoding.UTF8) : null;
|
||||
string source = isFile ? reader.ReadToEnd() : shader;
|
||||
|
||||
string[] splitString = Regex.Split(source, "(#type [a-zA-Z]+)");
|
||||
string firstPattern = splitString[1].Replace("#type ", "").Trim();
|
||||
string secondPattern = splitString[3].Replace("#type ", "").Trim();
|
||||
|
||||
if (firstPattern.ToLower().Equals("vertex"))
|
||||
_vertexCode = splitString[2];
|
||||
else if (firstPattern.ToLower().Equals("fragment"))
|
||||
_fragmentCode = splitString[2];
|
||||
else throw new IOException("Unexpected token '" + firstPattern + "' in '" + shader + "'");
|
||||
|
||||
if (secondPattern.ToLower().Equals("vertex"))
|
||||
_vertexCode = splitString[4];
|
||||
else if (secondPattern.ToLower().Equals("fragment"))
|
||||
_fragmentCode = splitString[4];
|
||||
else throw new IOException("Unexpected token '" + secondPattern + "' in '" + shader + "'");
|
||||
}
|
||||
|
||||
public void Load() {
|
||||
if (_loaded) return;
|
||||
_loaded = true;
|
||||
uint vs = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(vs, _vertexCode);
|
||||
glCompileShader(vs);
|
||||
|
||||
int[] status = glGetShaderiv(vs, GL_COMPILE_STATUS, 1);
|
||||
if (status[0] == 0) {
|
||||
string error = glGetShaderInfoLog(vs);
|
||||
throw new Exception("Error compiling vertex shader: " + error);
|
||||
}
|
||||
|
||||
uint fs = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(fs, _fragmentCode);
|
||||
glCompileShader(fs);
|
||||
|
||||
status = glGetShaderiv(fs, GL_COMPILE_STATUS, 1);
|
||||
if (status[0] == 0) {
|
||||
string error = glGetShaderInfoLog(fs);
|
||||
throw new Exception("Error compiling fragment shader: " + error);
|
||||
}
|
||||
|
||||
Program = glCreateProgram();
|
||||
glAttachShader(Program, vs);
|
||||
glAttachShader(Program, fs);
|
||||
|
||||
glLinkProgram(Program);
|
||||
|
||||
glDetachShader(Program, vs);
|
||||
glDetachShader(Program, fs);
|
||||
glDeleteShader(vs);
|
||||
glDeleteShader(fs);
|
||||
}
|
||||
|
||||
public void Use() {
|
||||
glUseProgram(Program);
|
||||
}
|
||||
|
||||
public void Delete() {
|
||||
glUseProgram(0);
|
||||
glDeleteProgram(Program);
|
||||
}
|
||||
|
||||
public void SetMatrix4X4(string uniformName, Matrix4x4 matrix) {
|
||||
int location = glGetUniformLocation(Program, uniformName);
|
||||
glUniformMatrix4fv(location, 1, false, GetMatrix4X4Values(matrix));
|
||||
}
|
||||
|
||||
private float[] GetMatrix4X4Values(Matrix4x4 m) {
|
||||
return new [] {
|
||||
m.M11, m.M12, m.M13, m.M14,
|
||||
m.M21, m.M22, m.M23, m.M24,
|
||||
m.M31, m.M32, m.M33, m.M34,
|
||||
m.M41, m.M42, m.M43, m.M44
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user