This commit is contained in:
2024-12-09 13:56:18 +03:00
parent bd156ad028
commit ddc91e0bfa
22 changed files with 235 additions and 58 deletions

View File

@@ -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

View File

@@ -16,16 +16,16 @@ public class Image<T>(T[,] pixels)
{
}
public DynamicTexture<T> ToDynamicTexture()
public DynamicTexture ToDynamicTexture()
{
var texture = new DynamicTexture<T>(Width, Height);
var texture = DynamicTexture.Create<T>(Width, Height);
texture.UploadPixels(this);
return texture;
}
public StaticTexture<T> ToStaticTexture()
public StaticTexture ToStaticTexture()
{
var texture = new StaticTexture<T>(Width, Height);
var texture = StaticTexture.Create<T>(Width, Height);
texture.UploadPixels(this);
return texture;
}

View File

@@ -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<Vertex> _vertices = [];
private readonly List<uint> _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;
}
}

View File

@@ -30,16 +30,17 @@ public class Framebuffer : OpenGlObject
}
}
public IConstTexture<Rgb8> Texture => _texture;
internal Texture.Texture<Rgb8> TextureInternal => _texture;
public IConstTexture Texture => _texture;
internal Texture.Texture TextureInternal => _texture;
private int _width;
private int _height;
private readonly DynamicTexture<Rgb8> _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<Rgb8>(width, height);
_texture = new DynamicTexture(width, height, format, type, internalFormat);
GL.NamedFramebufferTexture(Handle, FramebufferAttachment.ColorAttachment0, _texture.Handle, 0);
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<T>(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()

View File

@@ -1,9 +1,8 @@
using Engine.Renderer.Pixel;
using Engine.Renderer.Texture;
using Engine.Renderer.Texture;
namespace Engine.Renderer;
public interface IPresenter<T> where T : struct, IPixel
public interface IPresenter
{
public void Present(IConstTexture<T> texture);
public void Present(IConstTexture texture);
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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<RenderPass> _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;
}
}
}

View File

@@ -9,7 +9,7 @@ namespace Engine.Renderer;
public class Renderer
{
internal Texture.Texture<Rgb8> TextureInternal => _framebuffer.TextureInternal;
internal Texture.Texture TextureInternal => _framebuffer.TextureInternal;
private readonly Framebuffer.Framebuffer _framebuffer;
private readonly Queue<Action<Renderer>> _renderActions = new();
@@ -18,7 +18,7 @@ public class Renderer
{
InitializeOpenGl();
_framebuffer = new Framebuffer.Framebuffer(width, height);
_framebuffer = Framebuffer.Framebuffer.Create<Rgb8>(width, height);
}
private void InitializeOpenGl()

View File

@@ -3,24 +3,31 @@ using OpenTK.Graphics.OpenGL;
namespace Engine.Renderer.Texture;
public class DynamicTexture<T> : Texture<T> 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<T>(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)

View File

@@ -3,20 +3,21 @@ using Engine.Renderer.Pixel;
namespace Engine.Renderer.Texture;
public interface IConstTexture<T> 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<T>(int x, int y, int width, int height, T[,] pixels)
where T : struct, IPixel;
}
public static class ConstTextureExtensions
{
public static T[,] ReadPixels<T>(this IConstTexture<T> texture) where T : struct, IPixel
=> texture.ReadPixels(0, 0, texture.Width, texture.Height);
public static T[,] ReadPixels<T>(this IConstTexture texture) where T : struct, IPixel
=> texture.ReadPixels<T>(0, 0, texture.Width, texture.Height);
public static T[,] ReadPixels<T>(this IConstTexture<T> texture, int x, int y, int width, int height)
public static T[,] ReadPixels<T>(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<T>(this IConstTexture<T> texture, Image<T> image) where T : struct, IPixel
public static void ReadPixels<T>(this IConstTexture texture, Image<T> image) where T : struct, IPixel
=> texture.ReadPixels(0, 0, image);
public static void ReadPixels<T>(this IConstTexture<T> texture, int x, int y, Image<T> image)
public static void ReadPixels<T>(this IConstTexture texture, int x, int y, Image<T> image)
where T : struct, IPixel =>
texture.ReadPixels(x, y, image.Width, image.Height, image.Pixels);
public static void ReadPixels<T>(this IConstTexture<T> texture, T[,] pixels) where T : struct, IPixel
public static void ReadPixels<T>(this IConstTexture texture, T[,] pixels) where T : struct, IPixel
=> texture.ReadPixels(0, 0, texture.Width, texture.Height, pixels);
}

View File

@@ -3,20 +3,21 @@ using Engine.Renderer.Pixel;
namespace Engine.Renderer.Texture;
public interface ITexture<T> : IConstTexture<T> where T : struct, IPixel
public interface ITexture : IConstTexture
{
public void UploadPixels(int x, int y, int width, int height, T[,] pixels);
public void UploadPixels<T>(int x, int y, int width, int height, T[,] pixels)
where T : struct, IPixel;
}
public static class TextureExtensions
{
public static void UploadPixels<T>(this ITexture<T> texture, Image<T> image) where T : struct, IPixel
public static void UploadPixels<T>(this ITexture texture, Image<T> image) where T : struct, IPixel
=> texture.UploadPixels(0, 0, image);
public static void UploadPixels<T>(this ITexture<T> texture, int x, int y, Image<T> image)
public static void UploadPixels<T>(this ITexture texture, int x, int y, Image<T> image)
where T : struct, IPixel =>
texture.UploadPixels(x, y, image.Width, image.Height, image.Pixels);
public static void UploadPixels<T>(this ITexture<T> texture, T[,] pixels) where T : struct, IPixel
public static void UploadPixels<T>(this ITexture texture, T[,] pixels) where T : struct, IPixel
=> texture.UploadPixels(0, 0, texture.Width, texture.Height, pixels);
}

View File

@@ -3,11 +3,17 @@ using OpenTK.Graphics.OpenGL;
namespace Engine.Renderer.Texture;
public class StaticTexture<T> : Texture<T> 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<T>(int width, int height)
where T : struct, IPixel
{
var format = default(T).SizedInternalFormat;
return new StaticTexture(width, height, format);
}
}

View File

@@ -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<T> : OpenGlObject, ITexture<T> where T : struct, IPixel
public abstract class Texture : OpenGlObject, ITexture
{
public int Width
{
@@ -43,7 +42,8 @@ public abstract class Texture<T> : OpenGlObject, ITexture<T> where T : struct, I
Handle = handle;
}
public void UploadPixels(int x, int y, int width, int height, T[,] pixels)
public void UploadPixels<T>(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<T> : OpenGlObject, ITexture<T> 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<T>(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<T> : OpenGlObject, ITexture<T> where T : struct, I
pixels);
}
internal void BindUnit(int unit)
internal void BindUnit(int unit = 0)
{
GL.BindTextureUnit(unit, Handle);
}

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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();
}
}

View File

@@ -7,7 +7,7 @@ using OpenTK.Windowing.GraphicsLibraryFramework;
namespace Engine;
public class Window : IPresenter<Rgb8>
public class Window : IPresenter
{
public bool IsExiting => _window.IsExiting;
public int Width { get; private set; }
@@ -42,7 +42,7 @@ public class Window : IPresenter<Rgb8>
}
}
public void Present(IConstTexture<Rgb8> texture)
public void Present(IConstTexture texture)
{
if (_headless)
return;

View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>DoomDeathmatchConsole</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\DoomDeathmatch\DoomDeathmatch.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Sylvan.BuildTools.Resources" Version="0.6.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup><StaticResourceFolder Include="assets"/></ItemGroup>
</Project>

View File

@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<RootNamespace>DoomDeathmatchWPF</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\DoomDeathmatch\DoomDeathmatch.csproj" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="src\App.xaml">
<Generator>MSBuild:Compile</Generator>
<XamlRuntime>Wpf</XamlRuntime>
<SubType>Designer</SubType>
</ApplicationDefinition>
</ItemGroup>
<ItemGroup>
<Page Update="src\MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<XamlRuntime>Wpf</XamlRuntime>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
</Project>