Files
Engine-Template/Platforms/Desktop/Engine.Integration.SDL3/Sdl3TriangleBatch.cs

270 lines
7.3 KiB
C#

using System;
using Engine.Core;
using Engine.Systems.Graphics;
using SDL3;
using Silk.NET.OpenGL;
namespace Engine.Integration.SDL3;
public class Sdl3TriangleBatch : Behaviour, ITriangleBatch, IFirstFrameUpdate
{
private GL gl = null!;
private readonly uint MaxVertices = 1024;
private uint vao;
private uint vbo;
private uint shader;
private uint vertexIndex = 0;
private VertexPositionColor[] vertices = new VertexPositionColor[1024];
private Matrix4x4 view = Matrix4x4.Identity;
private Matrix4x4 projection = Matrix4x4.Identity;
private Sdl3Window sdl3window = null!;
private ICamera? camera = null!;
private nint glContext;
public void FirstActiveFrame()
{
sdl3window = BehaviourController.GetRequiredBehaviourInParent<Sdl3Window>();
camera = BehaviourController.GetRequiredBehaviourInParent<ICamera>();
nint window = sdl3window.Window;
SDL.GLSetAttribute(SDL.GLAttr.ContextMajorVersion, 3);
SDL.GLSetAttribute(SDL.GLAttr.ContextMinorVersion, 3);
SDL.GLSetAttribute(SDL.GLAttr.ContextProfileMask, (int)SDL.GLProfile.Core);
SDL.GLSetAttribute(SDL.GLAttr.DoubleBuffer, 1);
glContext = SDL.GLCreateContext(window);
if (glContext == nint.Zero)
throw new Exception(SDL.GetError());
SDL.GLMakeCurrent(window, glContext);
gl = GL.GetApi(procName => SDL.GLGetProcAddress(procName));
gl.Viewport(0, 0, (uint)sdl3window.Size.X, (uint)sdl3window.Size.Y);
vertices = new VertexPositionColor[MaxVertices];
// --- GL objects ---
vao = gl.GenVertexArray();
vbo = gl.GenBuffer();
gl.BindVertexArray(vao);
gl.BindBuffer(BufferTargetARB.ArrayBuffer, vbo);
unsafe
{
gl.BufferData(
BufferTargetARB.ArrayBuffer,
(nuint)(MaxVertices * sizeof(VertexPositionColor)),
null,
BufferUsageARB.DynamicDraw
);
// position
gl.EnableVertexAttribArray(0);
gl.VertexAttribPointer(
0,
3,
VertexAttribPointerType.Float,
false,
(uint)sizeof(VertexPositionColor),
(void*)0
);
// color
gl.EnableVertexAttribArray(1);
gl.VertexAttribPointer(
1,
4,
VertexAttribPointerType.Float,
false,
(uint)sizeof(VertexPositionColor),
(void*)(3 * sizeof(float))
);
gl.BindVertexArray(0);
}
shader = CreateShader();
}
public void Draw(Triangle triangle, ColorRGBA colorRGBA)
{
if (vertexIndex + 3 >= vertices.Length)
Flush();
Vector2D A = triangle.A;
Vector2D B = triangle.B;
Vector2D C = triangle.C;
Vector4D c = new Vector4D(colorRGBA.R, colorRGBA.G, colorRGBA.B, colorRGBA.A) / 256f;
vertices[vertexIndex++] = new()
{
Position = new Vector3D(A.X, A.Y, 0f),
Color = c
};
vertices[vertexIndex++] = new()
{
Position = new Vector3D(B.X, B.Y, 0f),
Color = c
};
vertices[vertexIndex++] = new()
{
Position = new Vector3D(C.X, C.Y, 0f),
Color = c
};
}
public void Begin(Matrix4x4? view = null, Matrix4x4? projection = null)
{
this.view = view ?? camera?.ViewMatrix ?? Matrix4x4.Identity;
if (projection != null)
this.projection = projection.Value;
else
this.projection = camera?.ProjectionMatrix ?? Matrix4x4.CreateOrthographicViewCentered(sdl3window.Size.X, sdl3window.Size.Y);
vertexIndex = 0;
SDL.GLMakeCurrent(sdl3window.Window, glContext);
gl.ClearColor(sdl3window.BackgroundColor.R / 256f, sdl3window.BackgroundColor.G / 256f, sdl3window.BackgroundColor.B / 256f, 1f);
gl.Clear(ClearBufferMask.ColorBufferBit);
}
public void End()
{
Flush();
SDL.GLSwapWindow(sdl3window.Window);
}
private void Flush()
{
if (vertexIndex == 0)
return;
gl.UseProgram(shader);
int viewLoc = gl.GetUniformLocation(shader, "uView");
int projLoc = gl.GetUniformLocation(shader, "uProjection");
unsafe
{
Span<float> temp = stackalloc float[16];
CopyTo(view.Transposed, temp);
fixed (float* v = temp)
gl.UniformMatrix4(viewLoc, 1, false, v);
CopyTo(projection.Transposed, temp);
fixed (float* p = temp)
gl.UniformMatrix4(projLoc, 1, false, p);
gl.BindVertexArray(vao);
gl.BindBuffer(BufferTargetARB.ArrayBuffer, vbo);
fixed (VertexPositionColor* ptr = vertices)
{
gl.BufferSubData(
BufferTargetARB.ArrayBuffer,
0,
(nuint)(vertexIndex * sizeof(VertexPositionColor)),
ptr
);
}
}
gl.DrawArrays(PrimitiveType.Triangles, 0, vertexIndex);
vertexIndex = 0;
}
public static void CopyTo(Matrix4x4 m, Span<float> destination)
{
if (destination.Length < 16)
throw new ArgumentException("Destination span must have at least 16 elements.");
destination[0] = m.M11;
destination[1] = m.M12;
destination[2] = m.M13;
destination[3] = m.M14;
destination[4] = m.M21;
destination[5] = m.M22;
destination[6] = m.M23;
destination[7] = m.M24;
destination[8] = m.M31;
destination[9] = m.M32;
destination[10] = m.M33;
destination[11] = m.M34;
destination[12] = m.M41;
destination[13] = m.M42;
destination[14] = m.M43;
destination[15] = m.M44;
}
private uint CreateShader()
{
const string vertexSrc = """
#version 330 core
layout (location = 0) in vec3 aPosition;
layout (location = 1) in vec4 aColor;
uniform mat4 uView;
uniform mat4 uProjection;
out vec4 vColor;
void main()
{
gl_Position = uProjection * uView * vec4(aPosition, 1.0);
vColor = aColor;
}
""";
const string fragmentSrc = """
#version 330 core
in vec4 vColor;
out vec4 FragColor;
void main()
{
FragColor = vColor;
}
""";
uint vs = CompileShader(ShaderType.VertexShader, vertexSrc);
uint fs = CompileShader(ShaderType.FragmentShader, fragmentSrc);
uint program = gl.CreateProgram();
gl.AttachShader(program, vs);
gl.AttachShader(program, fs);
gl.LinkProgram(program);
gl.DeleteShader(vs);
gl.DeleteShader(fs);
return program;
}
private uint CompileShader(ShaderType type, string src)
{
uint shader = gl.CreateShader(type);
gl.ShaderSource(shader, src);
gl.CompileShader(shader);
return shader;
}
struct VertexPositionColor
{
public Vector3D Position;
public Vector4D Color;
public const int SizeInBytes = (3 + 4) * sizeof(float);
}
}