|
|
|
using System;
|
|
|
|
using System.Diagnostics;
|
|
|
|
using System.Numerics;
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
using gaemstone.ECS;
|
|
|
|
using gaemstone.Flecs;
|
|
|
|
using gaemstone.Utility;
|
|
|
|
using Silk.NET.OpenGL;
|
|
|
|
using Silk.NET.Windowing;
|
|
|
|
using static gaemstone.Client.Components.CameraComponents;
|
|
|
|
using static gaemstone.Client.Components.RenderingComponents;
|
|
|
|
using static gaemstone.Client.Systems.Windowing;
|
|
|
|
using static gaemstone.Components.TransformComponents;
|
|
|
|
|
|
|
|
namespace gaemstone.Client.Systems;
|
|
|
|
|
|
|
|
[Module]
|
|
|
|
[DependsOn<gaemstone.Client.Components.CameraComponents>]
|
|
|
|
[DependsOn<gaemstone.Client.Components.RenderingComponents>]
|
|
|
|
[DependsOn<gaemstone.Client.Systems.Windowing>]
|
|
|
|
[DependsOn<gaemstone.Components.TransformComponents>]
|
|
|
|
public class Renderer
|
|
|
|
{
|
|
|
|
private uint _program;
|
|
|
|
private int _cameraMatrixUniform;
|
|
|
|
private int _modelMatrixUniform;
|
|
|
|
private Rule? _renderEntityRule;
|
|
|
|
|
|
|
|
[Observer<ObserverEvent.OnSet>]
|
|
|
|
public void OnCanvasSet(Canvas canvas)
|
|
|
|
{
|
|
|
|
var GL = 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("/gaemstone.Client/Resources/default.vs.glsl");
|
|
|
|
var fragmentShaderSource = Resources.GetString("/gaemstone.Client/Resources/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<SystemPhase.PreStore>]
|
|
|
|
public void Clear(Canvas canvas)
|
|
|
|
{
|
|
|
|
var GL = canvas.GL;
|
|
|
|
GL.UseProgram(_program);
|
|
|
|
GL.Viewport(canvas.Size);
|
|
|
|
GL.ClearColor(canvas.BackgroundColor);
|
|
|
|
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
|
|
|
}
|
|
|
|
|
|
|
|
[System<SystemPhase.OnStore>]
|
|
|
|
public void Render(Universe universe, [Game] Canvas canvas,
|
|
|
|
in GlobalTransform cameraTransform, in Camera camera, CameraViewport? viewport)
|
|
|
|
{
|
|
|
|
var color = viewport?.ClearColor ?? Color.FromRGB(0.3f, 0.0f, 0.5f);
|
|
|
|
var bounds = viewport?.Viewport ?? new(default, canvas.Size);
|
|
|
|
|
|
|
|
var GL = canvas.GL;
|
|
|
|
GL.Enable(EnableCap.ScissorTest);
|
|
|
|
GL.Viewport(bounds); GL.Scissor(bounds.Left, bounds.Top, (uint)bounds.Width, (uint)bounds.Height);
|
|
|
|
GL.ClearColor(color); GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
|
|
|
GL.Disable(EnableCap.ScissorTest);
|
|
|
|
|
|
|
|
// Create the camera's projection matrix.
|
|
|
|
var cameraProjection = camera.IsOrthographic
|
|
|
|
? Matrix4x4.CreateOrthographic(
|
|
|
|
bounds.Width, -bounds.Height,
|
|
|
|
camera.NearPlane, camera.FarPlane)
|
|
|
|
: Matrix4x4.CreatePerspectiveFieldOfView(
|
|
|
|
camera.FieldOfView * MathF.PI / 180, // Degrees => Radians
|
|
|
|
(float)bounds.Width / bounds.Height, // Aspect Ratio
|
|
|
|
camera.NearPlane, camera.FarPlane);
|
|
|
|
|
|
|
|
// Get the camera's transform matrix and invert it.
|
|
|
|
Matrix4x4.Invert(cameraTransform, out var invertedTransform);
|
|
|
|
// Set the uniform to the combined transform and projection.
|
|
|
|
var cameraMatrix = invertedTransform * cameraProjection;
|
|
|
|
GL.UniformMatrix4(_cameraMatrixUniform, 1, false, in cameraMatrix.M11);
|
|
|
|
|
|
|
|
_renderEntityRule ??= new(universe, new("""
|
|
|
|
GlobalTransform,
|
|
|
|
(Mesh, $mesh), MeshHandle($mesh),
|
|
|
|
?(Texture, $tex), ?TextureHandle($tex)
|
|
|
|
"""));
|
|
|
|
foreach (var iter in _renderEntityRule.Iter()) {
|
|
|
|
var transforms = iter.Field<GlobalTransform>(1);
|
|
|
|
var meshes = iter.Field<MeshHandle>(3);
|
|
|
|
// var texPairs = iter.FieldOrEmpty<Identifier>(4);
|
|
|
|
var textures = iter.FieldOrEmpty<TextureHandle>(5);
|
|
|
|
|
|
|
|
for (var i = 0; i < iter.Count; i++) {
|
|
|
|
var rTransform = transforms[i];
|
|
|
|
var mesh = meshes[i];
|
|
|
|
// var hasTexture = (texPairs.Length > 0);
|
|
|
|
var texture = textures.GetOrNull(i);
|
|
|
|
|
|
|
|
// 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 rTransform.Value.M11);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[System<SystemPhase.PostFrame>]
|
|
|
|
public static void SwapBuffers(GameWindow window)
|
|
|
|
=> window.Handle.SwapBuffers();
|
|
|
|
|
|
|
|
[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);
|
|
|
|
}
|
|
|
|
}
|