diff --git a/DoomDeathmatch.sln b/DoomDeathmatch.sln index bd717de..00adcbd 100644 --- a/DoomDeathmatch.sln +++ b/DoomDeathmatch.sln @@ -4,9 +4,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DoomDeathmatch", "DoomDeath EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine", "Engine\Engine.csproj", "{5EE134DE-2275-40C0-8B9D-4EFF22474F63}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DoomDeathmatchConsole", "DoomDeathmatchConsole\DoomDeathmatchConsole.csproj", "{B9A652EE-4267-4D6B-B1A6-2447F870A06D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PresenterConsole", "PresenterConsole\PresenterConsole.csproj", "{B9A652EE-4267-4D6B-B1A6-2447F870A06D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DoomDeathmatchWPF", "DoomDeathmatchWPF\DoomDeathmatchWPF.csproj", "{B712A719-5EB3-4869-AA4A-3BFFA3B9C918}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PresenterWPF", "PresenterWPF\PresenterWPF.csproj", "{B712A719-5EB3-4869-AA4A-3BFFA3B9C918}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EngineTests", "EngineTests\EngineTests.csproj", "{CC28C26C-0998-4C13-8855-978658B8B0D6}" EndProject diff --git a/DoomDeathmatch/Program.cs b/DoomDeathmatch/src/Program.cs similarity index 100% rename from DoomDeathmatch/Program.cs rename to DoomDeathmatch/src/Program.cs diff --git a/DoomDeathmatch/QuadVertex.cs b/DoomDeathmatch/src/QuadVertex.cs similarity index 100% rename from DoomDeathmatch/QuadVertex.cs rename to DoomDeathmatch/src/QuadVertex.cs diff --git a/Engine/src/Asset/Image.cs b/Engine/src/Asset/Image.cs index ced57eb..d8721f3 100644 --- a/Engine/src/Asset/Image.cs +++ b/Engine/src/Asset/Image.cs @@ -16,16 +16,16 @@ public class Image(T[,] pixels) { } - public DynamicTexture ToDynamicTexture() + public DynamicTexture ToDynamicTexture() { - var texture = new DynamicTexture(Width, Height); + var texture = DynamicTexture.Create(Width, Height); texture.UploadPixels(this); return texture; } - public StaticTexture ToStaticTexture() + public StaticTexture ToStaticTexture() { - var texture = new StaticTexture(Width, Height); + var texture = StaticTexture.Create(Width, Height); texture.UploadPixels(this); return texture; } diff --git a/Engine/src/Asset/Mesh/Mesh.cs b/Engine/src/Asset/Mesh/Mesh.cs index 8cee22f..36d59e7 100644 --- a/Engine/src/Asset/Mesh/Mesh.cs +++ b/Engine/src/Asset/Mesh/Mesh.cs @@ -1,4 +1,6 @@ -using OpenTK.Mathematics; +using Engine.Renderer.Buffer.Vertex; +using OpenTK.Graphics.OpenGL; +using OpenTK.Mathematics; namespace Engine.Asset.Mesh; @@ -13,10 +15,15 @@ public class Mesh private readonly List _vertices = []; private readonly List _indices = []; - public record struct Vertex + public record struct Vertex : IVertex { - public Vector3 Position { get; internal set; } - public Vector3 Normal { get; internal set; } - public Vector2 Uv { get; internal set; } + [Vertex(VertexAttribType.Float, 3)] + public Vector3 Position; + + [Vertex(VertexAttribType.Float, 3)] + public Vector3 Normal; + + [Vertex(VertexAttribType.Float, 2)] + public Vector2 Uv; } } \ No newline at end of file diff --git a/Engine/src/Renderer/Framebuffer/Framebuffer.cs b/Engine/src/Renderer/Framebuffer/Framebuffer.cs index 8c49660..b30c9c8 100644 --- a/Engine/src/Renderer/Framebuffer/Framebuffer.cs +++ b/Engine/src/Renderer/Framebuffer/Framebuffer.cs @@ -30,16 +30,17 @@ public class Framebuffer : OpenGlObject } } - public IConstTexture Texture => _texture; - internal Texture.Texture TextureInternal => _texture; + public IConstTexture Texture => _texture; + internal Texture.Texture TextureInternal => _texture; private int _width; private int _height; - private readonly DynamicTexture _texture; - private readonly Renderbuffer _renderbuffer; + private readonly DynamicTexture _texture; + private readonly Renderbuffer? _renderbuffer; - public Framebuffer(int width, int height) + public Framebuffer(int width, int height, PixelFormat format, PixelType type, PixelInternalFormat internalFormat, + bool createRenderbuffer = true) { Width = width; Height = height; @@ -47,18 +48,28 @@ public class Framebuffer : OpenGlObject GL.CreateFramebuffers(1, out int handle); Handle = handle; - _texture = new DynamicTexture(width, height); + _texture = new DynamicTexture(width, height, format, type, internalFormat); GL.NamedFramebufferTexture(Handle, FramebufferAttachment.ColorAttachment0, _texture.Handle, 0); - _renderbuffer = new Renderbuffer(width, height, RenderbufferStorage.Depth24Stencil8); - GL.NamedFramebufferRenderbuffer(Handle, FramebufferAttachment.DepthStencilAttachment, - RenderbufferTarget.Renderbuffer, _renderbuffer.Handle); + if (createRenderbuffer) + { + _renderbuffer = new Renderbuffer(width, height, RenderbufferStorage.Depth24Stencil8); + GL.NamedFramebufferRenderbuffer(Handle, FramebufferAttachment.DepthStencilAttachment, + RenderbufferTarget.Renderbuffer, _renderbuffer.Handle); + } var status = GL.CheckNamedFramebufferStatus(Handle, FramebufferTarget.Framebuffer); if (status != FramebufferStatus.FramebufferComplete) throw new Exception($"Framebuffer is not complete: {status}"); } + public static Framebuffer Create(int width, int height, bool createRenderbuffer = true) + where T : struct, IPixel + { + var pixel = default(T); + return new Framebuffer(width, height, pixel.Format, pixel.Type, pixel.InternalFormat, createRenderbuffer); + } + public void Resize(int width, int height) { if (Width == width && Height == height) @@ -68,7 +79,7 @@ public class Framebuffer : OpenGlObject Height = height; _texture.Resize(width, height); - _renderbuffer.Resize(width, height); + _renderbuffer?.Resize(width, height); } internal override void Bind() diff --git a/Engine/src/Renderer/IPresenter.cs b/Engine/src/Renderer/IPresenter.cs index 110ae7f..8c116c8 100644 --- a/Engine/src/Renderer/IPresenter.cs +++ b/Engine/src/Renderer/IPresenter.cs @@ -1,9 +1,8 @@ -using Engine.Renderer.Pixel; -using Engine.Renderer.Texture; +using Engine.Renderer.Texture; namespace Engine.Renderer; -public interface IPresenter where T : struct, IPixel +public interface IPresenter { - public void Present(IConstTexture texture); + public void Present(IConstTexture texture); } \ No newline at end of file diff --git a/Engine/src/Renderer/Pixel/R8.cs b/Engine/src/Renderer/Pixel/R8.cs new file mode 100644 index 0000000..5bf4684 --- /dev/null +++ b/Engine/src/Renderer/Pixel/R8.cs @@ -0,0 +1,16 @@ +using System.Runtime.InteropServices; +using OpenTK.Graphics.OpenGL; + +namespace Engine.Renderer.Pixel; + +[StructLayout(LayoutKind.Sequential)] +public struct R8 : IPixel +{ + public PixelFormat Format => PixelFormat.Red; + public PixelType Type => PixelType.UnsignedByte; + + public PixelInternalFormat InternalFormat => PixelInternalFormat.R8; + public SizedInternalFormat SizedInternalFormat => SizedInternalFormat.R8; + + public byte R; +} \ No newline at end of file diff --git a/Engine/src/Renderer/Pixel/Rgb8.cs b/Engine/src/Renderer/Pixel/Rgb8.cs index e0c6306..0ef5a54 100644 --- a/Engine/src/Renderer/Pixel/Rgb8.cs +++ b/Engine/src/Renderer/Pixel/Rgb8.cs @@ -1,7 +1,9 @@ -using OpenTK.Graphics.OpenGL; +using System.Runtime.InteropServices; +using OpenTK.Graphics.OpenGL; namespace Engine.Renderer.Pixel; +[StructLayout(LayoutKind.Sequential)] public struct Rgb8 : IPixel { public PixelFormat Format => PixelFormat.Rgb; diff --git a/Engine/src/Renderer/Postprocess/Pipeline.cs b/Engine/src/Renderer/Postprocess/Pipeline.cs new file mode 100644 index 0000000..4be4b3d --- /dev/null +++ b/Engine/src/Renderer/Postprocess/Pipeline.cs @@ -0,0 +1,39 @@ +using Engine.Renderer.Framebuffer; +using Engine.Renderer.Pixel; +using Engine.Renderer.Shader; + +namespace Engine.Renderer.Postprocess; + +public class Pipeline +{ + private readonly List _renderPasses = []; + + public void AddRenderPass(ShaderProgram shaderProgram, Framebuffer.Framebuffer frameBuffer) + { + _renderPasses.Add(new RenderPass(shaderProgram, frameBuffer)); + } + + public void Render() + { + foreach (var renderPass in _renderPasses) + { + var texture = renderPass.FrameBuffer.TextureInternal; + texture.BindUnit(); + renderPass.ShaderProgram.Bind(); + renderPass.FrameBuffer.Bind(); + // TODO: Render + } + } + + private class RenderPass + { + internal ShaderProgram ShaderProgram { get; } + internal Framebuffer.Framebuffer FrameBuffer { get; } + + internal RenderPass(ShaderProgram shaderProgram, Framebuffer.Framebuffer frameBuffer) + { + ShaderProgram = shaderProgram; + FrameBuffer = frameBuffer; + } + } +} \ No newline at end of file diff --git a/Engine/src/Renderer/Renderer.cs b/Engine/src/Renderer/Renderer.cs index f691cbc..adb0c9a 100644 --- a/Engine/src/Renderer/Renderer.cs +++ b/Engine/src/Renderer/Renderer.cs @@ -9,7 +9,7 @@ namespace Engine.Renderer; public class Renderer { - internal Texture.Texture TextureInternal => _framebuffer.TextureInternal; + internal Texture.Texture TextureInternal => _framebuffer.TextureInternal; private readonly Framebuffer.Framebuffer _framebuffer; private readonly Queue> _renderActions = new(); @@ -18,7 +18,7 @@ public class Renderer { InitializeOpenGl(); - _framebuffer = new Framebuffer.Framebuffer(width, height); + _framebuffer = Framebuffer.Framebuffer.Create(width, height); } private void InitializeOpenGl() diff --git a/Engine/src/Renderer/Texture/DynamicTexture.cs b/Engine/src/Renderer/Texture/DynamicTexture.cs index 5e75fdd..78b9c14 100644 --- a/Engine/src/Renderer/Texture/DynamicTexture.cs +++ b/Engine/src/Renderer/Texture/DynamicTexture.cs @@ -3,24 +3,31 @@ using OpenTK.Graphics.OpenGL; namespace Engine.Renderer.Texture; -public class DynamicTexture : Texture where T : struct, IPixel +public class DynamicTexture : Texture { private readonly PixelFormat _format; private readonly PixelType _type; private readonly PixelInternalFormat _internalFormat; - public DynamicTexture(int width, int height) : base(width, height) + internal DynamicTexture(int width, int height, PixelFormat format, PixelType type, PixelInternalFormat internalFormat) + : base(width, height) { - var pixel = default(T); - _format = pixel.Format; - _type = pixel.Type; - _internalFormat = pixel.InternalFormat; + _format = format; + _type = type; + _internalFormat = internalFormat; GL.BindTexture(TextureTarget.Texture2D, Handle); GL.TexImage2D(TextureTarget.Texture2D, 0, _internalFormat, Width, Height, 0, _format, _type, IntPtr.Zero); } + public static DynamicTexture Create(int width, int height) + where T : struct, IPixel + { + var pixel = default(T); + return new DynamicTexture(width, height, pixel.Format, pixel.Type, pixel.InternalFormat); + } + public void Resize(int width, int height) { if (Width == width && Height == height) diff --git a/Engine/src/Renderer/Texture/IConstTexture.cs b/Engine/src/Renderer/Texture/IConstTexture.cs index c599c8c..7e759a7 100644 --- a/Engine/src/Renderer/Texture/IConstTexture.cs +++ b/Engine/src/Renderer/Texture/IConstTexture.cs @@ -3,20 +3,21 @@ using Engine.Renderer.Pixel; namespace Engine.Renderer.Texture; -public interface IConstTexture where T : struct, IPixel +public interface IConstTexture { public int Width { get; } public int Height { get; } - public void ReadPixels(int x, int y, int width, int height, T[,] pixels); + public void ReadPixels(int x, int y, int width, int height, T[,] pixels) + where T : struct, IPixel; } public static class ConstTextureExtensions { - public static T[,] ReadPixels(this IConstTexture texture) where T : struct, IPixel - => texture.ReadPixels(0, 0, texture.Width, texture.Height); + public static T[,] ReadPixels(this IConstTexture texture) where T : struct, IPixel + => texture.ReadPixels(0, 0, texture.Width, texture.Height); - public static T[,] ReadPixels(this IConstTexture texture, int x, int y, int width, int height) + public static T[,] ReadPixels(this IConstTexture texture, int x, int y, int width, int height) where T : struct, IPixel { var pixels = new T[width, height]; @@ -25,13 +26,13 @@ public static class ConstTextureExtensions return pixels; } - public static void ReadPixels(this IConstTexture texture, Image image) where T : struct, IPixel + public static void ReadPixels(this IConstTexture texture, Image image) where T : struct, IPixel => texture.ReadPixels(0, 0, image); - public static void ReadPixels(this IConstTexture texture, int x, int y, Image image) + public static void ReadPixels(this IConstTexture texture, int x, int y, Image image) where T : struct, IPixel => texture.ReadPixels(x, y, image.Width, image.Height, image.Pixels); - public static void ReadPixels(this IConstTexture texture, T[,] pixels) where T : struct, IPixel + public static void ReadPixels(this IConstTexture texture, T[,] pixels) where T : struct, IPixel => texture.ReadPixels(0, 0, texture.Width, texture.Height, pixels); } \ No newline at end of file diff --git a/Engine/src/Renderer/Texture/ITexture.cs b/Engine/src/Renderer/Texture/ITexture.cs index 0caea61..197a29b 100644 --- a/Engine/src/Renderer/Texture/ITexture.cs +++ b/Engine/src/Renderer/Texture/ITexture.cs @@ -3,20 +3,21 @@ using Engine.Renderer.Pixel; namespace Engine.Renderer.Texture; -public interface ITexture : IConstTexture where T : struct, IPixel +public interface ITexture : IConstTexture { - public void UploadPixels(int x, int y, int width, int height, T[,] pixels); + public void UploadPixels(int x, int y, int width, int height, T[,] pixels) + where T : struct, IPixel; } public static class TextureExtensions { - public static void UploadPixels(this ITexture texture, Image image) where T : struct, IPixel + public static void UploadPixels(this ITexture texture, Image image) where T : struct, IPixel => texture.UploadPixels(0, 0, image); - public static void UploadPixels(this ITexture texture, int x, int y, Image image) + public static void UploadPixels(this ITexture texture, int x, int y, Image image) where T : struct, IPixel => texture.UploadPixels(x, y, image.Width, image.Height, image.Pixels); - public static void UploadPixels(this ITexture texture, T[,] pixels) where T : struct, IPixel + public static void UploadPixels(this ITexture texture, T[,] pixels) where T : struct, IPixel => texture.UploadPixels(0, 0, texture.Width, texture.Height, pixels); } \ No newline at end of file diff --git a/Engine/src/Renderer/Texture/StaticTexture.cs b/Engine/src/Renderer/Texture/StaticTexture.cs index 94653e7..ad2cf84 100644 --- a/Engine/src/Renderer/Texture/StaticTexture.cs +++ b/Engine/src/Renderer/Texture/StaticTexture.cs @@ -3,11 +3,17 @@ using OpenTK.Graphics.OpenGL; namespace Engine.Renderer.Texture; -public class StaticTexture : Texture where T : struct, IPixel +public class StaticTexture : Texture { - public StaticTexture(int width, int height) : base(width, height) + private StaticTexture(int width, int height, SizedInternalFormat format) : base(width, height) { - var format = default(T).SizedInternalFormat; GL.TextureStorage2D(Handle, 1, format, Width, Height); } + + public static StaticTexture Create(int width, int height) + where T : struct, IPixel + { + var format = default(T).SizedInternalFormat; + return new StaticTexture(width, height, format); + } } \ No newline at end of file diff --git a/Engine/src/Renderer/Texture/Texture.cs b/Engine/src/Renderer/Texture/Texture.cs index ec435f4..fa105a5 100644 --- a/Engine/src/Renderer/Texture/Texture.cs +++ b/Engine/src/Renderer/Texture/Texture.cs @@ -1,11 +1,10 @@ using System.Runtime.InteropServices; -using Engine.Asset; using Engine.Renderer.Pixel; using OpenTK.Graphics.OpenGL; namespace Engine.Renderer.Texture; -public abstract class Texture : OpenGlObject, ITexture where T : struct, IPixel +public abstract class Texture : OpenGlObject, ITexture { public int Width { @@ -43,7 +42,8 @@ public abstract class Texture : OpenGlObject, ITexture where T : struct, I Handle = handle; } - public void UploadPixels(int x, int y, int width, int height, T[,] pixels) + public void UploadPixels(int x, int y, int width, int height, T[,] pixels) + where T : struct, IPixel { if (x < 0 || y < 0) throw new ArgumentException("x and y must be greater than 0"); @@ -63,7 +63,8 @@ public abstract class Texture : OpenGlObject, ITexture where T : struct, I GL.TextureSubImage2D(Handle, 0, x, y, width, height, format, type, pixels); } - public void ReadPixels(int x, int y, int width, int height, T[,] pixels) + public void ReadPixels(int x, int y, int width, int height, T[,] pixels) + where T : struct, IPixel { if (x < 0 || y < 0) throw new ArgumentException("x and y must be greater than 0"); @@ -84,7 +85,7 @@ public abstract class Texture : OpenGlObject, ITexture where T : struct, I pixels); } - internal void BindUnit(int unit) + internal void BindUnit(int unit = 0) { GL.BindTextureUnit(unit, Handle); } diff --git a/Engine/src/Scene/Component/BuiltIn/Camera.cs b/Engine/src/Scene/Component/BuiltIn/Camera.cs index 583193f..0a47d53 100644 --- a/Engine/src/Scene/Component/BuiltIn/Camera.cs +++ b/Engine/src/Scene/Component/BuiltIn/Camera.cs @@ -26,4 +26,7 @@ public abstract class Camera( AspectRatio = (float)value.X / value.Y; } } + + public abstract Vector3 ScreenToWorld(Vector2 screenPosition); + public abstract Vector2 WorldToScreen(Vector3 worldPosition); } \ No newline at end of file diff --git a/Engine/src/Scene/Component/BuiltIn/OrthographicCamera.cs b/Engine/src/Scene/Component/BuiltIn/OrthographicCamera.cs index 6f0f3c7..ced0fcd 100644 --- a/Engine/src/Scene/Component/BuiltIn/OrthographicCamera.cs +++ b/Engine/src/Scene/Component/BuiltIn/OrthographicCamera.cs @@ -1,4 +1,5 @@ -using OpenTK.Mathematics; +using Engine.Util; +using OpenTK.Mathematics; namespace Engine.Scene.Component.BuiltIn; @@ -19,6 +20,23 @@ public class OrthographicCamera( ? Matrix4.CreateOrthographic(Size, Size / AspectRatio, NearPlane, FarPlane) : Matrix4.CreateOrthographic(Size * AspectRatio, Size, NearPlane, FarPlane); + public override Vector3 ScreenToWorld(Vector2 screenPosition) + { + var offset = FixedAxis == Axis.X + ? new Vector2(Size, Size / AspectRatio) + : new Vector2(Size * AspectRatio, Size); + offset /= 2; + + return new Vector4(screenPosition.X - offset.X, screenPosition.Y - offset.Y, 0, 1) + .MulProject(GameObject.Transform.TransformMatrix) + .Xyz; + } + + public override Vector2 WorldToScreen(Vector3 worldPosition) + { + throw new NotImplementedException(); + } + public enum Axis { X, diff --git a/Engine/src/Scene/Component/BuiltIn/PerspectiveCamera.cs b/Engine/src/Scene/Component/BuiltIn/PerspectiveCamera.cs index 44ff400..a55a4d2 100644 --- a/Engine/src/Scene/Component/BuiltIn/PerspectiveCamera.cs +++ b/Engine/src/Scene/Component/BuiltIn/PerspectiveCamera.cs @@ -27,4 +27,14 @@ public class PerspectiveCamera( public override Matrix4 Projection => Matrix4.CreatePerspectiveFieldOfView(FieldOfView, AspectRatio, NearPlane, FarPlane); + + public override Vector3 ScreenToWorld(Vector2 screenPosition) + { + throw new NotImplementedException(); + } + + public override Vector2 WorldToScreen(Vector3 worldPosition) + { + throw new NotImplementedException(); + } } \ No newline at end of file diff --git a/Engine/src/Window.cs b/Engine/src/Window.cs index 16adaca..d90ec3b 100644 --- a/Engine/src/Window.cs +++ b/Engine/src/Window.cs @@ -7,7 +7,7 @@ using OpenTK.Windowing.GraphicsLibraryFramework; namespace Engine; -public class Window : IPresenter +public class Window : IPresenter { public bool IsExiting => _window.IsExiting; public int Width { get; private set; } @@ -42,7 +42,7 @@ public class Window : IPresenter } } - public void Present(IConstTexture texture) + public void Present(IConstTexture texture) { if (_headless) return; diff --git a/PresenterConsole/PresenterConsole.csproj b/PresenterConsole/PresenterConsole.csproj new file mode 100644 index 0000000..20ec187 --- /dev/null +++ b/PresenterConsole/PresenterConsole.csproj @@ -0,0 +1,24 @@ + + + + Exe + net8.0 + enable + enable + DoomDeathmatchConsole + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + diff --git a/PresenterWPF/PresenterWPF.csproj b/PresenterWPF/PresenterWPF.csproj new file mode 100644 index 0000000..4147e0f --- /dev/null +++ b/PresenterWPF/PresenterWPF.csproj @@ -0,0 +1,32 @@ + + + + WinExe + net8.0-windows + enable + enable + true + DoomDeathmatchWPF + + + + + + + + + MSBuild:Compile + Wpf + Designer + + + + + + MSBuild:Compile + Wpf + Designer + + + +