feat: added basic window, input & drawing
This commit is contained in:
269
Platforms/Desktop/Engine.Integration.SDL3/Sdl3TriangleBatch.cs
Normal file
269
Platforms/Desktop/Engine.Integration.SDL3/Sdl3TriangleBatch.cs
Normal file
@@ -0,0 +1,269 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user