Skip to content

Commit 40eb107

Browse files
author
LoneWandererProductions
committed
Sync back my render Engine
1 parent 4d79297 commit 40eb107

23 files changed

Lines changed: 2222 additions & 782 deletions

RenderEngine/Batched2DRenderer.cs

Lines changed: 372 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,372 @@
1+
/*
2+
* COPYRIGHT: See COPYING in the top level directory
3+
* PROJECT: RenderEngine;
4+
* FILE: Batched2DRenderer.cs
5+
* PURPOSE: Your file purpose here
6+
* PROGRAMMER: Peter Geinitz (Wayfarer)
7+
*/
8+
9+
using System;
10+
using System.Collections.Generic;
11+
using System.Drawing;
12+
using OpenTK.Graphics.OpenGL4;
13+
14+
namespace RenderEngine;
15+
16+
public sealed class Batched2DRenderer : IDisposable
17+
{
18+
private readonly int _width;
19+
private readonly int _height;
20+
21+
private int _vboSolid;
22+
private int _vaoSolid;
23+
private readonly List<Vertex> _solidVertices = new();
24+
25+
private int _vboTex;
26+
private int _vaoTex;
27+
private readonly List<Vertex> _texVertices = new();
28+
29+
private int _solidShader;
30+
private int _textureShader;
31+
32+
private bool _initialized;
33+
34+
private struct Vertex
35+
{
36+
public float X, Y;
37+
public float R, G, B, A;
38+
public float U, V;
39+
public float TexIndex;
40+
}
41+
42+
public Batched2DRenderer(int width, int height)
43+
{
44+
_width = width;
45+
_height = height;
46+
47+
Test_DrawFullscreenQuad(width, height);
48+
}
49+
50+
private void EnsureInitialized()
51+
{
52+
if (_initialized) return;
53+
54+
// --- Solid VAO/VBO ---
55+
_vaoSolid = GL.GenVertexArray();
56+
_vboSolid = GL.GenBuffer();
57+
GL.BindVertexArray(_vaoSolid);
58+
GL.BindBuffer(BufferTarget.ArrayBuffer, _vboSolid);
59+
GL.BufferData(BufferTarget.ArrayBuffer, 1024 * 1024, IntPtr.Zero, BufferUsageHint.DynamicDraw);
60+
var strideSolid = 6 * sizeof(float);
61+
GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, strideSolid, 0);
62+
GL.EnableVertexAttribArray(0);
63+
GL.VertexAttribPointer(1, 4, VertexAttribPointerType.Float, false, strideSolid, 2 * sizeof(float));
64+
GL.EnableVertexAttribArray(1);
65+
GL.BindVertexArray(0);
66+
67+
// --- Textured VAO/VBO ---
68+
_vaoTex = GL.GenVertexArray();
69+
_vboTex = GL.GenBuffer();
70+
GL.BindVertexArray(_vaoTex);
71+
GL.BindBuffer(BufferTarget.ArrayBuffer, _vboTex);
72+
GL.BufferData(BufferTarget.ArrayBuffer, 1024 * 1024, IntPtr.Zero, BufferUsageHint.DynamicDraw);
73+
var strideTex = 9 * sizeof(float);
74+
GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, strideTex, 0);
75+
GL.EnableVertexAttribArray(0);
76+
GL.VertexAttribPointer(1, 4, VertexAttribPointerType.Float, false, strideTex, 2 * sizeof(float));
77+
GL.EnableVertexAttribArray(1);
78+
GL.VertexAttribPointer(2, 2, VertexAttribPointerType.Float, false, strideTex, 6 * sizeof(float));
79+
GL.EnableVertexAttribArray(2);
80+
GL.VertexAttribPointer(3, 1, VertexAttribPointerType.Float, false, strideTex, 8 * sizeof(float));
81+
GL.EnableVertexAttribArray(3);
82+
GL.BindVertexArray(0);
83+
84+
// --- Shaders ---
85+
_solidShader = CompileShader(SolidVertex(), SolidFragment());
86+
_textureShader = CompileShader(TextureVertex(), TextureFragment());
87+
88+
_initialized = true;
89+
}
90+
91+
// Minimal test — renders a fullscreen red quad using its own VAO/VBO/shader (no batching)
92+
void Test_DrawFullscreenQuad(int width, int height)
93+
{
94+
// create simple shader program (compile once; re-create each test is fine)
95+
var vs = @"
96+
#version 410 core
97+
layout(location = 0) in vec2 aPos;
98+
void main() {
99+
// positions are already in clip space
100+
gl_Position = vec4(aPos, 0.0, 1.0);
101+
}";
102+
var fs = @"
103+
#version 410 core
104+
out vec4 FragColor;
105+
void main() { FragColor = vec4(1.0, 0.0, 0.0, 1.0); }"; // solid red
106+
107+
var v = GL.CreateShader(ShaderType.VertexShader);
108+
GL.ShaderSource(v, vs);
109+
GL.CompileShader(v);
110+
GL.GetShader(v, ShaderParameter.CompileStatus, out var okv);
111+
if (okv == 0) throw new Exception("VS: " + GL.GetShaderInfoLog(v));
112+
113+
var f = GL.CreateShader(ShaderType.FragmentShader);
114+
GL.ShaderSource(f, fs);
115+
GL.CompileShader(f);
116+
GL.GetShader(f, ShaderParameter.CompileStatus, out var okf);
117+
if (okf == 0) throw new Exception("FS: " + GL.GetShaderInfoLog(f));
118+
119+
var prog = GL.CreateProgram();
120+
GL.AttachShader(prog, v);
121+
GL.AttachShader(prog, f);
122+
GL.LinkProgram(prog);
123+
GL.GetProgram(prog, GetProgramParameterName.LinkStatus, out var linkOk);
124+
if (linkOk == 0) throw new Exception("Link: " + GL.GetProgramInfoLog(prog));
125+
126+
GL.DeleteShader(v);
127+
GL.DeleteShader(f);
128+
129+
// Fullscreen quad in clip space (already -1..1)
130+
var verts = new float[]
131+
{
132+
-1f, 1f, // top-left
133+
1f, 1f, // top-right
134+
1f, -1f, // bottom-right
135+
136+
1f, -1f,
137+
-1f, -1f,
138+
-1f, 1f
139+
};
140+
141+
var vao = GL.GenVertexArray();
142+
var vbo = GL.GenBuffer();
143+
144+
GL.BindVertexArray(vao);
145+
GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
146+
GL.BufferData(BufferTarget.ArrayBuffer, verts.Length * sizeof(float), verts, BufferUsageHint.StaticDraw);
147+
GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 2 * sizeof(float), 0);
148+
GL.EnableVertexAttribArray(0);
149+
150+
// Render
151+
GL.Viewport(0, 0, width, height);
152+
GL.Disable(EnableCap.DepthTest);
153+
GL.ClearColor(Color.CornflowerBlue);
154+
GL.Clear(ClearBufferMask.ColorBufferBit);
155+
GL.UseProgram(prog);
156+
GL.BindVertexArray(vao);
157+
GL.DrawArrays(PrimitiveType.Triangles, 0, 6);
158+
159+
// Read any GL error
160+
var err = GL.GetError();
161+
162+
// cleanup
163+
GL.BindVertexArray(0);
164+
GL.DeleteBuffer(vbo);
165+
GL.DeleteVertexArray(vao);
166+
GL.DeleteProgram(prog);
167+
168+
// If err != NoError, capture it and log
169+
if (err != ErrorCode.NoError) throw new Exception("GL error: " + err.ToString());
170+
}
171+
172+
173+
#region Shaders
174+
175+
private string SolidVertex() => $@"
176+
#version 410 core
177+
layout(location=0) in vec2 aPos;
178+
layout(location=1) in vec4 aColor;
179+
out vec4 vColor;
180+
void main() {{
181+
vec2 pos = aPos / vec2({_width},{_height}) * 2.0 - 1.0;
182+
pos.y = -pos.y;
183+
gl_Position = vec4(pos,0,1);
184+
vColor = aColor;
185+
}}";
186+
187+
private string SolidFragment() => @"
188+
#version 410 core
189+
in vec4 vColor;
190+
out vec4 FragColor;
191+
void main() { FragColor = vColor; }";
192+
193+
private string TextureVertex() => $@"
194+
#version 410 core
195+
layout(location=0) in vec2 aPos;
196+
layout(location=1) in vec4 aColor;
197+
layout(location=2) in vec2 aTex;
198+
layout(location=3) in float aTexIndex;
199+
out vec2 vTex;
200+
out vec4 vColor;
201+
out float vTexIndex;
202+
void main() {{
203+
vec2 pos = aPos / vec2({_width},{_height}) * 2.0 - 1.0;
204+
pos.y = -pos.y;
205+
gl_Position = vec4(pos,0,1);
206+
vTex = aTex;
207+
vColor = aColor;
208+
vTexIndex = aTexIndex;
209+
}}";
210+
211+
private string TextureFragment() => @"
212+
#version 410 core
213+
in vec2 vTex;
214+
in vec4 vColor;
215+
in float vTexIndex;
216+
uniform sampler2D uTexture;
217+
out vec4 FragColor;
218+
void main() {
219+
vec4 texColor = texture(uTexture, vTex);
220+
FragColor = texColor * vColor;
221+
}";
222+
223+
#endregion
224+
225+
private int CompileShader(string vs, string fs)
226+
{
227+
var v = GL.CreateShader(ShaderType.VertexShader);
228+
GL.ShaderSource(v, vs);
229+
GL.CompileShader(v);
230+
GL.GetShader(v, ShaderParameter.CompileStatus, out var success);
231+
if (success == 0) throw new Exception(GL.GetShaderInfoLog(v));
232+
233+
var f = GL.CreateShader(ShaderType.FragmentShader);
234+
GL.ShaderSource(f, fs);
235+
GL.CompileShader(f);
236+
GL.GetShader(f, ShaderParameter.CompileStatus, out success);
237+
if (success == 0) throw new Exception(GL.GetShaderInfoLog(f));
238+
239+
var program = GL.CreateProgram();
240+
GL.AttachShader(program, v);
241+
GL.AttachShader(program, f);
242+
GL.LinkProgram(program);
243+
GL.GetProgram(program, GetProgramParameterName.LinkStatus, out success);
244+
if (success == 0) throw new Exception(GL.GetProgramInfoLog(program));
245+
246+
GL.DeleteShader(v);
247+
GL.DeleteShader(f);
248+
return program;
249+
}
250+
251+
private void AddSolidVertex(float x, float y, float r, float g, float b, float a)
252+
=> _solidVertices.Add(new Vertex { X = x, Y = y, R = r, G = g, B = b, A = a });
253+
254+
private void AddTexVertex(float x, float y, float r, float g, float b, float a, float u, float v, float texId)
255+
=> _texVertices.Add(new Vertex { X = x, Y = y, R = r, G = g, B = b, A = a, U = u, V = v, TexIndex = texId });
256+
257+
#region Public Draw API
258+
259+
public void DrawSolidQuad(Point p0, Point p1, Point p2, Point p3, Color color)
260+
{
261+
EnsureInitialized();
262+
var r = color.R / 255f;
263+
var g = color.G / 255f;
264+
var b = color.B / 255f;
265+
var a = color.A / 255f;
266+
267+
AddSolidVertex(p0.X, p0.Y, r, g, b, a);
268+
AddSolidVertex(p1.X, p1.Y, r, g, b, a);
269+
AddSolidVertex(p2.X, p2.Y, r, g, b, a);
270+
271+
AddSolidVertex(p2.X, p2.Y, r, g, b, a);
272+
AddSolidVertex(p3.X, p3.Y, r, g, b, a);
273+
AddSolidVertex(p0.X, p0.Y, r, g, b, a);
274+
}
275+
276+
public void DrawTexturedQuad(Point p0, Point p1, Point p2, Point p3, int texId, float u0 = 0, float v0 = 0,
277+
float u1 = 1, float v1 = 1)
278+
{
279+
EnsureInitialized();
280+
AddTexVertex(p0.X, p0.Y, 1, 1, 1, 1, u0, v0, texId);
281+
AddTexVertex(p1.X, p1.Y, 1, 1, 1, 1, u1, v0, texId);
282+
AddTexVertex(p2.X, p2.Y, 1, 1, 1, 1, u1, v1, texId);
283+
284+
AddTexVertex(p2.X, p2.Y, 1, 1, 1, 1, u1, v1, texId);
285+
AddTexVertex(p3.X, p3.Y, 1, 1, 1, 1, u0, v1, texId);
286+
AddTexVertex(p0.X, p0.Y, 1, 1, 1, 1, u0, v0, texId);
287+
}
288+
289+
#endregion
290+
291+
public void Flush()
292+
{
293+
EnsureInitialized();
294+
295+
// --- Solid ---
296+
if (_solidVertices.Count > 0)
297+
{
298+
GL.BindVertexArray(_vaoSolid);
299+
GL.BindBuffer(BufferTarget.ArrayBuffer, _vboSolid);
300+
301+
var raw = new float[_solidVertices.Count * 6];
302+
for (var i = 0; i < _solidVertices.Count; i++)
303+
{
304+
var idx = i * 6;
305+
var v = _solidVertices[i];
306+
raw[idx + 0] = v.X;
307+
raw[idx + 1] = v.Y;
308+
raw[idx + 2] = v.R;
309+
raw[idx + 3] = v.G;
310+
raw[idx + 4] = v.B;
311+
raw[idx + 5] = v.A;
312+
}
313+
314+
GL.BufferSubData(BufferTarget.ArrayBuffer, IntPtr.Zero, raw.Length * sizeof(float), raw);
315+
316+
GL.UseProgram(_solidShader);
317+
GL.DrawArrays(PrimitiveType.Triangles, 0, _solidVertices.Count);
318+
_solidVertices.Clear();
319+
}
320+
321+
// --- Textured ---
322+
if (_texVertices.Count > 0)
323+
{
324+
GL.BindVertexArray(_vaoTex);
325+
GL.BindBuffer(BufferTarget.ArrayBuffer, _vboTex);
326+
327+
var raw = new float[_texVertices.Count * 9];
328+
for (var i = 0; i < _texVertices.Count; i++)
329+
{
330+
var idx = i * 9;
331+
var v = _texVertices[i];
332+
raw[idx + 0] = v.X;
333+
raw[idx + 1] = v.Y;
334+
raw[idx + 2] = v.R;
335+
raw[idx + 3] = v.G;
336+
raw[idx + 4] = v.B;
337+
raw[idx + 5] = v.A;
338+
raw[idx + 6] = v.U;
339+
raw[idx + 7] = v.V;
340+
raw[idx + 8] = v.TexIndex;
341+
}
342+
343+
GL.BufferSubData(BufferTarget.ArrayBuffer, IntPtr.Zero, raw.Length * sizeof(float), raw);
344+
345+
GL.UseProgram(_textureShader);
346+
// Bind first texture only (for simplicity)
347+
var texId = (int)_texVertices[0].TexIndex;
348+
GL.ActiveTexture(TextureUnit.Texture0);
349+
GL.BindTexture(TextureTarget.Texture2D, texId);
350+
var loc = GL.GetUniformLocation(_textureShader, "uTexture");
351+
GL.Uniform1(loc, 0);
352+
353+
GL.DrawArrays(PrimitiveType.Triangles, 0, _texVertices.Count);
354+
_texVertices.Clear();
355+
}
356+
357+
GL.BindVertexArray(0);
358+
}
359+
360+
public void Dispose()
361+
{
362+
if (!_initialized) return;
363+
364+
GL.DeleteVertexArray(_vaoSolid);
365+
GL.DeleteBuffer(_vboSolid);
366+
GL.DeleteVertexArray(_vaoTex);
367+
GL.DeleteBuffer(_vboTex);
368+
GL.DeleteProgram(_solidShader);
369+
GL.DeleteProgram(_textureShader);
370+
_initialized = false;
371+
}
372+
}

0 commit comments

Comments
 (0)