reformat
This commit is contained in:
@@ -19,14 +19,14 @@
|
||||
<InternalsVisibleTo Include="PresenterConsole"/>
|
||||
<InternalsVisibleTo Include="PresenterWpf"/>
|
||||
<InternalsVisibleTo Include="PresenterNative"/>
|
||||
<PackageReference Include="Evergine.Bindings.RenderDoc" Version="2024.10.7.18" />
|
||||
<PackageReference Include="Evergine.Bindings.RenderDoc" Version="2024.10.7.18"/>
|
||||
|
||||
<PackageReference Include="OpenTK" Version="4.8.2"/>
|
||||
<PackageReference Include="Serilog" Version="4.1.0"/>
|
||||
<PackageReference Include="Serilog.Enrichers.Thread" Version="4.0.0"/>
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0"/>
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0"/>
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.6" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.6"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -43,6 +43,7 @@
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>ShaderResource.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Remove="src\Resource\ResourceHandle.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -6,24 +6,20 @@ namespace Engine.Asset.Font;
|
||||
|
||||
public class Font
|
||||
{
|
||||
public StaticTexture AtlasTexture => _atlasTexture;
|
||||
public Metadata.Metadata Metadata => _metadata;
|
||||
public Vector2 UnitRange => _unitRange;
|
||||
|
||||
private readonly StaticTexture _atlasTexture;
|
||||
private readonly Metadata.Metadata _metadata;
|
||||
public StaticTexture AtlasTexture { get; }
|
||||
public Metadata.Metadata Metadata { get; }
|
||||
public Vector2 UnitRange { get; }
|
||||
|
||||
private readonly Dictionary<int, Glyph> _glyphs = new();
|
||||
private readonly Dictionary<int, GlyphData> _glyphData = new();
|
||||
private readonly Dictionary<(int, int), Kerning> _kernings = new();
|
||||
private readonly Vector2 _unitRange;
|
||||
|
||||
public Font(StaticTexture parAtlasTexture, Metadata.Metadata parMetadata)
|
||||
{
|
||||
_atlasTexture = parAtlasTexture;
|
||||
_metadata = parMetadata;
|
||||
_unitRange = new Vector2(_metadata.Atlas.DistanceRange / _metadata.Atlas.Width,
|
||||
_metadata.Atlas.DistanceRange / _metadata.Atlas.Height);
|
||||
AtlasTexture = parAtlasTexture;
|
||||
Metadata = parMetadata;
|
||||
UnitRange = new Vector2(Metadata.Atlas.DistanceRange / Metadata.Atlas.Width,
|
||||
Metadata.Atlas.DistanceRange / Metadata.Atlas.Height);
|
||||
|
||||
LoadGlyphs();
|
||||
LoadKernings();
|
||||
@@ -59,7 +55,7 @@ public class Font
|
||||
|
||||
private void LoadGlyphs()
|
||||
{
|
||||
foreach (var glyph in _metadata.Glyphs)
|
||||
foreach (var glyph in Metadata.Glyphs)
|
||||
{
|
||||
_glyphs.Add(glyph.Unicode, glyph);
|
||||
|
||||
@@ -68,13 +64,13 @@ public class Font
|
||||
continue;
|
||||
}
|
||||
|
||||
_glyphData.Add(glyph.Unicode, new GlyphData(_metadata, glyph));
|
||||
_glyphData.Add(glyph.Unicode, new GlyphData(Metadata, glyph));
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadKernings()
|
||||
{
|
||||
foreach (var kerning in _metadata.Kerning)
|
||||
foreach (var kerning in Metadata.Kerning)
|
||||
{
|
||||
_kernings.Add((kerning.Unicode1, kerning.Unicode2), kerning);
|
||||
}
|
||||
@@ -93,7 +89,7 @@ public class Font
|
||||
new Vector2(parGlyph.PlaneBounds!.Left, parGlyph.PlaneBounds.Bottom),
|
||||
new Vector2(parGlyph.PlaneBounds.Left, parGlyph.PlaneBounds.Top),
|
||||
new Vector2(parGlyph.PlaneBounds.Right, parGlyph.PlaneBounds.Bottom),
|
||||
new Vector2(parGlyph.PlaneBounds.Right, parGlyph.PlaneBounds.Top),
|
||||
new Vector2(parGlyph.PlaneBounds.Right, parGlyph.PlaneBounds.Top)
|
||||
];
|
||||
|
||||
UVs =
|
||||
@@ -101,8 +97,7 @@ public class Font
|
||||
new Vector2(parGlyph.AtlasBounds!.Left, parGlyph.AtlasBounds.Bottom) / size,
|
||||
new Vector2(parGlyph.AtlasBounds.Left, parGlyph.AtlasBounds.Top) / size,
|
||||
new Vector2(parGlyph.AtlasBounds.Right, parGlyph.AtlasBounds.Bottom) / size,
|
||||
|
||||
new Vector2(parGlyph.AtlasBounds.Right, parGlyph.AtlasBounds.Top) / size,
|
||||
new Vector2(parGlyph.AtlasBounds.Right, parGlyph.AtlasBounds.Top) / size
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ namespace Engine.Asset.Font;
|
||||
|
||||
public class FontIterator : IEnumerable<FontIterator.NextGlyphData>
|
||||
{
|
||||
public float MaxWidth => _maxWidth;
|
||||
public float MaxHeight => _maxHeight;
|
||||
public float MaxWidth { get; private set; }
|
||||
public float MaxHeight { get; private set; }
|
||||
|
||||
private readonly Font _font;
|
||||
private readonly string _text;
|
||||
@@ -17,44 +17,12 @@ public class FontIterator : IEnumerable<FontIterator.NextGlyphData>
|
||||
private Vector2 _cursor = Vector2.Zero;
|
||||
private Vector2 _kerning = Vector2.Zero;
|
||||
|
||||
private float _maxWidth;
|
||||
private float _maxHeight;
|
||||
|
||||
public FontIterator(Font parFont, string parText)
|
||||
{
|
||||
_font = parFont;
|
||||
_text = parText;
|
||||
}
|
||||
|
||||
private static bool IsLineBreak(int parCodepoint)
|
||||
{
|
||||
return parCodepoint == '\n';
|
||||
}
|
||||
|
||||
private static bool IsTab(int parCodepoint)
|
||||
{
|
||||
return parCodepoint == '\t';
|
||||
}
|
||||
|
||||
private void Tab()
|
||||
{
|
||||
var spaceGlyph = _font.GetGlyph(' ');
|
||||
if (spaceGlyph == null)
|
||||
return;
|
||||
|
||||
var missingSpaces = 4 - _lineCharCount % 4;
|
||||
_cursor.X += missingSpaces * spaceGlyph.Advance;
|
||||
}
|
||||
|
||||
private void LineBreak()
|
||||
{
|
||||
_kerning = Vector2.Zero;
|
||||
_cursor.X = 0;
|
||||
_cursor.Y += _font.Metadata.Metrics.LineHeight;
|
||||
_lineCharCount = 0;
|
||||
_previousCodepoint = -1;
|
||||
}
|
||||
|
||||
public IEnumerator<NextGlyphData> GetEnumerator()
|
||||
{
|
||||
while (_currentIndex < _text.Length)
|
||||
@@ -97,11 +65,13 @@ public class FontIterator : IEnumerable<FontIterator.NextGlyphData>
|
||||
|
||||
var glyphData = _font.GetGlyphData(glyph.Unicode);
|
||||
|
||||
_maxWidth = Math.Max(_maxWidth, _cursor.X + glyph.Advance);
|
||||
_maxHeight = Math.Max(_maxHeight, _cursor.Y + _font.Metadata.Metrics.Ascender);
|
||||
MaxWidth = Math.Max(MaxWidth, _cursor.X + glyph.Advance);
|
||||
MaxHeight = Math.Max(MaxHeight, _cursor.Y + _font.Metadata.Metrics.Ascender);
|
||||
|
||||
if (glyphData != null)
|
||||
{
|
||||
yield return new NextGlyphData(glyphData.Positions, glyphData.UVs, (_cursor + _kerning) * new Vector2(1, -1));
|
||||
}
|
||||
|
||||
_cursor.X += glyph.Advance;
|
||||
_lineCharCount++;
|
||||
@@ -115,5 +85,36 @@ public class FontIterator : IEnumerable<FontIterator.NextGlyphData>
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
private static bool IsLineBreak(int parCodepoint)
|
||||
{
|
||||
return parCodepoint == '\n';
|
||||
}
|
||||
|
||||
private static bool IsTab(int parCodepoint)
|
||||
{
|
||||
return parCodepoint == '\t';
|
||||
}
|
||||
|
||||
private void Tab()
|
||||
{
|
||||
var spaceGlyph = _font.GetGlyph(' ');
|
||||
if (spaceGlyph == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var missingSpaces = 4 - (_lineCharCount % 4);
|
||||
_cursor.X += missingSpaces * spaceGlyph.Advance;
|
||||
}
|
||||
|
||||
private void LineBreak()
|
||||
{
|
||||
_kerning = Vector2.Zero;
|
||||
_cursor.X = 0;
|
||||
_cursor.Y += _font.Metadata.Metrics.LineHeight;
|
||||
_lineCharCount = 0;
|
||||
_previousCodepoint = -1;
|
||||
}
|
||||
|
||||
public record NextGlyphData(Vector2[] Positions, Vector2[] UVs, Vector2 Offset);
|
||||
}
|
||||
@@ -9,8 +9,6 @@ public class Image<T> where T : struct, IPixel
|
||||
public int Height { get; }
|
||||
public T[,] Pixels { get; }
|
||||
|
||||
public T this[int parY, int parX] => Pixels[parY, parX];
|
||||
|
||||
public Image(int parWidth, int parHeight)
|
||||
{
|
||||
Width = parWidth;
|
||||
@@ -18,6 +16,8 @@ public class Image<T> where T : struct, IPixel
|
||||
Pixels = new T[parHeight, parWidth];
|
||||
}
|
||||
|
||||
public T this[int parY, int parX] => Pixels[parY, parX];
|
||||
|
||||
public DynamicTexture ToDynamicTexture()
|
||||
{
|
||||
var texture = DynamicTexture.Create<T>(Width, Height);
|
||||
|
||||
@@ -7,15 +7,15 @@ public class ObjMeshLoader : IMeshLoader
|
||||
{
|
||||
private static readonly ObjMeshLoader INSTANCE = new();
|
||||
|
||||
private ObjMeshLoader()
|
||||
{
|
||||
}
|
||||
|
||||
public static Mesh Load(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
||||
{
|
||||
return INSTANCE.LoadMesh(parReader, parParameters);
|
||||
}
|
||||
|
||||
private ObjMeshLoader()
|
||||
{
|
||||
}
|
||||
|
||||
public Mesh LoadMesh(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
||||
{
|
||||
var mesh = new Mesh();
|
||||
|
||||
@@ -7,15 +7,15 @@ public class StlMeshLoader : IMeshLoader
|
||||
{
|
||||
private static readonly StlMeshLoader INSTANCE = new();
|
||||
|
||||
private StlMeshLoader()
|
||||
{
|
||||
}
|
||||
|
||||
public static Mesh Load(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
||||
{
|
||||
return INSTANCE.LoadMesh(parReader, parParameters);
|
||||
}
|
||||
|
||||
private StlMeshLoader()
|
||||
{
|
||||
}
|
||||
|
||||
public Mesh LoadMesh(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
||||
{
|
||||
var mesh = new Mesh();
|
||||
|
||||
@@ -6,14 +6,14 @@ namespace Engine.Asset.Mesh;
|
||||
|
||||
public class Mesh
|
||||
{
|
||||
public IReadOnlyList<Vertex> Vertices => _vertices;
|
||||
public IReadOnlyList<uint> Indices => _indices;
|
||||
public IReadOnlyList<Vertex> Vertices => _vertices;
|
||||
|
||||
internal IList<Vertex> VerticesInternal => _vertices;
|
||||
internal IList<uint> IndicesInternal => _indices;
|
||||
internal IList<Vertex> VerticesInternal => _vertices;
|
||||
|
||||
private readonly List<Vertex> _vertices = [];
|
||||
private readonly List<uint> _indices = [];
|
||||
private readonly List<Vertex> _vertices = [];
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
@@ -23,9 +23,7 @@ public class Mesh
|
||||
public record struct Vertex : IVertex
|
||||
{
|
||||
[Vertex(VertexAttribType.Float, 3)] public Vector3 _position;
|
||||
|
||||
[Vertex(VertexAttribType.Float, 3)] public Vector3 _normal;
|
||||
|
||||
[Vertex(VertexAttribType.Float, 2)] public Vector2 _uv;
|
||||
}
|
||||
}
|
||||
@@ -22,12 +22,14 @@ namespace Engine;
|
||||
|
||||
public sealed class Engine
|
||||
{
|
||||
internal static Engine Instance { get; private set; } = null!;
|
||||
|
||||
public Renderer Renderer { get; }
|
||||
public IInputHandler? InputHandler { get; internal set; }
|
||||
public SceneManager SceneManager { get; } = new();
|
||||
public IResourceManager AssetResourceManager { get; }
|
||||
public string DataFolder { get; }
|
||||
|
||||
internal Window Window { get; }
|
||||
internal static Engine Instance { get; private set; } = null!;
|
||||
internal ResourceManager EngineResourceManager { get; }
|
||||
internal Renderer Renderer { get; }
|
||||
|
||||
internal IPresenter? Presenter
|
||||
{
|
||||
@@ -48,25 +50,12 @@ public sealed class Engine
|
||||
}
|
||||
}
|
||||
|
||||
public IInputHandler? InputHandler
|
||||
{
|
||||
get => _inputHandler;
|
||||
internal set => _inputHandler = value;
|
||||
}
|
||||
internal Window Window { get; }
|
||||
|
||||
private readonly ILogger _logger;
|
||||
private readonly object _sceneLock = new();
|
||||
|
||||
private IInputHandler? _inputHandler;
|
||||
private IPresenter? _presenter;
|
||||
|
||||
internal ResourceManager EngineResourceManager => _engineResourceManager;
|
||||
public IResourceManager AssetResourceManager => _assetResourceManager;
|
||||
|
||||
public string DataFolder { get; }
|
||||
|
||||
private readonly ResourceManager _engineResourceManager;
|
||||
private readonly ResourceManager _assetResourceManager;
|
||||
|
||||
private Thread? _updateThread;
|
||||
|
||||
public Engine(int parWidth, int parHeight, bool parHeadless, string parTitle, string parAssetFolder,
|
||||
@@ -94,8 +83,8 @@ public sealed class Engine
|
||||
StencilBits = 0
|
||||
};
|
||||
|
||||
_engineResourceManager = CreateEngineResourceManager();
|
||||
_assetResourceManager = CreateAssetResourceManager(parAssetFolder);
|
||||
EngineResourceManager = CreateEngineResourceManager();
|
||||
AssetResourceManager = CreateAssetResourceManager(parAssetFolder);
|
||||
|
||||
_logger.Information("Created asset resource manager in {AssetFolder}", parAssetFolder);
|
||||
|
||||
@@ -139,8 +128,6 @@ public sealed class Engine
|
||||
parResourceManager.RegisterLoader<Font>(new FontLoader());
|
||||
}
|
||||
|
||||
private readonly object _sceneLock = new();
|
||||
|
||||
public void Run()
|
||||
{
|
||||
_updateThread = new Thread(RunUpdate) { Name = "UpdateThread" };
|
||||
@@ -227,7 +214,9 @@ public sealed class Engine
|
||||
private void PresenterResize(ResizeEventArgs parEventArgs)
|
||||
{
|
||||
if (parEventArgs.Width == 0 || parEventArgs.Height == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Renderer.Resize(parEventArgs.Width, parEventArgs.Height);
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ public sealed class EngineBuilder
|
||||
private string _assetFolder = "./asset";
|
||||
private string _dataFolder = "./data";
|
||||
|
||||
private Func<Engine, IPresenter>? _presenterFunc;
|
||||
private Func<Engine, IInputHandler>? _inputHandlerFunc;
|
||||
private Func<Engine, IPresenter>? _presenterFunc;
|
||||
|
||||
// Logging
|
||||
private bool _logToConsole;
|
||||
@@ -65,24 +65,18 @@ public sealed class EngineBuilder
|
||||
return this;
|
||||
}
|
||||
|
||||
public EngineBuilder Presenter(Func<Engine, IPresenter> parPresenterFunc)
|
||||
{
|
||||
_presenterFunc = parPresenterFunc;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EngineBuilder InputHandler(IInputHandler parInputHandler)
|
||||
{
|
||||
_inputHandlerFunc = _ => parInputHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EngineBuilder InputHandler(Func<Engine, IInputHandler> parInputHandlerFunc)
|
||||
{
|
||||
_inputHandlerFunc = parInputHandlerFunc;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EngineBuilder Presenter(Func<Engine, IPresenter> parPresenterFunc)
|
||||
{
|
||||
_presenterFunc = parPresenterFunc;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EngineBuilder LogToConsole(bool parLogToConsole = true)
|
||||
{
|
||||
_logToConsole = parLogToConsole;
|
||||
@@ -92,7 +86,9 @@ public sealed class EngineBuilder
|
||||
public EngineBuilder LogToFile(bool parLogToFile = true, string? parLogFilePath = null)
|
||||
{
|
||||
if (parLogToFile && parLogFilePath == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(parLogFilePath));
|
||||
}
|
||||
|
||||
_logToFile = parLogToFile;
|
||||
_logFilePath = parLogFilePath;
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Engine.Graphics.Buffer;
|
||||
|
||||
public class VertexArray : OpenGlObject
|
||||
{
|
||||
private int _enabledAttribs = 0;
|
||||
private int _enabledAttributes;
|
||||
|
||||
public VertexArray()
|
||||
{
|
||||
@@ -41,9 +41,9 @@ public class VertexArray : OpenGlObject
|
||||
var attribute = field.GetCustomAttribute<VertexAttribute>()!;
|
||||
|
||||
var offset = Marshal.OffsetOf<T>(field.Name).ToInt32();
|
||||
SetupAttribute(attribute, _enabledAttribs, offset, parBindingIndex);
|
||||
SetupAttribute(attribute, _enabledAttributes, offset, parBindingIndex);
|
||||
|
||||
_enabledAttribs += attribute.RepeatCount;
|
||||
_enabledAttributes += attribute.RepeatCount;
|
||||
}
|
||||
|
||||
GL.VertexArrayBindingDivisor(Handle, parBindingIndex, parDivisor);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Evergine.Bindings.RenderDoc;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using Evergine.Bindings.RenderDoc;
|
||||
|
||||
namespace Engine.Graphics;
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ public class Framebuffer : OpenGlObject
|
||||
internal Texture.Texture? TextureInternal => GetAttachment<DynamicTexture>(FramebufferAttachment.ColorAttachment0);
|
||||
|
||||
private readonly IDictionary<FramebufferAttachment, IFramebufferAttachment> _attachments;
|
||||
|
||||
private int _width;
|
||||
private int _height;
|
||||
|
||||
|
||||
@@ -13,13 +13,13 @@ public class GenericRenderer : IRenderer
|
||||
public AnyMeshRenderer AnyMeshRenderer => _anyMeshRenderer ??= new AnyMeshRenderer(_engine, 1024);
|
||||
public TextRenderer TextRenderer => _textRenderer ??= new TextRenderer(_engine, 1024 * 8);
|
||||
|
||||
internal readonly Framebuffer.Framebuffer _framebuffer;
|
||||
|
||||
private readonly Engine _engine;
|
||||
|
||||
private QuadRenderer? _quadRenderer;
|
||||
private AnyMeshRenderer? _anyMeshRenderer;
|
||||
private TextRenderer? _textRenderer;
|
||||
|
||||
private readonly Engine _engine;
|
||||
internal readonly Framebuffer.Framebuffer _framebuffer;
|
||||
|
||||
private bool _frameStarted;
|
||||
|
||||
public GenericRenderer(Engine parEngine, int parWidth, int parHeight)
|
||||
@@ -40,7 +40,9 @@ public class GenericRenderer : IRenderer
|
||||
public void EndFrame(in Matrix4 parProjectionMatrix, in Matrix4 parViewMatrix)
|
||||
{
|
||||
if (!_frameStarted)
|
||||
{
|
||||
throw new InvalidOperationException("Frame not started");
|
||||
}
|
||||
|
||||
_framebuffer.Bind();
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@ namespace Engine.Graphics;
|
||||
|
||||
public interface IPresenter : IUpdate, IRender
|
||||
{
|
||||
bool IsExiting { get; }
|
||||
int Width { get; }
|
||||
int Height { get; }
|
||||
public event Action<ResizeEventArgs> Resize;
|
||||
|
||||
event Action<ResizeEventArgs> Resize;
|
||||
public int Width { get; }
|
||||
public int Height { get; }
|
||||
public bool IsExiting { get; }
|
||||
|
||||
void Present(IConstTexture parTexture);
|
||||
void Exit();
|
||||
public void Present(IConstTexture parTexture);
|
||||
public void Exit();
|
||||
}
|
||||
@@ -5,10 +5,10 @@ public class RenderLayer : IComparable<RenderLayer>
|
||||
public static readonly RenderLayer DEFAULT = new("default", 0);
|
||||
public static readonly RenderLayer OVERLAY = new("overlay", 1);
|
||||
public static readonly RenderLayer HUD = new("hud", 2);
|
||||
|
||||
public static readonly IReadOnlyList<RenderLayer> ALL = new List<RenderLayer> { DEFAULT, OVERLAY, HUD }.AsReadOnly();
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
private readonly int _order;
|
||||
|
||||
private RenderLayer(string parName, int parOrder)
|
||||
@@ -17,16 +17,16 @@ public class RenderLayer : IComparable<RenderLayer>
|
||||
_order = parOrder;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
public int CompareTo(RenderLayer? parOther)
|
||||
{
|
||||
return parOther == null ? 1 : _order.CompareTo(parOther._order);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Name.GetHashCode();
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Engine.Graphics.Pipeline;
|
||||
|
||||
public class RenderPipeline
|
||||
{
|
||||
|
||||
}
|
||||
@@ -10,18 +10,18 @@ public abstract class InstancedRenderer<C, I>
|
||||
where C : struct, IVertex
|
||||
where I : struct, IVertex
|
||||
{
|
||||
protected readonly int _instanceCount;
|
||||
protected readonly I[] _instanceVertices;
|
||||
|
||||
protected int _queuedInstanceCount;
|
||||
|
||||
private readonly PrimitiveType _primitiveType;
|
||||
private readonly Program _program;
|
||||
private readonly IndexBuffer _indexBuffer;
|
||||
private readonly VertexBuffer<C> _commonVertexBuffer;
|
||||
private readonly VertexBuffer<I> _instanceVertexBuffer;
|
||||
private readonly VertexArray _vertexArray;
|
||||
|
||||
protected readonly int _instanceCount;
|
||||
protected int _queuedInstanceCount;
|
||||
protected readonly I[] _instanceVertices;
|
||||
|
||||
private readonly PrimitiveType _primitiveType;
|
||||
private readonly Program _program;
|
||||
|
||||
protected InstancedRenderer(PrimitiveType parPrimitiveType, int parInstanceCount,
|
||||
uint[] parIndexBuffer, C[] parInstanceBuffer,
|
||||
Program parProgram)
|
||||
@@ -39,7 +39,7 @@ public abstract class InstancedRenderer<C, I>
|
||||
_vertexArray = new VertexArray();
|
||||
|
||||
_vertexArray.BindIndexBuffer(_indexBuffer);
|
||||
_vertexArray.BindVertexBuffer(_commonVertexBuffer, 0, 0);
|
||||
_vertexArray.BindVertexBuffer(_commonVertexBuffer);
|
||||
_vertexArray.BindVertexBuffer(_instanceVertexBuffer, 1, 1);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,10 @@ public abstract class InstancedRenderer<C, I>
|
||||
}
|
||||
|
||||
if (DataChanged())
|
||||
{
|
||||
_instanceVertexBuffer.UploadData(_instanceVertices, _queuedInstanceCount);
|
||||
}
|
||||
|
||||
_vertexArray.Bind();
|
||||
|
||||
_program.Bind();
|
||||
|
||||
@@ -46,7 +46,9 @@ public class AnyMeshRenderer(Engine parEngine, int parMaxInstanceCount)
|
||||
foreach (var mesh in meshes)
|
||||
{
|
||||
if (!_frameMeshes.Contains(mesh))
|
||||
{
|
||||
_meshRenderers.Remove(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
_frameMeshes.Clear();
|
||||
|
||||
@@ -8,7 +8,6 @@ namespace Engine.Graphics.Render.Quad;
|
||||
public class QuadRenderer : InstancedRenderer<QuadCommonVertex, QuadInstanceVertex>
|
||||
{
|
||||
private readonly TextureUnitMap _textureUnitMap = new(16);
|
||||
|
||||
private readonly int[] _textureUnitIndices = new int[16];
|
||||
|
||||
private int _frameHash;
|
||||
@@ -19,7 +18,7 @@ public class QuadRenderer : InstancedRenderer<QuadCommonVertex, QuadInstanceVert
|
||||
new QuadCommonVertex { _position = new Vector3(-0.5f, -0.5f, 0), _uv = new Vector2(0, 0) },
|
||||
new QuadCommonVertex { _position = new Vector3(-0.5f, 0.5f, 0), _uv = new Vector2(0, 1) },
|
||||
new QuadCommonVertex { _position = new Vector3(0.5f, -0.5f, 0), _uv = new Vector2(1, 0) },
|
||||
new QuadCommonVertex { _position = new Vector3(0.5f, 0.5f, 0), _uv = new Vector2(1, 1) },
|
||||
new QuadCommonVertex { _position = new Vector3(0.5f, 0.5f, 0), _uv = new Vector2(1, 1) }
|
||||
],
|
||||
parEngine.EngineResourceManager.Load<Program>("shader/quad"))
|
||||
{
|
||||
|
||||
@@ -9,41 +9,34 @@ namespace Engine.Graphics.Render.Text;
|
||||
|
||||
public class TextRenderer
|
||||
{
|
||||
private readonly Program _program;
|
||||
private readonly IndexBuffer _indexBuffer;
|
||||
private readonly VertexBuffer<GlyphVertex> _glyphVertexBuffer;
|
||||
private readonly VertexArray _vertexArray;
|
||||
|
||||
private readonly VertexBuffer<GlyphVertex> _glyphVertexBuffer;
|
||||
// private readonly VertexBuffer<GlyphCommonVertex> _glyphCommonVertexBuffer;
|
||||
|
||||
private readonly Program _program;
|
||||
private readonly int _characterCount;
|
||||
private int _queuedCharacterCount;
|
||||
private readonly GlyphVertex[] _glyphVertices;
|
||||
|
||||
private readonly TextureUnitMap _textureUnitMap = new(16);
|
||||
private readonly int[] _textureUnitIndices = new int[16];
|
||||
|
||||
private readonly GlyphVertex[] _glyphVertices;
|
||||
// private readonly GlyphCommonVertex[] _glyphCommonVertices;
|
||||
private int _queuedCharacterCount;
|
||||
|
||||
public TextRenderer(Engine parEngine, int parCharacterCount)
|
||||
{
|
||||
_characterCount = parCharacterCount;
|
||||
_glyphVertices = new GlyphVertex[parCharacterCount * 4];
|
||||
// _glyphCommonVertices = new GlyphCommonVertex[parCharacterCount];
|
||||
|
||||
_program = parEngine.EngineResourceManager.Load<Program>("shader/text");
|
||||
|
||||
_indexBuffer = new IndexBuffer(CreateIndices(_characterCount));
|
||||
_glyphVertexBuffer = new VertexBuffer<GlyphVertex>(_characterCount * 4,
|
||||
BufferStorageFlags.DynamicStorageBit);
|
||||
// _glyphCommonVertexBuffer = new VertexBuffer<GlyphCommonVertex>(_characterCount,
|
||||
// BufferStorageFlags.DynamicStorageBit);
|
||||
|
||||
_vertexArray = new VertexArray();
|
||||
|
||||
_vertexArray.BindIndexBuffer(_indexBuffer);
|
||||
_vertexArray.BindVertexBuffer(_glyphVertexBuffer, 0, 0);
|
||||
// _vertexArray.BindVertexBuffer(_glyphCommonVertexBuffer, 1, 1);
|
||||
_vertexArray.BindVertexBuffer(_glyphVertexBuffer);
|
||||
}
|
||||
|
||||
public void Commit(Font parFont, string parText, Vector4 parColor, in Matrix4 parModelMatrix)
|
||||
@@ -61,12 +54,12 @@ public class TextRenderer
|
||||
{
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
_glyphVertices[_queuedCharacterCount * 4 + i]._position = glyphData.Positions[i] + glyphData.Offset;
|
||||
_glyphVertices[_queuedCharacterCount * 4 + i]._uv = glyphData.UVs[i];
|
||||
_glyphVertices[_queuedCharacterCount * 4 + i]._color = parColor;
|
||||
_glyphVertices[_queuedCharacterCount * 4 + i]._atlasId = textureId;
|
||||
_glyphVertices[_queuedCharacterCount * 4 + i]._unitRange = parFont.UnitRange;
|
||||
_glyphVertices[_queuedCharacterCount * 4 + i]._modelMatrix = parModelMatrix;
|
||||
_glyphVertices[(_queuedCharacterCount * 4) + i]._position = glyphData.Positions[i] + glyphData.Offset;
|
||||
_glyphVertices[(_queuedCharacterCount * 4) + i]._uv = glyphData.UVs[i];
|
||||
_glyphVertices[(_queuedCharacterCount * 4) + i]._color = parColor;
|
||||
_glyphVertices[(_queuedCharacterCount * 4) + i]._atlasId = textureId;
|
||||
_glyphVertices[(_queuedCharacterCount * 4) + i]._unitRange = parFont.UnitRange;
|
||||
_glyphVertices[(_queuedCharacterCount * 4) + i]._modelMatrix = parModelMatrix;
|
||||
}
|
||||
|
||||
_queuedCharacterCount++;
|
||||
@@ -81,7 +74,6 @@ public class TextRenderer
|
||||
}
|
||||
|
||||
_glyphVertexBuffer.UploadData(_glyphVertices, _queuedCharacterCount * 4);
|
||||
// _glyphCommonVertexBuffer.UploadData(_glyphCommonVertices, _queuedCharacterCount);
|
||||
_vertexArray.Bind();
|
||||
|
||||
_program.Bind();
|
||||
|
||||
@@ -9,31 +9,18 @@ namespace Engine.Graphics;
|
||||
|
||||
public class Renderer
|
||||
{
|
||||
internal Framebuffer.Framebuffer RenderFramebuffer => _framebuffer;
|
||||
internal Texture.Texture RenderTexture => _framebuffer.TextureInternal!;
|
||||
|
||||
private QuadRenderer QuadRenderer { get; }
|
||||
public int ViewportWidth => _framebuffer.Width;
|
||||
public int ViewportHeight => _framebuffer.Height;
|
||||
|
||||
private readonly SortedDictionary<RenderLayer, GenericRenderer> _renderers = new();
|
||||
|
||||
public GenericRenderer this[RenderLayer parRenderLayer]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_renderers.TryGetValue(parRenderLayer, out var renderer))
|
||||
return renderer;
|
||||
|
||||
throw new InvalidOperationException($"Renderer for layer {parRenderLayer} not found");
|
||||
}
|
||||
}
|
||||
public int ViewportWidth => RenderFramebuffer.Width;
|
||||
public int ViewportHeight => RenderFramebuffer.Height;
|
||||
|
||||
internal Framebuffer.Framebuffer RenderFramebuffer { get; }
|
||||
internal Texture.Texture RenderTexture => RenderFramebuffer.TextureInternal!;
|
||||
internal NativeWindow NativeWindow { get; }
|
||||
|
||||
private readonly Framebuffer.Framebuffer _framebuffer;
|
||||
private readonly SortedDictionary<RenderLayer, GenericRenderer> _renderers = new();
|
||||
private readonly Thread _renderThread;
|
||||
|
||||
private QuadRenderer QuadRenderer { get; }
|
||||
|
||||
private readonly Queue<Action> _scheduleActions = new();
|
||||
|
||||
public Renderer(Engine parEngine, int parWidth, int parHeight, NativeWindowSettings parSettings)
|
||||
@@ -49,7 +36,7 @@ public class Renderer
|
||||
|
||||
InitializeOpenGl(parWidth, parHeight);
|
||||
|
||||
_framebuffer = Framebuffer.Framebuffer.Builder(parWidth, parHeight)
|
||||
RenderFramebuffer = Framebuffer.Framebuffer.Builder(parWidth, parHeight)
|
||||
.AddColorAttachment<Rgba8>()
|
||||
.Build();
|
||||
|
||||
@@ -61,6 +48,19 @@ public class Renderer
|
||||
}
|
||||
}
|
||||
|
||||
public GenericRenderer this[RenderLayer parRenderLayer]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_renderers.TryGetValue(parRenderLayer, out var renderer))
|
||||
{
|
||||
return renderer;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"Renderer for layer {parRenderLayer} not found");
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeOpenGl(int parWidth, int parHeight)
|
||||
{
|
||||
#if DEBUG
|
||||
@@ -129,15 +129,16 @@ public class Renderer
|
||||
foreach (var (renderLayer, renderer) in _renderers)
|
||||
{
|
||||
if (!parMatrices.TryGetValue(renderLayer, out var matrices))
|
||||
{
|
||||
renderer.EndFrame(Matrix4.Identity, Matrix4.Identity);
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer.EndFrame(in matrices.Item1, in matrices.Item2);
|
||||
}
|
||||
}
|
||||
|
||||
_framebuffer.Bind();
|
||||
|
||||
// GL.Disable(EnableCap.DepthTest);
|
||||
// GL.Disable(EnableCap.CullFace);
|
||||
RenderFramebuffer.Bind();
|
||||
|
||||
GL.ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit);
|
||||
@@ -145,22 +146,21 @@ public class Renderer
|
||||
foreach (var (renderLayer, renderer) in _renderers)
|
||||
{
|
||||
if (!parMatrices.ContainsKey(renderLayer))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QuadRenderer.Commit(Matrix4.CreateScale(2f, -2f, 1f), Vector4.One, renderer._framebuffer.TextureInternal);
|
||||
QuadRenderer.Render(Matrix4.Identity, Matrix4.Identity);
|
||||
QuadRenderer.Reset();
|
||||
}
|
||||
|
||||
// GL.Enable(EnableCap.DepthTest);
|
||||
// GL.Enable(EnableCap.CullFace);
|
||||
|
||||
_framebuffer.Unbind();
|
||||
RenderFramebuffer.Unbind();
|
||||
}
|
||||
|
||||
internal void Resize(int parWidth, int parHeight)
|
||||
{
|
||||
_framebuffer.Resize(parWidth, parHeight);
|
||||
RenderFramebuffer.Resize(parWidth, parHeight);
|
||||
GL.Viewport(0, 0, parWidth, parHeight);
|
||||
|
||||
foreach (var renderer in _renderers.Values)
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
using Engine.Asset;
|
||||
using Engine.Graphics.Pixel;
|
||||
using OpenTK.Mathematics;
|
||||
|
||||
namespace Engine.Graphics.Texture;
|
||||
|
||||
public interface IConstTexture
|
||||
{
|
||||
public Vector2i Size { get; }
|
||||
public int Width { get; }
|
||||
public int Height { get; }
|
||||
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Engine.Graphics.Pixel;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using OpenTK.Mathematics;
|
||||
using Serilog;
|
||||
|
||||
namespace Engine.Graphics.Texture;
|
||||
|
||||
public abstract class Texture : OpenGlObject, ITexture
|
||||
{
|
||||
public Vector2i Size => new(Width, Height);
|
||||
|
||||
public int Width
|
||||
{
|
||||
get => _width;
|
||||
|
||||
@@ -66,12 +66,16 @@ public enum KeyboardButtonCode
|
||||
|
||||
public static class KeyboardButtonCodeHelper
|
||||
{
|
||||
public static List<KeyboardButtonCode> GetAllPrintableKeys() =>
|
||||
Enum.GetValues<KeyboardButtonCode>().Where(parX => parX.IsPrintableKey()).ToList();
|
||||
public static List<KeyboardButtonCode> GetAllPrintableKeys()
|
||||
{
|
||||
return Enum.GetValues<KeyboardButtonCode>().Where(parX => parX.IsPrintableKey()).ToList();
|
||||
}
|
||||
|
||||
public static bool IsPrintableKey(this KeyboardButtonCode parKey) =>
|
||||
parKey is >= KeyboardButtonCode.A and <= KeyboardButtonCode.Z
|
||||
public static bool IsPrintableKey(this KeyboardButtonCode parKey)
|
||||
{
|
||||
return parKey is >= KeyboardButtonCode.A and <= KeyboardButtonCode.Z
|
||||
or >= KeyboardButtonCode.D1 and <= KeyboardButtonCode.D0 or KeyboardButtonCode.Space;
|
||||
}
|
||||
|
||||
public static char GetChar(this KeyboardButtonCode parKey)
|
||||
{
|
||||
|
||||
@@ -9,11 +9,10 @@ public class WindowInputHandler(Window parWindow) : IInputHandler
|
||||
public CultureInfo CurrentInputLanguage => new(1033);
|
||||
public Vector2 MousePosition => parWindow.NativeWindow.MouseState.Position;
|
||||
|
||||
private KeyboardState _previousKeyboardState = parWindow.NativeWindow.KeyboardState.GetSnapshot();
|
||||
private KeyboardState _keyboardState = parWindow.NativeWindow.KeyboardState.GetSnapshot();
|
||||
|
||||
private MouseState _previousMouseState = parWindow.NativeWindow.MouseState.GetSnapshot();
|
||||
private KeyboardState _previousKeyboardState = parWindow.NativeWindow.KeyboardState.GetSnapshot();
|
||||
private MouseState _mouseState = parWindow.NativeWindow.MouseState.GetSnapshot();
|
||||
private MouseState _previousMouseState = parWindow.NativeWindow.MouseState.GetSnapshot();
|
||||
|
||||
public void Update(double parDeltaTime)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Engine.Graphics.Pixel;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
|
||||
@@ -15,10 +16,10 @@ public class ImageLoader : IResourceLoader
|
||||
|
||||
internal static Asset.Image<Rgba8> Load(Stream parStream)
|
||||
{
|
||||
var sharpImage = SixLabors.ImageSharp.Image.Load<Rgba32>(parStream);
|
||||
var sharpImage = Image.Load<Rgba32>(parStream);
|
||||
if (sharpImage == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to load image from stream");
|
||||
throw new InvalidOperationException("Failed to load image from stream");
|
||||
}
|
||||
|
||||
sharpImage.Mutate(parImageContext => parImageContext.Flip(FlipMode.Vertical));
|
||||
|
||||
@@ -6,9 +6,6 @@ namespace Engine.Resource.Loader;
|
||||
|
||||
public partial class ProgramLoader : IResourceLoader
|
||||
{
|
||||
[GeneratedRegex(@"^//\s+#type\s+(?<type>[a-z]+)$", RegexOptions.Compiled)]
|
||||
private static partial Regex TypeRegex();
|
||||
|
||||
public object Load(string parPath, IResourceStreamProvider parStreamProvider)
|
||||
{
|
||||
var textReader = new StreamReader(parStreamProvider.GetStream(parPath));
|
||||
@@ -38,4 +35,7 @@ public partial class ProgramLoader : IResourceLoader
|
||||
|
||||
return new Program(vertexSource.ToString(), fragmentSource.ToString());
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"^//\s+#type\s+(?<type>[a-z]+)$", RegexOptions.Compiled)]
|
||||
private static partial Regex TypeRegex();
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
namespace Engine.Resource;
|
||||
|
||||
public class ResourceHandle<T>
|
||||
{
|
||||
public T? Value
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_isLoaded)
|
||||
{
|
||||
return _value!;
|
||||
}
|
||||
|
||||
if (!_task.IsCompleted)
|
||||
{
|
||||
return _defaultValue;
|
||||
}
|
||||
|
||||
_value = _task.Result;
|
||||
_isLoaded = true;
|
||||
|
||||
return _value!;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly T? _defaultValue;
|
||||
private readonly Task<T> _task;
|
||||
private T? _value;
|
||||
private bool _isLoaded;
|
||||
|
||||
public ResourceHandle(Task<T> parTask, T? parDefaultValue)
|
||||
{
|
||||
_defaultValue = parDefaultValue;
|
||||
_task = parTask;
|
||||
}
|
||||
}
|
||||
@@ -2,20 +2,14 @@
|
||||
|
||||
public class ResourceManager : IResourceManager
|
||||
{
|
||||
internal IResourceStreamProvider StreamProvider => _streamProvider;
|
||||
internal IResourceStreamProvider StreamProvider { get; }
|
||||
|
||||
private readonly IResourceStreamProvider _streamProvider;
|
||||
private readonly Dictionary<Type, IResourceLoader> _loaders = new();
|
||||
private readonly Dictionary<Type, ResourceStorage> _storages = new();
|
||||
|
||||
public ResourceManager(IResourceStreamProvider parStreamProvider)
|
||||
{
|
||||
_streamProvider = parStreamProvider;
|
||||
}
|
||||
|
||||
internal void RegisterLoader<T>(IResourceLoader parLoader) where T : class
|
||||
{
|
||||
_loaders.Add(typeof(T), parLoader);
|
||||
StreamProvider = parStreamProvider;
|
||||
}
|
||||
|
||||
public T Load<T>(string parPath) where T : class
|
||||
@@ -37,13 +31,18 @@ public class ResourceManager : IResourceManager
|
||||
throw new InvalidOperationException($"No loader found for type {typeof(T)}");
|
||||
}
|
||||
|
||||
var resource = loader.Load(parPath, _streamProvider);
|
||||
var resource = loader.Load(parPath, StreamProvider);
|
||||
|
||||
storage.Add(parPath, resource);
|
||||
|
||||
return (T)resource;
|
||||
}
|
||||
|
||||
internal void RegisterLoader<T>(IResourceLoader parLoader) where T : class
|
||||
{
|
||||
_loaders.Add(typeof(T), parLoader);
|
||||
}
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
_storages.Clear();
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<root>
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
|
||||
id="root"
|
||||
xmlns="">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
|
||||
</xsd:element>
|
||||
|
||||
@@ -11,13 +11,18 @@ public class OrthographicCamera(
|
||||
)
|
||||
: Camera(parNearPlane, parFarPlane)
|
||||
{
|
||||
public Axis FixedAxis { get; set; } = parAxis;
|
||||
public float Size { get; set; } = parSize;
|
||||
public bool UseScreenSize { get; set; } = false;
|
||||
|
||||
public override Matrix4 View => GameObject.Transform.TransformMatrix.Inverted();
|
||||
public enum Axis
|
||||
{
|
||||
X,
|
||||
Y
|
||||
}
|
||||
|
||||
public override Matrix4 Projection => GetProjectionMatrix();
|
||||
public override Matrix4 View => GameObject.Transform.TransformMatrix.Inverted();
|
||||
|
||||
public float Size { get; set; } = parSize;
|
||||
public bool UseScreenSize { get; set; } = false;
|
||||
public Axis FixedAxis { get; set; } = parAxis;
|
||||
|
||||
private Matrix4 GetProjectionMatrix()
|
||||
{
|
||||
@@ -27,7 +32,7 @@ public class OrthographicCamera(
|
||||
|
||||
public override Vector3 ScreenToWorld(Vector2 parScreenPosition)
|
||||
{
|
||||
var normalized = parScreenPosition / ScreenSize - new Vector2(0.5f);
|
||||
var normalized = (parScreenPosition / ScreenSize) - new Vector2(0.5f);
|
||||
normalized.X *= 2;
|
||||
normalized.Y *= -2;
|
||||
|
||||
@@ -70,10 +75,4 @@ public class OrthographicCamera(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum Axis
|
||||
{
|
||||
X,
|
||||
Y
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,6 @@ public class PerspectiveCamera(
|
||||
)
|
||||
: Camera(parNearPlane, parFarPlane)
|
||||
{
|
||||
public float FieldOfView { get; set; } = parFieldOfView;
|
||||
|
||||
public Vector3 Forward => new Vector4(0, 1, 0, 1).MulProject(GameObject.Transform.TransformMatrix).Xyz;
|
||||
public override Matrix4 View
|
||||
{
|
||||
get
|
||||
@@ -30,9 +27,12 @@ public class PerspectiveCamera(
|
||||
Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(FieldOfView), AspectRatio,
|
||||
NearPlane, FarPlane);
|
||||
|
||||
public Vector3 Forward => new Vector4(0, 1, 0, 1).MulProject(GameObject.Transform.TransformMatrix).Xyz;
|
||||
public float FieldOfView { get; set; } = parFieldOfView;
|
||||
|
||||
public override Vector3 ScreenToWorld(Vector2 parScreenPosition)
|
||||
{
|
||||
var normalized = parScreenPosition / ScreenSize - new Vector2(0.5f);
|
||||
var normalized = (parScreenPosition / ScreenSize) - new Vector2(0.5f);
|
||||
normalized.X *= 2;
|
||||
normalized.Y *= -2;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Engine.Scene.Component.BuiltIn.Renderer;
|
||||
public class Box2DRenderer : Component
|
||||
{
|
||||
public ref Vector4 Color => ref _color;
|
||||
public Texture? Texture { get; set; } = null;
|
||||
public Texture? Texture { get; set; }
|
||||
public RenderLayer RenderLayer { get; set; } = RenderLayer.DEFAULT;
|
||||
|
||||
private Vector4 _color = Vector4.One;
|
||||
|
||||
@@ -7,9 +7,8 @@ namespace Engine.Scene.Component.BuiltIn.Renderer;
|
||||
public class TextRenderer : Component
|
||||
{
|
||||
public Font Font { get; set; } = null!;
|
||||
public string? Text { get; set; }
|
||||
public ref Vector4 Color => ref _color;
|
||||
|
||||
public string? Text { get; set; }
|
||||
public RenderLayer RenderLayer { get; set; } = RenderLayer.DEFAULT;
|
||||
|
||||
private Vector4 _color = Vector4.One;
|
||||
@@ -17,7 +16,9 @@ public class TextRenderer : Component
|
||||
public override void Render()
|
||||
{
|
||||
if (Text == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Engine.Instance.Renderer[RenderLayer].TextRenderer
|
||||
.Commit(Font, Text, Color, GameObject.Transform.FullTransformMatrix);
|
||||
|
||||
@@ -4,24 +4,18 @@ namespace Engine.Scene.Component.BuiltIn;
|
||||
|
||||
public class Transform : Component
|
||||
{
|
||||
private Vector3 _translation = Vector3.Zero;
|
||||
private Quaternion _rotation = Quaternion.Identity;
|
||||
private Vector3 _scale = Vector3.One;
|
||||
private Vector3 _size = Vector3.One;
|
||||
|
||||
public ref Vector3 Translation => ref _translation;
|
||||
public ref Quaternion Rotation => ref _rotation;
|
||||
public ref Vector3 Scale => ref _scale;
|
||||
public ref Vector3 Size => ref _size;
|
||||
public ref Vector3 Scale => ref _scale;
|
||||
public ref Quaternion Rotation => ref _rotation;
|
||||
public ref Vector3 Translation => ref _translation;
|
||||
|
||||
public Matrix4 FullTransformMatrix => Matrix4.CreateScale(Size) * TransformMatrix;
|
||||
public Matrix4 TransformMatrix => LocalTransformMatrix * ParentTransformMatrix;
|
||||
|
||||
public Matrix4 LocalTransformMatrix => Matrix4.CreateScale(Scale) *
|
||||
Matrix4.CreateFromQuaternion(Rotation) *
|
||||
Matrix4.CreateTranslation(Translation);
|
||||
|
||||
public Matrix4 TransformMatrix => LocalTransformMatrix * ParentTransformMatrix;
|
||||
|
||||
public Matrix4 FullTransformMatrix => Matrix4.CreateScale(Size) * TransformMatrix;
|
||||
|
||||
private Matrix4 ParentTransformMatrix
|
||||
{
|
||||
get
|
||||
@@ -31,6 +25,11 @@ public class Transform : Component
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3 _size = Vector3.One;
|
||||
private Vector3 _scale = Vector3.One;
|
||||
private Quaternion _rotation = Quaternion.Identity;
|
||||
private Vector3 _translation = Vector3.Zero;
|
||||
|
||||
public Vector3 GetFullTranslation()
|
||||
{
|
||||
return FullTransformMatrix.ExtractTranslation();
|
||||
@@ -43,15 +42,4 @@ public class Transform : Component
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
public float SquaredDistanceTo(Transform parTransform)
|
||||
{
|
||||
var translation = GetFullTranslation();
|
||||
var otherTranslation = parTransform.GetFullTranslation();
|
||||
|
||||
var difference = translation - otherTranslation;
|
||||
var squaredDistance = difference.LengthSquared;
|
||||
|
||||
return squaredDistance;
|
||||
}
|
||||
}
|
||||
@@ -14,23 +14,22 @@ public sealed class GameObject : IUpdate, IRender
|
||||
set => _nextIsSelfEnabled = value;
|
||||
}
|
||||
|
||||
private bool IsSelfEnabled { get; set; } = true;
|
||||
private bool _prevIsSelfEnabled = true;
|
||||
private bool _nextIsSelfEnabled = true;
|
||||
private bool IsParentEnabled => Scene?.Hierarchy.GetParent(this)?.IsEnabled ?? true;
|
||||
|
||||
public Transform Transform { get; }
|
||||
|
||||
internal Scene? Scene { get; set; }
|
||||
|
||||
private readonly Queue<Action> _componentActions = new();
|
||||
|
||||
private readonly List<Component.Component> _components = [];
|
||||
private readonly HashSet<Type> _addedComponentTypes = [];
|
||||
private bool IsParentEnabled => Scene?.Hierarchy.GetParent(this)?.IsEnabled ?? true;
|
||||
private bool IsSelfEnabled { get; set; } = true;
|
||||
|
||||
private readonly HashSet<Component.Component> _addedComponents = [];
|
||||
private readonly HashSet<Type> _addedComponentTypes = [];
|
||||
private readonly Queue<Action> _componentActions = new();
|
||||
private readonly List<Component.Component> _components = [];
|
||||
private readonly HashSet<Component.Component> _removedComponents = [];
|
||||
|
||||
private bool _nextIsSelfEnabled = true;
|
||||
private bool _prevIsSelfEnabled = true;
|
||||
|
||||
public GameObject()
|
||||
{
|
||||
AddComponent<Transform>();
|
||||
@@ -58,34 +57,14 @@ public sealed class GameObject : IUpdate, IRender
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessAddedComponents()
|
||||
{
|
||||
foreach (var component in _addedComponents)
|
||||
{
|
||||
component.Awake();
|
||||
component.Start();
|
||||
}
|
||||
|
||||
_addedComponents.Clear();
|
||||
}
|
||||
|
||||
private void ProcessRemovedComponents()
|
||||
{
|
||||
foreach (var component in _removedComponents)
|
||||
{
|
||||
component.Destroy();
|
||||
component.GameObject = null!;
|
||||
}
|
||||
|
||||
_removedComponents.Clear();
|
||||
}
|
||||
|
||||
public void Update(double parDeltaTime)
|
||||
{
|
||||
if (!IsEnabled)
|
||||
{
|
||||
if (!_prevIsSelfEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var component in _components)
|
||||
{
|
||||
@@ -115,7 +94,9 @@ public sealed class GameObject : IUpdate, IRender
|
||||
public void Render()
|
||||
{
|
||||
if (!IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var component in _components)
|
||||
{
|
||||
@@ -134,12 +115,16 @@ public sealed class GameObject : IUpdate, IRender
|
||||
public T? GetComponent<T>() where T : Component.Component
|
||||
{
|
||||
if (!HasComponent<T>())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (var component in _components)
|
||||
{
|
||||
if (component is T result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -149,7 +134,9 @@ public sealed class GameObject : IUpdate, IRender
|
||||
{
|
||||
var component = GetComponent<T>();
|
||||
if (component != null)
|
||||
{
|
||||
return component;
|
||||
}
|
||||
|
||||
component = GetComponentInChildren<T>();
|
||||
return component;
|
||||
@@ -163,11 +150,15 @@ public sealed class GameObject : IUpdate, IRender
|
||||
{
|
||||
var component = child.GetComponent<T>();
|
||||
if (component != null)
|
||||
{
|
||||
return component;
|
||||
}
|
||||
|
||||
var childComponent = child.GetComponentInChildren<T>();
|
||||
if (childComponent != null)
|
||||
{
|
||||
return childComponent;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -257,6 +248,28 @@ public sealed class GameObject : IUpdate, IRender
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessAddedComponents()
|
||||
{
|
||||
foreach (var component in _addedComponents)
|
||||
{
|
||||
component.Awake();
|
||||
component.Start();
|
||||
}
|
||||
|
||||
_addedComponents.Clear();
|
||||
}
|
||||
|
||||
private void ProcessRemovedComponents()
|
||||
{
|
||||
foreach (var component in _removedComponents)
|
||||
{
|
||||
component.Destroy();
|
||||
component.GameObject = null!;
|
||||
}
|
||||
|
||||
_removedComponents.Clear();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Id.ToString();
|
||||
|
||||
@@ -10,7 +10,6 @@ public class Hierarchy<T>
|
||||
|
||||
private readonly Dictionary<NullableObject<T>, IList<T>> _childrenLookup = new();
|
||||
private readonly Dictionary<T, T?> _parentLookup = new();
|
||||
|
||||
private readonly ConcurrentQueue<Action> _hierarchyActions = new();
|
||||
|
||||
public Hierarchy()
|
||||
@@ -51,7 +50,9 @@ public class Hierarchy<T>
|
||||
_hierarchyActions.Enqueue(() =>
|
||||
{
|
||||
if (!Contains(parObj))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var parent = GetParent(parObj);
|
||||
_childrenLookup[parent].Remove(parObj);
|
||||
|
||||
@@ -7,49 +7,14 @@ namespace Engine.Scene;
|
||||
public class Scene : IUpdate, IRender
|
||||
{
|
||||
public bool IsPlaying { get; private set; }
|
||||
public IReadOnlyDictionary<RenderLayer, ICamera> Cameras => _cameras;
|
||||
public float TimeScale { get; set; } = 1.0f;
|
||||
|
||||
private readonly Dictionary<RenderLayer, ICamera> _cameras = new();
|
||||
|
||||
internal Hierarchy<GameObject> Hierarchy { get; } = new();
|
||||
internal IReadOnlyDictionary<RenderLayer, ICamera> Cameras => _cameras;
|
||||
|
||||
private readonly Dictionary<RenderLayer, ICamera> _cameras = new();
|
||||
private readonly Queue<Action> _sceneActions = [];
|
||||
|
||||
internal void Enter()
|
||||
{
|
||||
if (IsPlaying)
|
||||
{
|
||||
throw new InvalidOperationException("Scene is already playing");
|
||||
}
|
||||
|
||||
ProcessChanges();
|
||||
|
||||
var allCameras = FindAllComponents<Camera>();
|
||||
foreach (var camera in allCameras)
|
||||
{
|
||||
_cameras.Add(camera.RenderLayer, camera);
|
||||
}
|
||||
|
||||
IsPlaying = true;
|
||||
}
|
||||
|
||||
public List<T> FindAllComponents<T>(bool parOnlyEnabled = true) where T : Component.Component
|
||||
{
|
||||
return Hierarchy.Objects
|
||||
.Where(parGameObject => !parOnlyEnabled || parGameObject.IsEnabled)
|
||||
.Select(parGameObject => parGameObject.GetComponent<T>())
|
||||
.Where(parComponent => parComponent != null)
|
||||
.Distinct()
|
||||
.ToList()!;
|
||||
}
|
||||
|
||||
public T? FindFirstComponent<T>() where T : Component.Component
|
||||
{
|
||||
return Hierarchy.Objects.Select(parGameObject => parGameObject.GetComponent<T>())
|
||||
.FirstOrDefault(parComponent => parComponent != null);
|
||||
}
|
||||
|
||||
public void Update(double parDeltaTime)
|
||||
{
|
||||
if (!IsPlaying)
|
||||
@@ -85,19 +50,20 @@ public class Scene : IUpdate, IRender
|
||||
}
|
||||
}
|
||||
|
||||
internal void Exit()
|
||||
public List<T> FindAllComponents<T>(bool parOnlyEnabled = true) where T : Component.Component
|
||||
{
|
||||
if (!IsPlaying)
|
||||
{
|
||||
throw new InvalidOperationException("Scene is not playing");
|
||||
}
|
||||
return Hierarchy.Objects
|
||||
.Where(parGameObject => !parOnlyEnabled || parGameObject.IsEnabled)
|
||||
.Select(parGameObject => parGameObject.GetComponent<T>())
|
||||
.Where(parComponent => parComponent != null)
|
||||
.Distinct()
|
||||
.ToList()!;
|
||||
}
|
||||
|
||||
foreach (var gameObject in Hierarchy.Objects)
|
||||
{
|
||||
gameObject.Destroy();
|
||||
}
|
||||
|
||||
IsPlaying = false;
|
||||
public T? FindFirstComponent<T>() where T : Component.Component
|
||||
{
|
||||
return Hierarchy.Objects.Select(parGameObject => parGameObject.GetComponent<T>())
|
||||
.FirstOrDefault(parComponent => parComponent != null);
|
||||
}
|
||||
|
||||
public void Add(GameObject parGameObject)
|
||||
@@ -136,6 +102,39 @@ public class Scene : IUpdate, IRender
|
||||
return parRecursive ? Hierarchy.GetAllChildren(parParent) : Hierarchy.GetChildren(parParent);
|
||||
}
|
||||
|
||||
internal void Enter()
|
||||
{
|
||||
if (IsPlaying)
|
||||
{
|
||||
throw new InvalidOperationException("Scene is already playing");
|
||||
}
|
||||
|
||||
ProcessChanges();
|
||||
|
||||
var allCameras = FindAllComponents<Camera>();
|
||||
foreach (var camera in allCameras)
|
||||
{
|
||||
_cameras.Add(camera.RenderLayer, camera);
|
||||
}
|
||||
|
||||
IsPlaying = true;
|
||||
}
|
||||
|
||||
internal void Exit()
|
||||
{
|
||||
if (!IsPlaying)
|
||||
{
|
||||
throw new InvalidOperationException("Scene is not playing");
|
||||
}
|
||||
|
||||
foreach (var gameObject in Hierarchy.Objects)
|
||||
{
|
||||
gameObject.Destroy();
|
||||
}
|
||||
|
||||
IsPlaying = false;
|
||||
}
|
||||
|
||||
private void ProcessChanges()
|
||||
{
|
||||
Hierarchy.ProcessChanges();
|
||||
|
||||
@@ -2,32 +2,33 @@
|
||||
|
||||
public class SceneManager : IUpdate, IRender
|
||||
{
|
||||
public Scene? CurrentScene => _currentScene;
|
||||
public Scene? CurrentScene { get; private set; }
|
||||
|
||||
private Scene? _currentScene;
|
||||
private Func<Scene>? _nextScene;
|
||||
|
||||
public void TransitionTo(Func<Scene>? parScene)
|
||||
{
|
||||
_nextScene = parScene;
|
||||
}
|
||||
|
||||
public void Update(double parDeltaTime)
|
||||
{
|
||||
if (_nextScene != null)
|
||||
{
|
||||
_currentScene?.Exit();
|
||||
_currentScene = _nextScene();
|
||||
CurrentScene?.Exit();
|
||||
CurrentScene = _nextScene();
|
||||
_nextScene = null;
|
||||
_currentScene.Enter();
|
||||
CurrentScene.Enter();
|
||||
}
|
||||
|
||||
if (parDeltaTime != 0)
|
||||
_currentScene?.Update(parDeltaTime);
|
||||
{
|
||||
CurrentScene?.Update(parDeltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
public void Render()
|
||||
{
|
||||
_currentScene?.Render();
|
||||
CurrentScene?.Render();
|
||||
}
|
||||
|
||||
public void TransitionTo(Func<Scene>? parScene)
|
||||
{
|
||||
_nextScene = parScene;
|
||||
}
|
||||
}
|
||||
@@ -2,21 +2,10 @@
|
||||
|
||||
public class TickableTimer
|
||||
{
|
||||
public event Action? OnFinished;
|
||||
public event Action<double>? OnUpdate;
|
||||
public event Action? OnFinished;
|
||||
|
||||
public double TotalTime
|
||||
{
|
||||
get => _totalTime;
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value);
|
||||
|
||||
_totalTime = value;
|
||||
CurrentTime = value;
|
||||
}
|
||||
}
|
||||
public bool IsFinished => _currentTime <= 0;
|
||||
|
||||
public double CurrentTime
|
||||
{
|
||||
@@ -24,30 +13,54 @@ public class TickableTimer
|
||||
set
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
value = 0;
|
||||
}
|
||||
|
||||
if (value > TotalTime)
|
||||
{
|
||||
value = TotalTime;
|
||||
}
|
||||
|
||||
if (value == _currentTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_currentTime = value;
|
||||
OnUpdate?.Invoke(value);
|
||||
|
||||
if (IsFinished)
|
||||
{
|
||||
OnFinished?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsFinished => _currentTime <= 0;
|
||||
public double TotalTime
|
||||
{
|
||||
get => _totalTime;
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value);
|
||||
}
|
||||
|
||||
_totalTime = value;
|
||||
CurrentTime = value;
|
||||
}
|
||||
}
|
||||
|
||||
private double _totalTime;
|
||||
private double _currentTime;
|
||||
private double _totalTime;
|
||||
|
||||
public TickableTimer(double parTotalTime)
|
||||
{
|
||||
if (parTotalTime <= 0)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(parTotalTime);
|
||||
}
|
||||
|
||||
_totalTime = parTotalTime;
|
||||
_currentTime = parTotalTime;
|
||||
|
||||
@@ -9,34 +9,34 @@ namespace Engine;
|
||||
|
||||
public class Window : IPresenter
|
||||
{
|
||||
public bool IsExiting => _window.IsExiting;
|
||||
public int Width { get; private set; }
|
||||
public int Height { get; private set; }
|
||||
public event Action<ResizeEventArgs>? Resize;
|
||||
|
||||
internal NativeWindow NativeWindow => _window;
|
||||
public int Width { get; private set; }
|
||||
public int Height { get; private set; }
|
||||
public bool IsExiting => NativeWindow.IsExiting;
|
||||
|
||||
internal NativeWindow NativeWindow { get; }
|
||||
|
||||
private readonly Engine _engine;
|
||||
private readonly NativeWindow _window;
|
||||
private readonly bool _headless;
|
||||
|
||||
public Window(Engine parEngine, NativeWindow parWindow, bool parHeadless)
|
||||
{
|
||||
_engine = parEngine;
|
||||
_window = parWindow;
|
||||
NativeWindow = parWindow;
|
||||
_headless = parHeadless;
|
||||
|
||||
(Width, Height) = _window.ClientSize;
|
||||
(Width, Height) = NativeWindow.ClientSize;
|
||||
|
||||
_window.MakeCurrent();
|
||||
_window.Resize += parArgs =>
|
||||
NativeWindow.MakeCurrent();
|
||||
NativeWindow.Resize += parArgs =>
|
||||
{
|
||||
Width = parArgs.Width;
|
||||
Height = parArgs.Height;
|
||||
Resize?.Invoke(parArgs);
|
||||
};
|
||||
|
||||
_window.VSync = VSyncMode.On;
|
||||
NativeWindow.VSync = VSyncMode.On;
|
||||
}
|
||||
|
||||
public void Update(double parDeltaTime)
|
||||
@@ -50,9 +50,9 @@ public class Window : IPresenter
|
||||
return;
|
||||
}
|
||||
|
||||
_window.NewInputFrame();
|
||||
NativeWindow.NewInputFrame();
|
||||
NativeWindow.ProcessWindowEvents(false);
|
||||
_window.SwapBuffers();
|
||||
NativeWindow.SwapBuffers();
|
||||
}
|
||||
|
||||
public void Present(IConstTexture parTexture)
|
||||
@@ -72,7 +72,7 @@ public class Window : IPresenter
|
||||
|
||||
public void Exit()
|
||||
{
|
||||
_window.Close();
|
||||
NativeWindow.Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user