You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

109 lines
4.1 KiB

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using gaemstone.ECS;
using Silk.NET.Maths;
using Silk.NET.OpenGL;
using static gaemstone.Client.CameraModule;
using static gaemstone.Client.Windowing;
namespace gaemstone.Client;
[Module]
[DependsOn(typeof(Windowing))]
public class Renderer
{
private readonly uint _program;
private readonly int _cameraMatrixUniform;
private readonly int _modelMatrixUniform;
public Renderer(Universe universe)
{
var GL = universe.Lookup<Game>().Get<Canvas>().GL;
GL.Enable(EnableCap.DebugOutputSynchronous);
GL.DebugMessageCallback(DebugCallback, 0);
GL.Enable(EnableCap.CullFace);
GL.CullFace(CullFaceMode.Back);
GL.Enable(EnableCap.DepthTest);
GL.DepthFunc(DepthFunction.Less);
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
var vertexShaderSource = Resources.GetString("default.vs.glsl");
var fragmentShaderSource = Resources.GetString("default.fs.glsl");
var vertexShader = GL.CreateAndCompileShader(ShaderType.VertexShader , "vertex" , vertexShaderSource);
var fragmentShader = GL.CreateAndCompileShader(ShaderType.FragmentShader, "fragment", fragmentShaderSource);
_program = GL.CreateAndLinkProgram("program", vertexShader, fragmentShader);
_cameraMatrixUniform = GL.GetUniformLocation(_program, "cameraMatrix");
_modelMatrixUniform = GL.GetUniformLocation(_program, "modelMatrix");
}
[System]
public void Render(Universe universe, Canvas canvas)
{
var GL = canvas.GL;
GL.UseProgram(_program);
GL.Viewport(default, canvas.Size);
GL.ClearColor(new Vector4D<float>(0, 0, 0, 255));
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
Filter.RunOnce(universe, (in GlobalTransform transform, in Camera camera, CameraViewport? viewport) => {
var color = viewport?.ClearColor ?? new(0x4B, 0x00, 0x82, 255);
var bounds = viewport?.Viewport ?? new(default, canvas.Size);
GL.Enable(EnableCap.ScissorTest);
GL.Viewport(bounds); GL.Scissor(bounds.Origin.X, bounds.Origin.Y, (uint)bounds.Size.X, (uint)bounds.Size.Y);
GL.ClearColor(color); GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.Disable(EnableCap.ScissorTest);
// Get the camera's transform matrix and invert it.
Matrix4X4.Invert<float>(transform, out var cameraTransform);
// Create the camera's projection matrix.
var cameraProjection = camera.IsOrthographic
? Matrix4X4.CreateOrthographic(
bounds.Size.X, -bounds.Size.Y,
camera.NearPlane, camera.FarPlane)
: Matrix4X4.CreatePerspectiveFieldOfView(
camera.FieldOfView * MathF.PI / 180, // Degrees => Radians
(float)bounds.Size.X / bounds.Size.Y, // Aspect Ratio
camera.NearPlane, camera.FarPlane);
// Set the uniform to the combined transform and projection.
var cameraMatrix = cameraTransform * cameraProjection;
GL.UniformMatrix4(_cameraMatrixUniform, 1, false, in cameraMatrix.Row1.X);
Filter.RunOnce(universe, (in GlobalTransform transform, in Mesh mesh, Texture? texture) =>
{
// If entity has Texture, bind it now.
if (texture.HasValue) GL.BindTexture(texture.Value.Target, texture.Value.Handle);
// Draw the mesh.
GL.UniformMatrix4(_modelMatrixUniform, 1, false, in transform.Value.Row1.X);
GL.BindVertexArray(mesh.Handle);
if (!mesh.IsIndexed) GL.DrawArrays(PrimitiveType.Triangles, 0, (uint)mesh.Count);
else unsafe { GL.DrawElements(PrimitiveType.Triangles, (uint)mesh.Count, DrawElementsType.UnsignedShort, null); }
// If entity has Texture, unbind it after it has been rendered.
if (texture.HasValue) GL.BindTexture(texture.Value.Target, 0);
});
});
}
[DebuggerStepThrough]
private static void DebugCallback(GLEnum source, GLEnum _type, int id, GLEnum _severity,
int length, nint _message, nint userParam)
{
var type = (DebugType)_type;
var severity = (DebugSeverity)_severity;
var message = Marshal.PtrToStringAnsi(_message, length);
Console.WriteLine($"[GLDebug] [{severity}] {type}/{id}: {message}");
if (type == DebugType.DebugTypeError) throw new Exception(message);
}
}