add docs for engine
This commit is contained in:
@@ -4,16 +4,46 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Asset.Font;
|
namespace Engine.Asset.Font;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a font loaded from a texture atlas and metadata.
|
||||||
|
/// </summary>
|
||||||
public class Font
|
public class Font
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The static texture atlas used for rendering the font.
|
||||||
|
/// </summary>
|
||||||
public StaticTexture AtlasTexture { get; }
|
public StaticTexture AtlasTexture { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The metadata associated with the font.
|
||||||
|
/// </summary>
|
||||||
public Metadata.Metadata Metadata { get; }
|
public Metadata.Metadata Metadata { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The unit range of the font, calculated from the distance range and the atlas size.
|
||||||
|
/// </summary>
|
||||||
public Vector2 UnitRange { get; }
|
public Vector2 UnitRange { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dictionary mapping Unicode code points to glyphs.
|
||||||
|
/// </summary>
|
||||||
private readonly Dictionary<int, Glyph> _glyphs = new();
|
private readonly Dictionary<int, Glyph> _glyphs = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dictionary mapping Unicode code points to glyph data.
|
||||||
|
/// </summary>
|
||||||
private readonly Dictionary<int, GlyphData> _glyphData = new();
|
private readonly Dictionary<int, GlyphData> _glyphData = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dictionary mapping pairs of Unicode code points to kerning data.
|
||||||
|
/// </summary>
|
||||||
private readonly Dictionary<(int, int), Kerning> _kernings = new();
|
private readonly Dictionary<(int, int), Kerning> _kernings = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Font"/> class with the specified texture atlas and metadata.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parAtlasTexture">The static texture atlas for the font.</param>
|
||||||
|
/// <param name="parMetadata">The metadata for the font.</param>
|
||||||
public Font(StaticTexture parAtlasTexture, Metadata.Metadata parMetadata)
|
public Font(StaticTexture parAtlasTexture, Metadata.Metadata parMetadata)
|
||||||
{
|
{
|
||||||
AtlasTexture = parAtlasTexture;
|
AtlasTexture = parAtlasTexture;
|
||||||
@@ -25,26 +55,52 @@ public class Font
|
|||||||
LoadKernings();
|
LoadKernings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the glyph associated with the specified Unicode value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parUnicode">The Unicode value of the glyph.</param>
|
||||||
|
/// <returns>The glyph for the specified Unicode value, or <c>null</c> if not found.</returns>
|
||||||
public Glyph? GetGlyph(int parUnicode)
|
public Glyph? GetGlyph(int parUnicode)
|
||||||
{
|
{
|
||||||
return _glyphs.GetValueOrDefault(parUnicode);
|
return _glyphs.GetValueOrDefault(parUnicode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the glyph data associated with the specified Unicode value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parUnicode">The Unicode value of the glyph.</param>
|
||||||
|
/// <returns>The glyph data for the specified Unicode value, or <c>null</c> if not found.</returns>
|
||||||
public GlyphData? GetGlyphData(int parUnicode)
|
public GlyphData? GetGlyphData(int parUnicode)
|
||||||
{
|
{
|
||||||
return _glyphData.GetValueOrDefault(parUnicode);
|
return _glyphData.GetValueOrDefault(parUnicode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the kerning information between two specified Unicode values.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parUnicode1">The Unicode value of the first character.</param>
|
||||||
|
/// <param name="parUnicode2">The Unicode value of the second character.</param>
|
||||||
|
/// <returns>The kerning information, or <c>null</c> if not found.</returns>
|
||||||
public Kerning? GetKerning(int parUnicode1, int parUnicode2)
|
public Kerning? GetKerning(int parUnicode1, int parUnicode2)
|
||||||
{
|
{
|
||||||
return _kernings.GetValueOrDefault((parUnicode1, parUnicode2));
|
return _kernings.GetValueOrDefault((parUnicode1, parUnicode2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an iterator for traversing through the specified text using the font.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parText">The text to iterate over.</param>
|
||||||
|
/// <returns>An iterator for the text.</returns>
|
||||||
public FontIterator Iterator(string parText)
|
public FontIterator Iterator(string parText)
|
||||||
{
|
{
|
||||||
return new FontIterator(this, parText);
|
return new FontIterator(this, parText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Measures the size of the specified text when rendered using the font.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parText">The text to measure.</param>
|
||||||
|
/// <returns>The size of the text in <see cref="Vector2"/>.</returns>
|
||||||
public Vector2 Measure(string parText)
|
public Vector2 Measure(string parText)
|
||||||
{
|
{
|
||||||
var fontIterator = Iterator(parText);
|
var fontIterator = Iterator(parText);
|
||||||
@@ -53,6 +109,9 @@ public class Font
|
|||||||
return new Vector2(fontIterator.MaxWidth, fontIterator.MaxHeight);
|
return new Vector2(fontIterator.MaxWidth, fontIterator.MaxHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the glyphs from the metadata into the font with their associated data.
|
||||||
|
/// </summary>
|
||||||
private void LoadGlyphs()
|
private void LoadGlyphs()
|
||||||
{
|
{
|
||||||
foreach (var glyph in Metadata.Glyphs)
|
foreach (var glyph in Metadata.Glyphs)
|
||||||
@@ -68,6 +127,9 @@ public class Font
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the kerning information from the metadata into the font.
|
||||||
|
/// </summary>
|
||||||
private void LoadKernings()
|
private void LoadKernings()
|
||||||
{
|
{
|
||||||
foreach (var kerning in Metadata.Kerning)
|
foreach (var kerning in Metadata.Kerning)
|
||||||
@@ -76,11 +138,26 @@ public class Font
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the data associated with a single glyph in the font.
|
||||||
|
/// </summary>
|
||||||
public record GlyphData
|
public record GlyphData
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The positions of the glyph's vertices.
|
||||||
|
/// </summary>
|
||||||
public Vector2[] Positions { get; }
|
public Vector2[] Positions { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The texture UV coordinates for the glyph's vertices.
|
||||||
|
/// </summary>
|
||||||
public Vector2[] UVs { get; }
|
public Vector2[] UVs { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="GlyphData"/> record with the given metadata and glyph.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parMetadata">The font metadata.</param>
|
||||||
|
/// <param name="parGlyph">The glyph for which to create data.</param>
|
||||||
public GlyphData(in Metadata.Metadata parMetadata, in Glyph parGlyph)
|
public GlyphData(in Metadata.Metadata parMetadata, in Glyph parGlyph)
|
||||||
{
|
{
|
||||||
var size = new Vector2(parMetadata.Atlas.Width, parMetadata.Atlas.Height);
|
var size = new Vector2(parMetadata.Atlas.Width, parMetadata.Atlas.Height);
|
||||||
|
|||||||
@@ -3,26 +3,71 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Asset.Font;
|
namespace Engine.Asset.Font;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An iterator that traverses through the characters of a given text using a font, calculating the glyph data, positions, and kerning.
|
||||||
|
/// </summary>
|
||||||
public class FontIterator : IEnumerable<FontIterator.NextGlyphData>
|
public class FontIterator : IEnumerable<FontIterator.NextGlyphData>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum width encountered while measuring the text.
|
||||||
|
/// </summary>
|
||||||
public float MaxWidth { get; private set; }
|
public float MaxWidth { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum height encountered while measuring the text.
|
||||||
|
/// </summary>
|
||||||
public float MaxHeight { get; private set; }
|
public float MaxHeight { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The font to use for text iteration.
|
||||||
|
/// </summary>
|
||||||
private readonly Font _font;
|
private readonly Font _font;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The text to iterate over.
|
||||||
|
/// </summary>
|
||||||
private readonly string _text;
|
private readonly string _text;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current index in the text.
|
||||||
|
/// </summary>
|
||||||
private int _currentIndex;
|
private int _currentIndex;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The previous Unicode code point.
|
||||||
|
/// </summary>
|
||||||
private int _previousCodepoint = -1;
|
private int _previousCodepoint = -1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of characters on the current line.
|
||||||
|
/// </summary>
|
||||||
private int _lineCharCount;
|
private int _lineCharCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current cursor position.
|
||||||
|
/// </summary>
|
||||||
private Vector2 _cursor = Vector2.Zero;
|
private Vector2 _cursor = Vector2.Zero;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current kerning offset.
|
||||||
|
/// </summary>
|
||||||
private Vector2 _kerning = Vector2.Zero;
|
private Vector2 _kerning = Vector2.Zero;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="FontIterator"/> class with the specified font and text.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parFont">The font to use for rendering.</param>
|
||||||
|
/// <param name="parText">The text to iterate over.</param>
|
||||||
public FontIterator(Font parFont, string parText)
|
public FontIterator(Font parFont, string parText)
|
||||||
{
|
{
|
||||||
_font = parFont;
|
_font = parFont;
|
||||||
_text = parText;
|
_text = parText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an enumerator that iterates through the next glyph data for the text.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An enumerator for the next glyph data in the text.</returns>
|
||||||
public IEnumerator<NextGlyphData> GetEnumerator()
|
public IEnumerator<NextGlyphData> GetEnumerator()
|
||||||
{
|
{
|
||||||
while (_currentIndex < _text.Length)
|
while (_currentIndex < _text.Length)
|
||||||
@@ -80,21 +125,38 @@ public class FontIterator : IEnumerable<FontIterator.NextGlyphData>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a non-generic enumerator for the <see cref="FontIterator"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A non-generic enumerator for the font iterator.</returns>
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
{
|
{
|
||||||
return GetEnumerator();
|
return GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if the provided codepoint represents a line break character.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parCodepoint">The codepoint to check.</param>
|
||||||
|
/// <returns><c>true</c> if the codepoint represents a line break, otherwise <c>false</c>.</returns>
|
||||||
private static bool IsLineBreak(int parCodepoint)
|
private static bool IsLineBreak(int parCodepoint)
|
||||||
{
|
{
|
||||||
return parCodepoint == '\n';
|
return parCodepoint == '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if the provided codepoint represents a tab character.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parCodepoint">The codepoint to check.</param>
|
||||||
|
/// <returns><c>true</c> if the codepoint represents a tab character, otherwise <c>false</c>.</returns>
|
||||||
private static bool IsTab(int parCodepoint)
|
private static bool IsTab(int parCodepoint)
|
||||||
{
|
{
|
||||||
return parCodepoint == '\t';
|
return parCodepoint == '\t';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles the tab character by advancing the cursor position to the next tab stop.
|
||||||
|
/// </summary>
|
||||||
private void Tab()
|
private void Tab()
|
||||||
{
|
{
|
||||||
var spaceGlyph = _font.GetGlyph(' ');
|
var spaceGlyph = _font.GetGlyph(' ');
|
||||||
@@ -107,6 +169,9 @@ public class FontIterator : IEnumerable<FontIterator.NextGlyphData>
|
|||||||
_cursor.X += missingSpaces * spaceGlyph.Advance;
|
_cursor.X += missingSpaces * spaceGlyph.Advance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles a line break by resetting the cursor position and updating the line height.
|
||||||
|
/// </summary>
|
||||||
private void LineBreak()
|
private void LineBreak()
|
||||||
{
|
{
|
||||||
_kerning = Vector2.Zero;
|
_kerning = Vector2.Zero;
|
||||||
@@ -116,5 +181,8 @@ public class FontIterator : IEnumerable<FontIterator.NextGlyphData>
|
|||||||
_previousCodepoint = -1;
|
_previousCodepoint = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains the data for the next glyph in the iteration, including its positions, texture UVs, and offset.
|
||||||
|
/// </summary>
|
||||||
public record NextGlyphData(Vector2[] Positions, Vector2[] UVs, Vector2 Offset);
|
public record NextGlyphData(Vector2[] Positions, Vector2[] UVs, Vector2 Offset);
|
||||||
}
|
}
|
||||||
@@ -2,26 +2,44 @@
|
|||||||
|
|
||||||
namespace Engine.Asset.Font.Metadata;
|
namespace Engine.Asset.Font.Metadata;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the texture atlas used for font rendering.
|
||||||
|
/// </summary>
|
||||||
[JsonSerializable(typeof(Atlas))]
|
[JsonSerializable(typeof(Atlas))]
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public record Atlas
|
public record Atlas
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The distance range for the atlas.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("distanceRange")]
|
[JsonPropertyName("distanceRange")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public float DistanceRange { get; private set; }
|
public float DistanceRange { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The size of the atlas.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("size")]
|
[JsonPropertyName("size")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public float Size { get; private set; }
|
public float Size { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The width of the atlas.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("width")]
|
[JsonPropertyName("width")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public int Width { get; private set; }
|
public int Width { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The height of the atlas.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("height")]
|
[JsonPropertyName("height")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public int Height { get; private set; }
|
public int Height { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Y-origin of the atlas.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("yOrigin")]
|
[JsonPropertyName("yOrigin")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public string YOrigin { get; private set; }
|
public string YOrigin { get; private set; }
|
||||||
|
|||||||
@@ -2,22 +2,37 @@
|
|||||||
|
|
||||||
namespace Engine.Asset.Font.Metadata;
|
namespace Engine.Asset.Font.Metadata;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the bounds of a font glyph in the coordinate space.
|
||||||
|
/// </summary>
|
||||||
[JsonSerializable(typeof(Bounds))]
|
[JsonSerializable(typeof(Bounds))]
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public record Bounds
|
public record Bounds
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The left boundary of the bounds.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("left")]
|
[JsonPropertyName("left")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public float Left { get; private set; }
|
public float Left { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The bottom boundary of the bounds.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("bottom")]
|
[JsonPropertyName("bottom")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public float Bottom { get; private set; }
|
public float Bottom { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The right boundary of the bounds.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("right")]
|
[JsonPropertyName("right")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public float Right { get; private set; }
|
public float Right { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The top boundary of the bounds.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("top")]
|
[JsonPropertyName("top")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public float Top { get; private set; }
|
public float Top { get; private set; }
|
||||||
|
|||||||
@@ -2,22 +2,37 @@
|
|||||||
|
|
||||||
namespace Engine.Asset.Font.Metadata;
|
namespace Engine.Asset.Font.Metadata;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a single glyph in the font with associated metadata.
|
||||||
|
/// </summary>
|
||||||
[JsonSerializable(typeof(Glyph))]
|
[JsonSerializable(typeof(Glyph))]
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public record Glyph
|
public record Glyph
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The Unicode code point of the glyph.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("unicode")]
|
[JsonPropertyName("unicode")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public int Unicode { get; private set; }
|
public int Unicode { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The advance width of the glyph.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("advance")]
|
[JsonPropertyName("advance")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public float Advance { get; private set; }
|
public float Advance { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The bounds of the glyph in the plane.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("planeBounds")]
|
[JsonPropertyName("planeBounds")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public Bounds? PlaneBounds { get; private set; }
|
public Bounds? PlaneBounds { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The bounds of the glyph in the atlas.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("atlasBounds")]
|
[JsonPropertyName("atlasBounds")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public Bounds? AtlasBounds { get; private set; }
|
public Bounds? AtlasBounds { get; private set; }
|
||||||
|
|||||||
@@ -2,18 +2,30 @@
|
|||||||
|
|
||||||
namespace Engine.Asset.Font.Metadata;
|
namespace Engine.Asset.Font.Metadata;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents kerning information between two characters in the font.
|
||||||
|
/// </summary>
|
||||||
[JsonSerializable(typeof(Kerning))]
|
[JsonSerializable(typeof(Kerning))]
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public record Kerning
|
public record Kerning
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The Unicode value of the first character in the kerning pair.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("unicode1")]
|
[JsonPropertyName("unicode1")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public int Unicode1 { get; private set; }
|
public int Unicode1 { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Unicode value of the second character in the kerning pair.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("unicode2")]
|
[JsonPropertyName("unicode2")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public int Unicode2 { get; private set; }
|
public int Unicode2 { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of kerning (adjustment) between the two characters.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("advance")]
|
[JsonPropertyName("advance")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public float Advance { get; private set; }
|
public float Advance { get; private set; }
|
||||||
|
|||||||
@@ -2,22 +2,37 @@
|
|||||||
|
|
||||||
namespace Engine.Asset.Font.Metadata;
|
namespace Engine.Asset.Font.Metadata;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents metadata for a font, including atlas, metrics, glyphs, and kerning.
|
||||||
|
/// </summary>
|
||||||
[JsonSerializable(typeof(Metadata))]
|
[JsonSerializable(typeof(Metadata))]
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public record Metadata
|
public record Metadata
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The atlas information for the font.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("atlas")]
|
[JsonPropertyName("atlas")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public Atlas Atlas { get; private set; }
|
public Atlas Atlas { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The metrics of the font.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("metrics")]
|
[JsonPropertyName("metrics")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public Metrics Metrics { get; private set; }
|
public Metrics Metrics { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The array of glyphs in the font.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("glyphs")]
|
[JsonPropertyName("glyphs")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public Glyph[] Glyphs { get; private set; }
|
public Glyph[] Glyphs { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The array of kerning pairs for the font.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("kerning")]
|
[JsonPropertyName("kerning")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public Kerning[] Kerning { get; private set; }
|
public Kerning[] Kerning { get; private set; }
|
||||||
|
|||||||
@@ -2,30 +2,51 @@
|
|||||||
|
|
||||||
namespace Engine.Asset.Font.Metadata;
|
namespace Engine.Asset.Font.Metadata;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the metrics for a font, including size and line height.
|
||||||
|
/// </summary>
|
||||||
[JsonSerializable(typeof(Metrics))]
|
[JsonSerializable(typeof(Metrics))]
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public record Metrics
|
public record Metrics
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The em size of the font.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("emSize")]
|
[JsonPropertyName("emSize")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public float EmSize { get; private set; }
|
public float EmSize { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The line height of the font.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("lineHeight")]
|
[JsonPropertyName("lineHeight")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public float LineHeight { get; private set; }
|
public float LineHeight { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ascender height of the font.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("ascender")]
|
[JsonPropertyName("ascender")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public float Ascender { get; private set; }
|
public float Ascender { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The descender height of the font.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("descender")]
|
[JsonPropertyName("descender")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public float Descender { get; private set; }
|
public float Descender { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Y-coordinate of the underline.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("underlineY")]
|
[JsonPropertyName("underlineY")]
|
||||||
[JsonInclude]
|
[JsonInclude]
|
||||||
public float UnderlineY { get; private set; }
|
public float UnderlineY { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The thickness of the underline in the font.
|
||||||
|
/// </summary>
|
||||||
[JsonPropertyName("underlineThickness")]
|
[JsonPropertyName("underlineThickness")]
|
||||||
public float UnderlineThickness { get; private set; }
|
public float UnderlineThickness { get; private set; }
|
||||||
}
|
}
|
||||||
@@ -3,12 +3,34 @@ using Engine.Graphics.Texture;
|
|||||||
|
|
||||||
namespace Engine.Asset;
|
namespace Engine.Asset;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an image containing pixels of a specified type.
|
||||||
|
/// The image stores pixel data in a 2D array and provides methods for converting it into textures.
|
||||||
|
/// </summary>
|
||||||
public class Image<T> where T : struct, IPixel
|
public class Image<T> where T : struct, IPixel
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The width of the image, in pixels.
|
||||||
|
/// </summary>
|
||||||
public int Width { get; }
|
public int Width { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The height of the image, in pixels.
|
||||||
|
/// </summary>
|
||||||
public int Height { get; }
|
public int Height { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The 2D array of pixels representing the image's content.
|
||||||
|
/// Each element in the array is of type <typeparamref name="T"/>.
|
||||||
|
/// </summary>
|
||||||
public T[,] Pixels { get; }
|
public T[,] Pixels { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Image{T}"/> class with specified width and height.
|
||||||
|
/// Allocates a 2D array to hold pixel data.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parWidth">The width of the image in pixels.</param>
|
||||||
|
/// <param name="parHeight">The height of the image in pixels.</param>
|
||||||
public Image(int parWidth, int parHeight)
|
public Image(int parWidth, int parHeight)
|
||||||
{
|
{
|
||||||
Width = parWidth;
|
Width = parWidth;
|
||||||
@@ -16,8 +38,18 @@ public class Image<T> where T : struct, IPixel
|
|||||||
Pixels = new T[parHeight, parWidth];
|
Pixels = new T[parHeight, parWidth];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indexer to access individual pixels in the image by their Y (row) and X (column) coordinates.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parY">The Y coordinate (row) of the pixel.</param>
|
||||||
|
/// <param name="parX">The X coordinate (column) of the pixel.</param>
|
||||||
|
/// <returns>The pixel at the specified coordinates.</returns>
|
||||||
public T this[int parY, int parX] => Pixels[parY, parX];
|
public T this[int parY, int parX] => Pixels[parY, parX];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the image into a dynamic texture that can be uploaded to the GPU.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A <see cref="DynamicTexture"/> representing the image.</returns>
|
||||||
public DynamicTexture ToDynamicTexture()
|
public DynamicTexture ToDynamicTexture()
|
||||||
{
|
{
|
||||||
var texture = DynamicTexture.Create<T>(Width, Height);
|
var texture = DynamicTexture.Create<T>(Width, Height);
|
||||||
@@ -25,6 +57,11 @@ public class Image<T> where T : struct, IPixel
|
|||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the image into a static texture that can be uploaded to the GPU.
|
||||||
|
/// Static textures are optimized for performance but cannot be updated after creation.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A <see cref="StaticTexture"/> representing the image.</returns>
|
||||||
public StaticTexture ToStaticTexture()
|
public StaticTexture ToStaticTexture()
|
||||||
{
|
{
|
||||||
var texture = StaticTexture.Create<T>(Width, Height);
|
var texture = StaticTexture.Create<T>(Width, Height);
|
||||||
|
|||||||
@@ -1,9 +1,23 @@
|
|||||||
namespace Engine.Asset.Mesh.Loader;
|
namespace Engine.Asset.Mesh.Loader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the interface for a mesh loader that can load meshes from a given stream.
|
||||||
|
/// </summary>
|
||||||
public interface IMeshLoader
|
public interface IMeshLoader
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Loads a mesh from the provided text reader with optional parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parReader">The text reader to read the mesh data from.</param>
|
||||||
|
/// <param name="parParameters">Optional parameters to control the mesh loading behavior.</param>
|
||||||
|
/// <returns>The loaded mesh.</returns>
|
||||||
public Mesh LoadMesh(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default);
|
public Mesh LoadMesh(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Optimizes the provided mesh by removing duplicate vertices and reusing indices.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parMesh">The mesh to optimize.</param>
|
||||||
|
/// <returns>A new optimized mesh.</returns>
|
||||||
internal static Mesh Optimize(Mesh parMesh)
|
internal static Mesh Optimize(Mesh parMesh)
|
||||||
{
|
{
|
||||||
var optimizedMesh = new Mesh();
|
var optimizedMesh = new Mesh();
|
||||||
|
|||||||
@@ -1,12 +1,33 @@
|
|||||||
namespace Engine.Asset.Mesh.Loader;
|
namespace Engine.Asset.Mesh.Loader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies options for mesh loading behavior, such as loading normals, UVs, and optimization.
|
||||||
|
/// </summary>
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum MeshLoaderParameters
|
public enum MeshLoaderParameters
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// No options selected.
|
||||||
|
/// </summary>
|
||||||
None = 0,
|
None = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load normals for the mesh.
|
||||||
|
/// </summary>
|
||||||
LoadNormals = 1 << 0,
|
LoadNormals = 1 << 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load UVs for the mesh.
|
||||||
|
/// </summary>
|
||||||
LoadUVs = 1 << 1,
|
LoadUVs = 1 << 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Optimize the mesh by removing duplicate vertices and reusing indices.
|
||||||
|
/// </summary>
|
||||||
Optimize = 1 << 2,
|
Optimize = 1 << 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default set of options: load normals, load UVs, and optimize the mesh.
|
||||||
|
/// </summary>
|
||||||
Default = LoadNormals | LoadUVs | Optimize
|
Default = LoadNormals | LoadUVs | Optimize
|
||||||
}
|
}
|
||||||
@@ -3,19 +3,40 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Asset.Mesh.Loader;
|
namespace Engine.Asset.Mesh.Loader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A mesh loader for loading meshes from OBJ file format.
|
||||||
|
/// </summary>
|
||||||
public class ObjMeshLoader : IMeshLoader
|
public class ObjMeshLoader : IMeshLoader
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The singleton instance of the <see cref="ObjMeshLoader"/>.
|
||||||
|
/// </summary>
|
||||||
private static readonly ObjMeshLoader INSTANCE = new();
|
private static readonly ObjMeshLoader INSTANCE = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ObjMeshLoader"/> class.
|
||||||
|
/// </summary>
|
||||||
private ObjMeshLoader()
|
private ObjMeshLoader()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads a mesh from the provided text reader in the OBJ format with optional parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parReader">The text reader to read the OBJ data from.</param>
|
||||||
|
/// <param name="parParameters">Optional parameters to control the mesh loading behavior.</param>
|
||||||
|
/// <returns>The loaded mesh.</returns>
|
||||||
public static Mesh Load(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
public static Mesh Load(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
||||||
{
|
{
|
||||||
return INSTANCE.LoadMesh(parReader, parParameters);
|
return INSTANCE.LoadMesh(parReader, parParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads a mesh from the provided text reader in the OBJ format with optional parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parReader">The text reader to read the OBJ data from.</param>
|
||||||
|
/// <param name="parParameters">Optional parameters to control the mesh loading behavior.</param>
|
||||||
|
/// <returns>The loaded mesh.</returns>
|
||||||
public Mesh LoadMesh(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
public Mesh LoadMesh(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
||||||
{
|
{
|
||||||
var mesh = new Mesh();
|
var mesh = new Mesh();
|
||||||
|
|||||||
@@ -3,19 +3,40 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Asset.Mesh.Loader;
|
namespace Engine.Asset.Mesh.Loader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A mesh loader for loading meshes from STL file format.
|
||||||
|
/// </summary>
|
||||||
public class StlMeshLoader : IMeshLoader
|
public class StlMeshLoader : IMeshLoader
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The singleton instance of the <see cref="StlMeshLoader"/>.
|
||||||
|
/// </summary>
|
||||||
private static readonly StlMeshLoader INSTANCE = new();
|
private static readonly StlMeshLoader INSTANCE = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="StlMeshLoader"/> class.
|
||||||
|
/// </summary>
|
||||||
private StlMeshLoader()
|
private StlMeshLoader()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads a mesh from the provided text reader in the STL format with optional parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parReader">The text reader to read the STL data from.</param>
|
||||||
|
/// <param name="parParameters">Optional parameters to control the mesh loading behavior.</param>
|
||||||
|
/// <returns>The loaded mesh.</returns>
|
||||||
public static Mesh Load(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
public static Mesh Load(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
||||||
{
|
{
|
||||||
return INSTANCE.LoadMesh(parReader, parParameters);
|
return INSTANCE.LoadMesh(parReader, parParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads a mesh from the provided text reader in the STL format with optional parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parReader">The text reader to read the STL data from.</param>
|
||||||
|
/// <param name="parParameters">Optional parameters to control the mesh loading behavior.</param>
|
||||||
|
/// <returns>The loaded mesh.</returns>
|
||||||
public Mesh LoadMesh(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
public Mesh LoadMesh(TextReader parReader, MeshLoaderParameters parParameters = MeshLoaderParameters.Default)
|
||||||
{
|
{
|
||||||
var mesh = new Mesh();
|
var mesh = new Mesh();
|
||||||
|
|||||||
@@ -4,26 +4,68 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Asset.Mesh;
|
namespace Engine.Asset.Mesh;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a 3D mesh consisting of vertices and indices used for rendering.
|
||||||
|
/// </summary>
|
||||||
public class Mesh
|
public class Mesh
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A read-only list of indices representing the mesh's geometry.
|
||||||
|
/// </summary>
|
||||||
public IReadOnlyList<uint> Indices => _indices;
|
public IReadOnlyList<uint> Indices => _indices;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A read-only list of vertices representing the mesh's geometry.
|
||||||
|
/// </summary>
|
||||||
public IReadOnlyList<Vertex> Vertices => _vertices;
|
public IReadOnlyList<Vertex> Vertices => _vertices;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Internal list of indices representing the mesh's geometry.
|
||||||
|
/// </summary>
|
||||||
internal IList<uint> IndicesInternal => _indices;
|
internal IList<uint> IndicesInternal => _indices;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Internal list of vertices representing the mesh's geometry.
|
||||||
|
/// </summary>
|
||||||
internal IList<Vertex> VerticesInternal => _vertices;
|
internal IList<Vertex> VerticesInternal => _vertices;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of indices representing the mesh's geometry.
|
||||||
|
/// </summary>
|
||||||
private readonly List<uint> _indices = [];
|
private readonly List<uint> _indices = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of vertices representing the mesh's geometry.
|
||||||
|
/// </summary>
|
||||||
private readonly List<Vertex> _vertices = [];
|
private readonly List<Vertex> _vertices = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Computes a hash code for the mesh based on its vertices and indices.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The hash code for the mesh.</returns>
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return HashCode.Combine(Indices, Vertices);
|
return HashCode.Combine(Indices, Vertices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a vertex in a mesh, including position, normal, and UV coordinates.
|
||||||
|
/// </summary>
|
||||||
public record struct Vertex : IVertex
|
public record struct Vertex : IVertex
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The position of the vertex in 3D space.
|
||||||
|
/// </summary>
|
||||||
[Vertex(VertexAttribType.Float, 3)] public Vector3 _position;
|
[Vertex(VertexAttribType.Float, 3)] public Vector3 _position;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The normal of the vertex for lighting calculations.
|
||||||
|
/// </summary>
|
||||||
[Vertex(VertexAttribType.Float, 3)] public Vector3 _normal;
|
[Vertex(VertexAttribType.Float, 3)] public Vector3 _normal;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The texture coordinates (UVs) of the vertex.
|
||||||
|
/// </summary>
|
||||||
[Vertex(VertexAttribType.Float, 2)] public Vector2 _uv;
|
[Vertex(VertexAttribType.Float, 2)] public Vector2 _uv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,17 +20,49 @@ using Debug = Engine.Graphics.Debug;
|
|||||||
|
|
||||||
namespace Engine;
|
namespace Engine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the main engine class responsible for managing resources, rendering, input, and scenes.
|
||||||
|
/// </summary>
|
||||||
public sealed class Engine
|
public sealed class Engine
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The input handler of the engine.
|
||||||
|
/// </summary>
|
||||||
public IInputHandler? InputHandler { get; internal set; }
|
public IInputHandler? InputHandler { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The scene manager for managing and updating scenes.
|
||||||
|
/// </summary>
|
||||||
public SceneManager SceneManager { get; } = new();
|
public SceneManager SceneManager { get; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The resource manager responsible for asset management.
|
||||||
|
/// </summary>
|
||||||
public IResourceManager AssetResourceManager { get; }
|
public IResourceManager AssetResourceManager { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The path to the data folder used by the engine.
|
||||||
|
/// </summary>
|
||||||
public string DataFolder { get; }
|
public string DataFolder { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The singleton instance of the engine.
|
||||||
|
/// </summary>
|
||||||
internal static Engine Instance { get; private set; } = null!;
|
internal static Engine Instance { get; private set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The resource manager for engine-specific resources.
|
||||||
|
/// </summary>
|
||||||
internal ResourceManager EngineResourceManager { get; }
|
internal ResourceManager EngineResourceManager { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The renderer responsible for managing rendering operations.
|
||||||
|
/// </summary>
|
||||||
internal Renderer Renderer { get; }
|
internal Renderer Renderer { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The presenter responsible for displaying rendered content.
|
||||||
|
/// </summary>
|
||||||
internal IPresenter? Presenter
|
internal IPresenter? Presenter
|
||||||
{
|
{
|
||||||
get => _presenter;
|
get => _presenter;
|
||||||
@@ -38,26 +70,54 @@ public sealed class Engine
|
|||||||
{
|
{
|
||||||
if (_presenter != null)
|
if (_presenter != null)
|
||||||
{
|
{
|
||||||
_presenter.Resize -= PresenterResize;
|
_presenter.OnResize -= PresenterResize;
|
||||||
}
|
}
|
||||||
|
|
||||||
_presenter = value;
|
_presenter = value;
|
||||||
|
|
||||||
if (_presenter != null)
|
if (_presenter != null)
|
||||||
{
|
{
|
||||||
_presenter.Resize += PresenterResize;
|
_presenter.OnResize += PresenterResize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The application window that holds rendering context.
|
||||||
|
/// </summary>
|
||||||
internal Window Window { get; }
|
internal Window Window { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The logger instance used by the engine.
|
||||||
|
/// </summary>
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The lock used to synchronize scene access.
|
||||||
|
/// </summary>
|
||||||
private readonly object _sceneLock = new();
|
private readonly object _sceneLock = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The presenter used to display rendered content.
|
||||||
|
/// </summary>
|
||||||
private IPresenter? _presenter;
|
private IPresenter? _presenter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The thread used to run the update loop.
|
||||||
|
/// </summary>
|
||||||
private Thread? _updateThread;
|
private Thread? _updateThread;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Engine"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parWidth">The width of the rendering window.</param>
|
||||||
|
/// <param name="parHeight">The height of the rendering window.</param>
|
||||||
|
/// <param name="parHeadless">Indicates whether the engine should run in headless mode.</param>
|
||||||
|
/// <param name="parTitle">The title of the application window.</param>
|
||||||
|
/// <param name="parAssetFolder">The path to the asset folder.</param>
|
||||||
|
/// <param name="parDataFolder">The path to the data folder.</param>
|
||||||
|
/// <param name="parLogger">The logger instance to use for logging.</param>
|
||||||
|
/// <exception cref="InvalidOperationException">Thrown if an engine instance is already running.</exception>
|
||||||
public Engine(int parWidth, int parHeight, bool parHeadless, string parTitle, string parAssetFolder,
|
public Engine(int parWidth, int parHeight, bool parHeadless, string parTitle, string parAssetFolder,
|
||||||
string parDataFolder,
|
string parDataFolder,
|
||||||
ILogger parLogger)
|
ILogger parLogger)
|
||||||
@@ -96,6 +156,31 @@ public sealed class Engine
|
|||||||
Window = new Window(this, Renderer.NativeWindow, parHeadless);
|
Window = new Window(this, Renderer.NativeWindow, parHeadless);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts the engine's update and render threads.
|
||||||
|
/// </summary>
|
||||||
|
public void Run()
|
||||||
|
{
|
||||||
|
_updateThread = new Thread(RunUpdate) { Name = "UpdateThread" };
|
||||||
|
_updateThread.Start();
|
||||||
|
|
||||||
|
RunRender();
|
||||||
|
|
||||||
|
_updateThread.Join();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Closes the engine and stops all running threads.
|
||||||
|
/// </summary>
|
||||||
|
public void Close()
|
||||||
|
{
|
||||||
|
Presenter?.Exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates and initializes the engine resource manager.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The initialized resource manager.</returns>
|
||||||
private static ResourceManager CreateEngineResourceManager()
|
private static ResourceManager CreateEngineResourceManager()
|
||||||
{
|
{
|
||||||
var memoryStreamProvider = new MemoryResourceStreamProvider();
|
var memoryStreamProvider = new MemoryResourceStreamProvider();
|
||||||
@@ -109,6 +194,11 @@ public sealed class Engine
|
|||||||
return resourceManager;
|
return resourceManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates and initializes the asset resource manager.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parAssetFolder">The path to the asset folder.</param>
|
||||||
|
/// <returns>The initialized resource manager.</returns>
|
||||||
private static ResourceManager CreateAssetResourceManager(string parAssetFolder)
|
private static ResourceManager CreateAssetResourceManager(string parAssetFolder)
|
||||||
{
|
{
|
||||||
var filesystemStreamProvider = new FilesystemResourceStreamProvider(parAssetFolder);
|
var filesystemStreamProvider = new FilesystemResourceStreamProvider(parAssetFolder);
|
||||||
@@ -119,6 +209,10 @@ public sealed class Engine
|
|||||||
return resourceManager;
|
return resourceManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers the default loaders for the resource manager.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parResourceManager">The resource manager to register loaders for.</param>
|
||||||
private static void RegisterDefaultLoaders(ResourceManager parResourceManager)
|
private static void RegisterDefaultLoaders(ResourceManager parResourceManager)
|
||||||
{
|
{
|
||||||
parResourceManager.RegisterLoader<Program>(new ProgramLoader());
|
parResourceManager.RegisterLoader<Program>(new ProgramLoader());
|
||||||
@@ -128,21 +222,9 @@ public sealed class Engine
|
|||||||
parResourceManager.RegisterLoader<Font>(new FontLoader());
|
parResourceManager.RegisterLoader<Font>(new FontLoader());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Run()
|
/// <summary>
|
||||||
{
|
/// Runs the render loop for the engine.
|
||||||
_updateThread = new Thread(RunUpdate) { Name = "UpdateThread" };
|
/// </summary>
|
||||||
_updateThread.Start();
|
|
||||||
|
|
||||||
RunRender();
|
|
||||||
|
|
||||||
_updateThread.Join();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Close()
|
|
||||||
{
|
|
||||||
Presenter?.Exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RunRender()
|
private void RunRender()
|
||||||
{
|
{
|
||||||
while (!Presenter?.IsExiting ?? false)
|
while (!Presenter?.IsExiting ?? false)
|
||||||
@@ -184,6 +266,9 @@ public sealed class Engine
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Runs the update loop for the engine.
|
||||||
|
/// </summary>
|
||||||
private void RunUpdate()
|
private void RunUpdate()
|
||||||
{
|
{
|
||||||
var timer = Stopwatch.StartNew();
|
var timer = Stopwatch.StartNew();
|
||||||
@@ -211,6 +296,10 @@ public sealed class Engine
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles the presenter resize event to adjust the renderer's viewport.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parEventArgs">The resize event arguments.</param>
|
||||||
private void PresenterResize(ResizeEventArgs parEventArgs)
|
private void PresenterResize(ResizeEventArgs parEventArgs)
|
||||||
{
|
{
|
||||||
if (parEventArgs.Width == 0 || parEventArgs.Height == 0)
|
if (parEventArgs.Width == 0 || parEventArgs.Height == 0)
|
||||||
|
|||||||
@@ -7,36 +7,99 @@ using Serilog.Sinks.SystemConsole.Themes;
|
|||||||
|
|
||||||
namespace Engine;
|
namespace Engine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides a builder for creating and configuring an instance of the <see cref="Engine"/> class.
|
||||||
|
/// </summary>
|
||||||
public sealed class EngineBuilder
|
public sealed class EngineBuilder
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The title of the application window.
|
||||||
|
/// </summary>
|
||||||
private string _title = "";
|
private string _title = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the engine should run in headless mode.
|
||||||
|
/// </summary>
|
||||||
private bool _headless;
|
private bool _headless;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The width of the rendering window.
|
||||||
|
/// </summary>
|
||||||
private int _width = 1;
|
private int _width = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The height of the rendering window.
|
||||||
|
/// </summary>
|
||||||
private int _height = 1;
|
private int _height = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The path to the asset folder.
|
||||||
|
/// </summary>
|
||||||
private string _assetFolder = "./asset";
|
private string _assetFolder = "./asset";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The path to the data folder.
|
||||||
|
/// </summary>
|
||||||
private string _dataFolder = "./data";
|
private string _dataFolder = "./data";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The input handler factory.
|
||||||
|
/// </summary>
|
||||||
private Func<Engine, IInputHandler>? _inputHandlerFunc;
|
private Func<Engine, IInputHandler>? _inputHandlerFunc;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The presenter factory.
|
||||||
|
/// </summary>
|
||||||
private Func<Engine, IPresenter>? _presenterFunc;
|
private Func<Engine, IPresenter>? _presenterFunc;
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether to log to the console.
|
||||||
|
/// </summary>
|
||||||
private bool _logToConsole;
|
private bool _logToConsole;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether to log to a file.
|
||||||
|
/// </summary>
|
||||||
private bool _logToFile;
|
private bool _logToFile;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The path to the log file.
|
||||||
|
/// </summary>
|
||||||
private string? _logFilePath;
|
private string? _logFilePath;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The log level.
|
||||||
|
/// </summary>
|
||||||
private LogEventLevel _logLevel = LogEventLevel.Information;
|
private LogEventLevel _logLevel = LogEventLevel.Information;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the title of the engine window.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parTitle">The title to use for the engine window.</param>
|
||||||
|
/// <returns>The current instance of <see cref="EngineBuilder"/> for chaining.</returns>
|
||||||
public EngineBuilder Title(string parTitle)
|
public EngineBuilder Title(string parTitle)
|
||||||
{
|
{
|
||||||
_title = parTitle;
|
_title = parTitle;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configures the engine to run in headless mode.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parHeadless">Indicates whether to enable headless mode. Defaults to <c>true</c>.</param>
|
||||||
|
/// <returns>The current instance of <see cref="EngineBuilder"/> for chaining.</returns>
|
||||||
public EngineBuilder Headless(bool parHeadless = true)
|
public EngineBuilder Headless(bool parHeadless = true)
|
||||||
{
|
{
|
||||||
_headless = parHeadless;
|
_headless = parHeadless;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the width of the engine window.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parWidth">The width in pixels. Must be greater than zero.</param>
|
||||||
|
/// <returns>The current instance of <see cref="EngineBuilder"/> for chaining.</returns>
|
||||||
public EngineBuilder Width(int parWidth)
|
public EngineBuilder Width(int parWidth)
|
||||||
{
|
{
|
||||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(parWidth);
|
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(parWidth);
|
||||||
@@ -45,6 +108,11 @@ public sealed class EngineBuilder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the height of the engine window.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parHeight">The height in pixels. Must be greater than zero.</param>
|
||||||
|
/// <returns>The current instance of <see cref="EngineBuilder"/> for chaining.</returns>
|
||||||
public EngineBuilder Height(int parHeight)
|
public EngineBuilder Height(int parHeight)
|
||||||
{
|
{
|
||||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(parHeight);
|
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(parHeight);
|
||||||
@@ -53,36 +121,68 @@ public sealed class EngineBuilder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the folder path for assets used by the engine.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parAssetFolder">The folder path containing asset files.</param>
|
||||||
|
/// <returns>The current instance of <see cref="EngineBuilder"/> for chaining.</returns>
|
||||||
public EngineBuilder AssetFolder(string parAssetFolder)
|
public EngineBuilder AssetFolder(string parAssetFolder)
|
||||||
{
|
{
|
||||||
_assetFolder = parAssetFolder;
|
_assetFolder = parAssetFolder;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the folder path for data files used by the engine.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parDataFolder">The folder path containing data files.</param>
|
||||||
|
/// <returns>The current instance of <see cref="EngineBuilder"/> for chaining.</returns>
|
||||||
public EngineBuilder DataFolder(string parDataFolder)
|
public EngineBuilder DataFolder(string parDataFolder)
|
||||||
{
|
{
|
||||||
_dataFolder = parDataFolder;
|
_dataFolder = parDataFolder;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the input handler to be used by the engine.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parInputHandlerFunc">A function that creates an input handler for the engine.</param>
|
||||||
|
/// <returns>The current instance of <see cref="EngineBuilder"/> for chaining.</returns>
|
||||||
public EngineBuilder InputHandler(Func<Engine, IInputHandler> parInputHandlerFunc)
|
public EngineBuilder InputHandler(Func<Engine, IInputHandler> parInputHandlerFunc)
|
||||||
{
|
{
|
||||||
_inputHandlerFunc = parInputHandlerFunc;
|
_inputHandlerFunc = parInputHandlerFunc;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the presenter to be used by the engine.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parPresenterFunc">A function that creates a presenter for the engine.</param>
|
||||||
|
/// <returns>The current instance of <see cref="EngineBuilder"/> for chaining.</returns>
|
||||||
public EngineBuilder Presenter(Func<Engine, IPresenter> parPresenterFunc)
|
public EngineBuilder Presenter(Func<Engine, IPresenter> parPresenterFunc)
|
||||||
{
|
{
|
||||||
_presenterFunc = parPresenterFunc;
|
_presenterFunc = parPresenterFunc;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configures logging to output to the console.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parLogToConsole">Indicates whether to enable console logging. Defaults to <c>true</c>.</param>
|
||||||
|
/// <returns>The current instance of <see cref="EngineBuilder"/> for chaining.</returns>
|
||||||
public EngineBuilder LogToConsole(bool parLogToConsole = true)
|
public EngineBuilder LogToConsole(bool parLogToConsole = true)
|
||||||
{
|
{
|
||||||
_logToConsole = parLogToConsole;
|
_logToConsole = parLogToConsole;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configures logging to output to a file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parLogToFile">Indicates whether to enable file logging. Defaults to <c>true</c>.</param>
|
||||||
|
/// <param name="parLogFilePath">The path of the log file. Cannot be <c>null</c> if file logging is enabled.</param>
|
||||||
|
/// <returns>The current instance of <see cref="EngineBuilder"/> for chaining.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Thrown if <paramref name="parLogFilePath"/> is <c>null</c> when <paramref name="parLogToFile"/> is <c>true</c>.</exception>
|
||||||
public EngineBuilder LogToFile(bool parLogToFile = true, string? parLogFilePath = null)
|
public EngineBuilder LogToFile(bool parLogToFile = true, string? parLogFilePath = null)
|
||||||
{
|
{
|
||||||
if (parLogToFile && parLogFilePath == null)
|
if (parLogToFile && parLogFilePath == null)
|
||||||
@@ -95,12 +195,21 @@ public sealed class EngineBuilder
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the minimum log level for logging.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parLogLevel">The minimum level of log events to capture.</param>
|
||||||
|
/// <returns>The current instance of <see cref="EngineBuilder"/> for chaining.</returns>
|
||||||
public EngineBuilder LogLevel(LogEventLevel parLogLevel)
|
public EngineBuilder LogLevel(LogEventLevel parLogLevel)
|
||||||
{
|
{
|
||||||
_logLevel = parLogLevel;
|
_logLevel = parLogLevel;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds and returns a new instance of the <see cref="Engine"/> class based on the configured settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A fully configured instance of <see cref="Engine"/>.</returns>
|
||||||
public Engine Build()
|
public Engine Build()
|
||||||
{
|
{
|
||||||
var logger = BuildLogger();
|
var logger = BuildLogger();
|
||||||
@@ -121,6 +230,10 @@ public sealed class EngineBuilder
|
|||||||
return engine;
|
return engine;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configures and builds a logger based on the current logging settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A configured instance of <see cref="Logger"/>.</returns>
|
||||||
private Logger BuildLogger()
|
private Logger BuildLogger()
|
||||||
{
|
{
|
||||||
const string template =
|
const string template =
|
||||||
|
|||||||
@@ -3,10 +3,21 @@ using Serilog;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Buffer;
|
namespace Engine.Graphics.Buffer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an OpenGL index buffer, used for storing indices for rendering.
|
||||||
|
/// </summary>
|
||||||
public class IndexBuffer : OpenGlObject
|
public class IndexBuffer : OpenGlObject
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The number of indices in the buffer.
|
||||||
|
/// </summary>
|
||||||
internal int Count { get; }
|
internal int Count { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new index buffer with a specified number of elements.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parCount">The number of indices the buffer will store.</param>
|
||||||
|
/// <param name="parFlags">Optional storage flags for the buffer.</param>
|
||||||
public IndexBuffer(int parCount, BufferStorageFlags parFlags = BufferStorageFlags.None)
|
public IndexBuffer(int parCount, BufferStorageFlags parFlags = BufferStorageFlags.None)
|
||||||
{
|
{
|
||||||
Count = parCount;
|
Count = parCount;
|
||||||
@@ -20,6 +31,11 @@ public class IndexBuffer : OpenGlObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new index buffer with the provided data.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parData">The data to populate the buffer with.</param>
|
||||||
|
/// <param name="parFlags">Optional storage flags for the buffer.</param>
|
||||||
public IndexBuffer(uint[] parData, BufferStorageFlags parFlags = BufferStorageFlags.None)
|
public IndexBuffer(uint[] parData, BufferStorageFlags parFlags = BufferStorageFlags.None)
|
||||||
{
|
{
|
||||||
Count = parData.Length;
|
Count = parData.Length;
|
||||||
@@ -32,11 +48,21 @@ public class IndexBuffer : OpenGlObject
|
|||||||
Log.Debug("Index buffer {Handle} created with {Count} elements", Handle, Count);
|
Log.Debug("Index buffer {Handle} created with {Count} elements", Handle, Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uploads data to the buffer starting at offset 0.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parData">The data to upload.</param>
|
||||||
public void UploadData(uint[] parData)
|
public void UploadData(uint[] parData)
|
||||||
{
|
{
|
||||||
UploadData(0, parData);
|
UploadData(0, parData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uploads data to the buffer at the specified offset.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parOffset">The offset (in elements) where data should be uploaded.</param>
|
||||||
|
/// <param name="parData">The data to upload.</param>
|
||||||
|
/// <exception cref="ArgumentException">Thrown if the offset is invalid or the data exceeds buffer size.</exception>
|
||||||
public void UploadData(int parOffset, uint[] parData)
|
public void UploadData(int parOffset, uint[] parData)
|
||||||
{
|
{
|
||||||
if (parOffset < 0)
|
if (parOffset < 0)
|
||||||
@@ -52,16 +78,19 @@ public class IndexBuffer : OpenGlObject
|
|||||||
GL.NamedBufferSubData(Handle, parOffset, parData.Length * sizeof(uint), parData);
|
GL.NamedBufferSubData(Handle, parOffset, parData.Length * sizeof(uint), parData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
internal override void Bind()
|
internal override void Bind()
|
||||||
{
|
{
|
||||||
GL.BindBuffer(BufferTarget.ElementArrayBuffer, Handle);
|
GL.BindBuffer(BufferTarget.ElementArrayBuffer, Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
internal override void Unbind()
|
internal override void Unbind()
|
||||||
{
|
{
|
||||||
GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
|
GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
protected override void Destroy()
|
protected override void Destroy()
|
||||||
{
|
{
|
||||||
GL.DeleteBuffer(Handle);
|
GL.DeleteBuffer(Handle);
|
||||||
|
|||||||
@@ -4,18 +4,38 @@ using OpenTK.Graphics.OpenGL;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Buffer.Vertex;
|
namespace Engine.Graphics.Buffer.Vertex;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interface for a vertex structure used in graphics buffers.
|
||||||
|
/// Provides methods for reflecting on vertex fields and checking their validity.
|
||||||
|
/// </summary>
|
||||||
public interface IVertex
|
public interface IVertex
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the fields of a type implementing <see cref="IVertex"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type to retrieve the fields for.</typeparam>
|
||||||
|
/// <returns>An ordered enumerable of <see cref="FieldInfo"/> for the fields in the type.</returns>
|
||||||
public static IOrderedEnumerable<FieldInfo> GetFields<T>()
|
public static IOrderedEnumerable<FieldInfo> GetFields<T>()
|
||||||
{
|
{
|
||||||
return GetFields(typeof(T));
|
return GetFields(typeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the fields of a specified type implementing <see cref="IVertex"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parType">The type to retrieve the fields for.</param>
|
||||||
|
/// <returns>An ordered enumerable of <see cref="FieldInfo"/> for the fields in the type.</returns>
|
||||||
public static IOrderedEnumerable<FieldInfo> GetFields(Type parType)
|
public static IOrderedEnumerable<FieldInfo> GetFields(Type parType)
|
||||||
{
|
{
|
||||||
return parType.GetFields(BindingFlags.Public | BindingFlags.Instance).OrderBy(parF => parF.MetadataToken);
|
return parType.GetFields(BindingFlags.Public | BindingFlags.Instance).OrderBy(parF => parF.MetadataToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validates if a type is a valid <see cref="IVertex"/>.
|
||||||
|
/// Checks if all fields are value types, have <see cref="VertexAttribute"/> attributes, and match their size.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parType">The type to validate.</param>
|
||||||
|
/// <returns>True if the type is valid, otherwise false.</returns>
|
||||||
public static bool IsValid(Type parType)
|
public static bool IsValid(Type parType)
|
||||||
{
|
{
|
||||||
if (!parType.IsValueType || !parType.IsAssignableTo(typeof(IVertex)))
|
if (!parType.IsValueType || !parType.IsAssignableTo(typeof(IVertex)))
|
||||||
@@ -50,6 +70,11 @@ public interface IVertex
|
|||||||
return totalSize == Marshal.SizeOf(parType);
|
return totalSize == Marshal.SizeOf(parType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the size of a vertex attribute based on its OpenGL type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parType">The OpenGL type of the attribute.</param>
|
||||||
|
/// <returns>The size of the attribute in bytes.</returns>
|
||||||
public static int AttributeSize(VertexAttribType parType)
|
public static int AttributeSize(VertexAttribType parType)
|
||||||
{
|
{
|
||||||
return parType switch
|
return parType switch
|
||||||
|
|||||||
@@ -2,14 +2,40 @@
|
|||||||
|
|
||||||
namespace Engine.Graphics.Buffer.Vertex;
|
namespace Engine.Graphics.Buffer.Vertex;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attribute used to describe a vertex field.
|
||||||
|
/// Provides details about the attribute's type, component count, normalization, and repetition.
|
||||||
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Field)]
|
[AttributeUsage(AttributeTargets.Field)]
|
||||||
public class VertexAttribute : Attribute
|
public class VertexAttribute : Attribute
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// OpenGL type of the vertex attribute.
|
||||||
|
/// </summary>
|
||||||
public VertexAttribType Type { get; }
|
public VertexAttribType Type { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Number of components in the vertex attribute.
|
||||||
|
/// </summary>
|
||||||
public int ComponentCount { get; }
|
public int ComponentCount { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the vertex attribute is normalized.
|
||||||
|
/// </summary>
|
||||||
public bool Normalized { get; }
|
public bool Normalized { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Number of times the vertex attribute is repeated.
|
||||||
|
/// </summary>
|
||||||
public int RepeatCount { get; }
|
public int RepeatCount { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="VertexAttribute"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parType">The OpenGL type of the vertex attribute.</param>
|
||||||
|
/// <param name="parComponentCount">The number of components in the vertex attribute.</param>
|
||||||
|
/// <param name="parRepeatCount">The repeat count for the vertex attribute.</param>
|
||||||
|
/// <param name="parNormalized">Whether the attribute is normalized.</param>
|
||||||
public VertexAttribute(VertexAttribType parType, int parComponentCount = 1, int parRepeatCount = 1,
|
public VertexAttribute(VertexAttribType parType, int parComponentCount = 1, int parRepeatCount = 1,
|
||||||
bool parNormalized = false
|
bool parNormalized = false
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,10 +6,19 @@ using Serilog;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Buffer;
|
namespace Engine.Graphics.Buffer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a vertex array object in OpenGL, managing vertex buffer bindings and configurations.
|
||||||
|
/// </summary>
|
||||||
public class VertexArray : OpenGlObject
|
public class VertexArray : OpenGlObject
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Stores the enabled attribute flags for this vertex array.
|
||||||
|
/// </summary>
|
||||||
private int _enabledAttributes;
|
private int _enabledAttributes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="VertexArray"/> class, creating an OpenGL vertex array object.
|
||||||
|
/// </summary>
|
||||||
public VertexArray()
|
public VertexArray()
|
||||||
{
|
{
|
||||||
GL.CreateVertexArrays(1, out int handle);
|
GL.CreateVertexArrays(1, out int handle);
|
||||||
@@ -18,6 +27,10 @@ public class VertexArray : OpenGlObject
|
|||||||
Log.Debug("Vertex array {Handle} created", Handle);
|
Log.Debug("Vertex array {Handle} created", Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Binds an index buffer to this vertex array.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parBuffer">The index buffer to bind.</param>
|
||||||
public void BindIndexBuffer(IndexBuffer parBuffer)
|
public void BindIndexBuffer(IndexBuffer parBuffer)
|
||||||
{
|
{
|
||||||
GL.VertexArrayElementBuffer(Handle, parBuffer.Handle);
|
GL.VertexArrayElementBuffer(Handle, parBuffer.Handle);
|
||||||
@@ -25,6 +38,13 @@ public class VertexArray : OpenGlObject
|
|||||||
Log.Debug("Index buffer {Buffer} bound to vertex array {Handle}", parBuffer.Handle, Handle);
|
Log.Debug("Index buffer {Buffer} bound to vertex array {Handle}", parBuffer.Handle, Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Binds a vertex buffer to this vertex array and configures its vertex attributes.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the vertex buffer, which must implement the <see cref="IVertex"/> interface.</typeparam>
|
||||||
|
/// <param name="parBuffer">The vertex buffer to bind.</param>
|
||||||
|
/// <param name="parBindingIndex">The binding index for the vertex buffer. Defaults to 0.</param>
|
||||||
|
/// <param name="parDivisor">The divisor for instancing, which controls how often the vertex data is used. Defaults to 0.</param>
|
||||||
public void BindVertexBuffer<T>(VertexBuffer<T> parBuffer, int parBindingIndex = 0, int parDivisor = 0)
|
public void BindVertexBuffer<T>(VertexBuffer<T> parBuffer, int parBindingIndex = 0, int parDivisor = 0)
|
||||||
where T : struct, IVertex
|
where T : struct, IVertex
|
||||||
{
|
{
|
||||||
@@ -52,6 +72,13 @@ public class VertexArray : OpenGlObject
|
|||||||
parBuffer.Handle, Handle, parBindingIndex, parDivisor);
|
parBuffer.Handle, Handle, parBindingIndex, parDivisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configures a vertex attribute for a vertex array.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parAttribute">The vertex attribute to configure.</param>
|
||||||
|
/// <param name="parBaseLocation">The base location for the attribute.</param>
|
||||||
|
/// <param name="parBaseOffset">The base offset for the attribute.</param>
|
||||||
|
/// <param name="parBindingIndex">The binding index for the attribute.</param>
|
||||||
private void SetupAttribute(VertexAttribute parAttribute, int parBaseLocation, int parBaseOffset, int parBindingIndex)
|
private void SetupAttribute(VertexAttribute parAttribute, int parBaseLocation, int parBaseOffset, int parBindingIndex)
|
||||||
{
|
{
|
||||||
var size = parAttribute.ComponentCount * IVertex.AttributeSize(parAttribute.Type);
|
var size = parAttribute.ComponentCount * IVertex.AttributeSize(parAttribute.Type);
|
||||||
@@ -69,16 +96,19 @@ public class VertexArray : OpenGlObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
internal override void Bind()
|
internal override void Bind()
|
||||||
{
|
{
|
||||||
GL.BindVertexArray(Handle);
|
GL.BindVertexArray(Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
internal override void Unbind()
|
internal override void Unbind()
|
||||||
{
|
{
|
||||||
GL.BindVertexArray(0);
|
GL.BindVertexArray(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
protected override void Destroy()
|
protected override void Destroy()
|
||||||
{
|
{
|
||||||
GL.DeleteVertexArray(Handle);
|
GL.DeleteVertexArray(Handle);
|
||||||
|
|||||||
@@ -5,13 +5,29 @@ using Serilog;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Buffer;
|
namespace Engine.Graphics.Buffer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an OpenGL vertex buffer for storing vertex data.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The vertex structure type implementing <see cref="IVertex"/>.</typeparam>
|
||||||
public class VertexBuffer<T> : OpenGlObject
|
public class VertexBuffer<T> : OpenGlObject
|
||||||
where T : struct, IVertex
|
where T : struct, IVertex
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The number of vertices in the buffer.
|
||||||
|
/// </summary>
|
||||||
internal int Count { get; }
|
internal int Count { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The size of a single vertex in bytes.
|
||||||
|
/// </summary>
|
||||||
private readonly int _stride = Marshal.SizeOf<T>();
|
private readonly int _stride = Marshal.SizeOf<T>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new vertex buffer with a specified number of elements.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parCount">The number of vertices the buffer will store.</param>
|
||||||
|
/// <param name="parFlags">Optional storage flags for the buffer.</param>
|
||||||
|
/// <exception cref="ArgumentException">Thrown if the type is not a valid vertex type or the count is invalid.</exception>
|
||||||
public VertexBuffer(int parCount, BufferStorageFlags parFlags = BufferStorageFlags.None)
|
public VertexBuffer(int parCount, BufferStorageFlags parFlags = BufferStorageFlags.None)
|
||||||
{
|
{
|
||||||
if (!IVertex.IsValid(typeof(T)))
|
if (!IVertex.IsValid(typeof(T)))
|
||||||
@@ -34,6 +50,12 @@ public class VertexBuffer<T> : OpenGlObject
|
|||||||
Log.Debug("Vertex buffer {Handle} created with {Count} elements of type {Type}", Handle, Count, typeof(T).Name);
|
Log.Debug("Vertex buffer {Handle} created with {Count} elements of type {Type}", Handle, Count, typeof(T).Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new vertex buffer with the provided data.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parData">The data to populate the buffer with.</param>
|
||||||
|
/// <param name="parFlags">Optional storage flags for the buffer.</param>
|
||||||
|
/// <exception cref="ArgumentException">Thrown if the type is not a valid vertex type or the data is invalid.</exception>
|
||||||
public VertexBuffer(T[] parData, BufferStorageFlags parFlags = BufferStorageFlags.None)
|
public VertexBuffer(T[] parData, BufferStorageFlags parFlags = BufferStorageFlags.None)
|
||||||
{
|
{
|
||||||
if (!IVertex.IsValid(typeof(T)))
|
if (!IVertex.IsValid(typeof(T)))
|
||||||
@@ -56,16 +78,32 @@ public class VertexBuffer<T> : OpenGlObject
|
|||||||
Log.Debug("Vertex buffer {Handle} created with {Count} elements of type {Type}", Handle, Count, typeof(T).Name);
|
Log.Debug("Vertex buffer {Handle} created with {Count} elements of type {Type}", Handle, Count, typeof(T).Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uploads data to the buffer starting at offset 0.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parData">The data to upload.</param>
|
||||||
public void UploadData(T[] parData)
|
public void UploadData(T[] parData)
|
||||||
{
|
{
|
||||||
UploadData(0, parData, parData.Length);
|
UploadData(0, parData, parData.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uploads data to the buffer starting at offset 0 with a specified count.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parData">The data to upload.</param>
|
||||||
|
/// <param name="parCount">The number of elements to upload.</param>
|
||||||
public void UploadData(T[] parData, int parCount)
|
public void UploadData(T[] parData, int parCount)
|
||||||
{
|
{
|
||||||
UploadData(0, parData, parCount);
|
UploadData(0, parData, parCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uploads data to the buffer at the specified offset.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parOffset">The offset (in elements) where data should be uploaded.</param>
|
||||||
|
/// <param name="parData">The data to upload.</param>
|
||||||
|
/// <param name="parCount">The number of elements to upload.</param>
|
||||||
|
/// <exception cref="ArgumentException">Thrown if parameters are invalid or data exceeds buffer size.</exception>
|
||||||
public void UploadData(int parOffset, T[] parData, int parCount)
|
public void UploadData(int parOffset, T[] parData, int parCount)
|
||||||
{
|
{
|
||||||
if (parOffset < 0)
|
if (parOffset < 0)
|
||||||
@@ -91,16 +129,19 @@ public class VertexBuffer<T> : OpenGlObject
|
|||||||
GL.NamedBufferSubData(Handle, parOffset * _stride, parCount * _stride, parData);
|
GL.NamedBufferSubData(Handle, parOffset * _stride, parCount * _stride, parData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
internal override void Bind()
|
internal override void Bind()
|
||||||
{
|
{
|
||||||
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
|
GL.BindBuffer(BufferTarget.ArrayBuffer, Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
internal override void Unbind()
|
internal override void Unbind()
|
||||||
{
|
{
|
||||||
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
|
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
protected override void Destroy()
|
protected override void Destroy()
|
||||||
{
|
{
|
||||||
GL.DeleteBuffer(Handle);
|
GL.DeleteBuffer(Handle);
|
||||||
|
|||||||
@@ -2,9 +2,23 @@
|
|||||||
|
|
||||||
namespace Engine.Graphics.Camera;
|
namespace Engine.Graphics.Camera;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the interface for camera functionality, including projection and view matrices.
|
||||||
|
/// </summary>
|
||||||
public interface ICamera
|
public interface ICamera
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The matrix representing the camera's view transformation.
|
||||||
|
/// </summary>
|
||||||
Matrix4 View { get; }
|
Matrix4 View { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The matrix representing the camera's projection transformation.
|
||||||
|
/// </summary>
|
||||||
Matrix4 Projection { get; }
|
Matrix4 Projection { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The dimensions of the screen in pixels.
|
||||||
|
/// </summary>
|
||||||
Vector2i ScreenSize { get; internal set; }
|
Vector2i ScreenSize { get; internal set; }
|
||||||
}
|
}
|
||||||
@@ -2,9 +2,17 @@
|
|||||||
|
|
||||||
namespace Engine.Graphics.Camera;
|
namespace Engine.Graphics.Camera;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a camera that uses a screenspace projection.
|
||||||
|
/// </summary>
|
||||||
public class ScreenspaceCamera : ICamera
|
public class ScreenspaceCamera : ICamera
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
public Matrix4 View => Matrix4.Identity;
|
public Matrix4 View => Matrix4.Identity;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public Matrix4 Projection => Matrix4.Identity;
|
public Matrix4 Projection => Matrix4.Identity;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public Vector2i ScreenSize { get; set; }
|
public Vector2i ScreenSize { get; set; }
|
||||||
}
|
}
|
||||||
@@ -6,31 +6,59 @@ using Serilog.Events;
|
|||||||
|
|
||||||
namespace Engine.Graphics;
|
namespace Engine.Graphics;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides debugging utilities for OpenGL and integrates with RenderDoc for frame capturing.
|
||||||
|
/// </summary>
|
||||||
internal static class Debug
|
internal static class Debug
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The RenderDoc API instance for capturing and debugging GPU frames.
|
||||||
|
/// </summary>
|
||||||
private static RenderDoc _renderDocApi;
|
private static RenderDoc _renderDocApi;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configures OpenGL debugging and enables debug output.
|
||||||
|
/// </summary>
|
||||||
public static void Setup()
|
public static void Setup()
|
||||||
{
|
{
|
||||||
GL.Enable(EnableCap.DebugOutput);
|
GL.Enable(EnableCap.DebugOutput);
|
||||||
GL.DebugMessageCallback(DebugCallback, IntPtr.Zero);
|
GL.DebugMessageCallback(DebugCallback, IntPtr.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes RenderDoc support for capturing GPU frames.
|
||||||
|
/// </summary>
|
||||||
public static void InitializeRenderDoc()
|
public static void InitializeRenderDoc()
|
||||||
{
|
{
|
||||||
RenderDoc.Load(out _renderDocApi);
|
RenderDoc.Load(out _renderDocApi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts a new RenderDoc frame capture.
|
||||||
|
/// </summary>
|
||||||
public static void RenderDocStartFrame()
|
public static void RenderDocStartFrame()
|
||||||
{
|
{
|
||||||
// _renderDocApi.API.StartFrameCapture(IntPtr.Zero, IntPtr.Zero);
|
// _renderDocApi.API.StartFrameCapture(IntPtr.Zero, IntPtr.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ends the current RenderDoc frame capture.
|
||||||
|
/// </summary>
|
||||||
public static void RenderDocEndFrame()
|
public static void RenderDocEndFrame()
|
||||||
{
|
{
|
||||||
// _renderDocApi.API.EndFrameCapture(IntPtr.Zero, IntPtr.Zero);
|
// _renderDocApi.API.EndFrameCapture(IntPtr.Zero, IntPtr.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Callback for handling OpenGL debug messages.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parSource">The source of the debug message.</param>
|
||||||
|
/// <param name="parType">The type of the debug message.</param>
|
||||||
|
/// <param name="parId">The identifier of the debug message.</param>
|
||||||
|
/// <param name="parSeverity">The severity of the debug message.</param>
|
||||||
|
/// <param name="parLength">The length of the debug message string.</param>
|
||||||
|
/// <param name="parMessage">A pointer to the debug message string.</param>
|
||||||
|
/// <param name="parUserParam">A pointer to user-defined data.</param>
|
||||||
private static void DebugCallback(DebugSource parSource, DebugType parType, int parId, DebugSeverity parSeverity,
|
private static void DebugCallback(DebugSource parSource, DebugType parType, int parId, DebugSeverity parSeverity,
|
||||||
int parLength,
|
int parLength,
|
||||||
IntPtr parMessage, IntPtr parUserParam)
|
IntPtr parMessage, IntPtr parUserParam)
|
||||||
@@ -73,6 +101,11 @@ internal static class Debug
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps OpenGL debug severity levels to Serilog logging levels.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parSeverity">The severity of the OpenGL debug message.</param>
|
||||||
|
/// <returns>The corresponding Serilog log level.</returns>
|
||||||
private static LogEventLevel GetLogLevel(DebugSeverity parSeverity)
|
private static LogEventLevel GetLogLevel(DebugSeverity parSeverity)
|
||||||
{
|
{
|
||||||
return parSeverity switch
|
return parSeverity switch
|
||||||
|
|||||||
@@ -5,8 +5,15 @@ using Serilog;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Framebuffer;
|
namespace Engine.Graphics.Framebuffer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an OpenGL framebuffer object, which manages a collection of attachments,
|
||||||
|
/// such as color and depth buffers, and provides functionality for binding, resizing, and checking status.
|
||||||
|
/// </summary>
|
||||||
public class Framebuffer : OpenGlObject
|
public class Framebuffer : OpenGlObject
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The width of the framebuffer.
|
||||||
|
/// </summary>
|
||||||
public int Width
|
public int Width
|
||||||
{
|
{
|
||||||
get => _width;
|
get => _width;
|
||||||
@@ -18,6 +25,9 @@ public class Framebuffer : OpenGlObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The height of the framebuffer.
|
||||||
|
/// </summary>
|
||||||
public int Height
|
public int Height
|
||||||
{
|
{
|
||||||
get => _height;
|
get => _height;
|
||||||
@@ -29,13 +39,32 @@ public class Framebuffer : OpenGlObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the internal texture attached to the framebuffer at the color attachment point.
|
||||||
|
/// </summary>
|
||||||
internal Texture.Texture? TextureInternal => GetAttachment<DynamicTexture>(FramebufferAttachment.ColorAttachment0);
|
internal Texture.Texture? TextureInternal => GetAttachment<DynamicTexture>(FramebufferAttachment.ColorAttachment0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A collection of framebuffer attachments, mapped by their attachment point.
|
||||||
|
/// </summary>
|
||||||
private readonly IDictionary<FramebufferAttachment, IFramebufferAttachment> _attachments;
|
private readonly IDictionary<FramebufferAttachment, IFramebufferAttachment> _attachments;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The width of the framebuffer.
|
||||||
|
/// </summary>
|
||||||
private int _width;
|
private int _width;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The height of the framebuffer.
|
||||||
|
/// </summary>
|
||||||
private int _height;
|
private int _height;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Framebuffer"/> class with the specified width, height, and attachments.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parWidth">The width of the framebuffer.</param>
|
||||||
|
/// <param name="parHeight">The height of the framebuffer.</param>
|
||||||
|
/// <param name="parAttachments">A dictionary of attachments to associate with the framebuffer.</param>
|
||||||
internal Framebuffer(int parWidth, int parHeight,
|
internal Framebuffer(int parWidth, int parHeight,
|
||||||
IDictionary<FramebufferAttachment, IFramebufferAttachment> parAttachments)
|
IDictionary<FramebufferAttachment, IFramebufferAttachment> parAttachments)
|
||||||
{
|
{
|
||||||
@@ -58,11 +87,23 @@ public class Framebuffer : OpenGlObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a builder for creating a new framebuffer with the specified width and height.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parWidth">The width of the framebuffer.</param>
|
||||||
|
/// <param name="parHeight">The height of the framebuffer.</param>
|
||||||
|
/// <returns>A <see cref="FramebufferBuilder"/> that can be used to customize the framebuffer.</returns>
|
||||||
public static FramebufferBuilder Builder(int parWidth, int parHeight)
|
public static FramebufferBuilder Builder(int parWidth, int parHeight)
|
||||||
{
|
{
|
||||||
return new FramebufferBuilder(parWidth, parHeight);
|
return new FramebufferBuilder(parWidth, parHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the attachment of the specified type from the framebuffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the attachment to retrieve.</typeparam>
|
||||||
|
/// <param name="parAttachment">The attachment point to retrieve the attachment from.</param>
|
||||||
|
/// <returns>The attachment if found, otherwise <c>null</c>.</returns>
|
||||||
internal T? GetAttachment<T>(FramebufferAttachment parAttachment) where T : IFramebufferAttachment
|
internal T? GetAttachment<T>(FramebufferAttachment parAttachment) where T : IFramebufferAttachment
|
||||||
{
|
{
|
||||||
if (!_attachments.TryGetValue(parAttachment, out var attachmentValue))
|
if (!_attachments.TryGetValue(parAttachment, out var attachmentValue))
|
||||||
@@ -78,6 +119,11 @@ public class Framebuffer : OpenGlObject
|
|||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resizes the framebuffer and all its attachments to the specified width and height.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parWidth">The new width of the framebuffer.</param>
|
||||||
|
/// <param name="parHeight">The new height of the framebuffer.</param>
|
||||||
public void Resize(int parWidth, int parHeight)
|
public void Resize(int parWidth, int parHeight)
|
||||||
{
|
{
|
||||||
if (Width == parWidth && Height == parHeight)
|
if (Width == parWidth && Height == parHeight)
|
||||||
@@ -96,16 +142,19 @@ public class Framebuffer : OpenGlObject
|
|||||||
Log.Debug("Framebuffer {Handle} resized to {Width}x{Height}", Handle, parWidth, parHeight);
|
Log.Debug("Framebuffer {Handle} resized to {Width}x{Height}", Handle, parWidth, parHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
internal override void Bind()
|
internal override void Bind()
|
||||||
{
|
{
|
||||||
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Handle);
|
GL.BindFramebuffer(FramebufferTarget.Framebuffer, Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
internal override void Unbind()
|
internal override void Unbind()
|
||||||
{
|
{
|
||||||
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
|
GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
protected override void Destroy()
|
protected override void Destroy()
|
||||||
{
|
{
|
||||||
GL.DeleteFramebuffer(Handle);
|
GL.DeleteFramebuffer(Handle);
|
||||||
|
|||||||
@@ -4,13 +4,52 @@ using OpenTK.Graphics.OpenGL;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Framebuffer;
|
namespace Engine.Graphics.Framebuffer;
|
||||||
|
|
||||||
public class FramebufferBuilder(int parWidth, int parHeight)
|
/// <summary>
|
||||||
|
/// A builder class used to construct framebuffers with custom attachments, such as color and depth buffers.
|
||||||
|
/// </summary>
|
||||||
|
public class FramebufferBuilder
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The width of the framebuffer.
|
||||||
|
/// </summary>
|
||||||
|
private readonly int _width;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The height of the framebuffer.
|
||||||
|
/// </summary>
|
||||||
|
private readonly int _height;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A list of attachments to be added to the framebuffer.
|
||||||
|
/// </summary>
|
||||||
private readonly List<AttachmentSpecification> _attachments = [];
|
private readonly List<AttachmentSpecification> _attachments = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A set of used attachment points.
|
||||||
|
/// </summary>
|
||||||
private readonly HashSet<FramebufferAttachment> _usedAttachments = [];
|
private readonly HashSet<FramebufferAttachment> _usedAttachments = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current color attachment point.
|
||||||
|
/// </summary>
|
||||||
private FramebufferAttachment _currentColorAttachment = FramebufferAttachment.ColorAttachment0;
|
private FramebufferAttachment _currentColorAttachment = FramebufferAttachment.ColorAttachment0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="FramebufferBuilder"/> class with the specified width and height.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parWidth">The width of the framebuffer.</param>
|
||||||
|
/// <param name="parHeight">The height of the framebuffer.</param>
|
||||||
|
public FramebufferBuilder(int parWidth, int parHeight)
|
||||||
|
{
|
||||||
|
_width = parWidth;
|
||||||
|
_height = parHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a color attachment to the framebuffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The pixel format to use for the color attachment.</typeparam>
|
||||||
|
/// <returns>The builder instance for method chaining.</returns>
|
||||||
public FramebufferBuilder AddColorAttachment<T>()
|
public FramebufferBuilder AddColorAttachment<T>()
|
||||||
where T : struct, IPixel
|
where T : struct, IPixel
|
||||||
{
|
{
|
||||||
@@ -30,6 +69,10 @@ public class FramebufferBuilder(int parWidth, int parHeight)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a depth attachment to the framebuffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The builder instance for method chaining.</returns>
|
||||||
public FramebufferBuilder AddDepthAttachment()
|
public FramebufferBuilder AddDepthAttachment()
|
||||||
{
|
{
|
||||||
return AddAttachment(new RenderbufferAttachmentSpecification
|
return AddAttachment(new RenderbufferAttachmentSpecification
|
||||||
@@ -39,6 +82,26 @@ public class FramebufferBuilder(int parWidth, int parHeight)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the framebuffer based on the current configuration.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A new <see cref="Framebuffer"/> instance.</returns>
|
||||||
|
public Framebuffer Build()
|
||||||
|
{
|
||||||
|
var attachments = _attachments
|
||||||
|
.ToDictionary(
|
||||||
|
parAttachment => parAttachment.FramebufferAttachment,
|
||||||
|
parAttachment => parAttachment.Create(_width, _height)
|
||||||
|
);
|
||||||
|
|
||||||
|
return new Framebuffer(_width, _height, attachments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a custom attachment to the framebuffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parAttachment">The attachment specification to add.</param>
|
||||||
|
/// <returns>The builder instance for method chaining.</returns>
|
||||||
private FramebufferBuilder AddAttachment(AttachmentSpecification parAttachment)
|
private FramebufferBuilder AddAttachment(AttachmentSpecification parAttachment)
|
||||||
{
|
{
|
||||||
if (_usedAttachments.Contains(parAttachment.FramebufferAttachment))
|
if (_usedAttachments.Contains(parAttachment.FramebufferAttachment))
|
||||||
@@ -53,43 +116,67 @@ public class FramebufferBuilder(int parWidth, int parHeight)
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Framebuffer Build()
|
/// <summary>
|
||||||
{
|
/// Represents a specification for a texture attachment in a framebuffer.
|
||||||
var attachments = _attachments
|
/// </summary>
|
||||||
.ToDictionary(
|
|
||||||
parAttachment => parAttachment.FramebufferAttachment,
|
|
||||||
parAttachment => parAttachment.Create(parWidth, parHeight)
|
|
||||||
);
|
|
||||||
|
|
||||||
return new Framebuffer(parWidth, parHeight, attachments);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TextureAttachmentSpecification : AttachmentSpecification
|
private class TextureAttachmentSpecification : AttachmentSpecification
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The pixel format for the texture attachment.
|
||||||
|
/// </summary>
|
||||||
public PixelFormat PixelFormat { get; init; }
|
public PixelFormat PixelFormat { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The pixel type for the texture attachment.
|
||||||
|
/// </summary>
|
||||||
public PixelType PixelType { get; init; }
|
public PixelType PixelType { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The internal format for the texture attachment.
|
||||||
|
/// </summary>
|
||||||
public PixelInternalFormat PixelInternalFormat { get; init; }
|
public PixelInternalFormat PixelInternalFormat { get; init; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override DynamicTexture Create(int parWidth, int parHeight)
|
public override DynamicTexture Create(int parWidth, int parHeight)
|
||||||
{
|
{
|
||||||
return new DynamicTexture(parWidth, parHeight, PixelFormat, PixelType, PixelInternalFormat);
|
return new DynamicTexture(parWidth, parHeight, PixelFormat, PixelType, PixelInternalFormat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a specification for a renderbuffer attachment in a framebuffer.
|
||||||
|
/// </summary>
|
||||||
private class RenderbufferAttachmentSpecification : AttachmentSpecification
|
private class RenderbufferAttachmentSpecification : AttachmentSpecification
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The storage format for the renderbuffer attachment.
|
||||||
|
/// </summary>
|
||||||
public RenderbufferStorage RenderbufferStorage { get; init; }
|
public RenderbufferStorage RenderbufferStorage { get; init; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override Renderbuffer Create(int parWidth, int parHeight)
|
public override Renderbuffer Create(int parWidth, int parHeight)
|
||||||
{
|
{
|
||||||
return new Renderbuffer(parWidth, parHeight, RenderbufferStorage);
|
return new Renderbuffer(parWidth, parHeight, RenderbufferStorage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a base class for framebuffer attachment specifications.
|
||||||
|
/// Provides common functionality for creating attachments like textures or renderbuffers.
|
||||||
|
/// </summary>
|
||||||
private abstract class AttachmentSpecification
|
private abstract class AttachmentSpecification
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The attachment point for the framebuffer.
|
||||||
|
/// </summary>
|
||||||
public FramebufferAttachment FramebufferAttachment { get; init; }
|
public FramebufferAttachment FramebufferAttachment { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new attachment for the framebuffer based on the specific type of attachment.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parWidth">The width of the attachment.</param>
|
||||||
|
/// <param name="parHeight">The height of the attachment.</param>
|
||||||
|
/// <returns>An instance of <see cref="IFramebufferAttachment"/> that represents the created attachment.</returns>
|
||||||
public abstract IFramebufferAttachment Create(int parWidth, int parHeight);
|
public abstract IFramebufferAttachment Create(int parWidth, int parHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,11 +2,27 @@
|
|||||||
|
|
||||||
namespace Engine.Graphics.Framebuffer;
|
namespace Engine.Graphics.Framebuffer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interface representing a framebuffer attachment, providing methods to attach, resize, and manage the attachment's OpenGL handle.
|
||||||
|
/// </summary>
|
||||||
internal interface IFramebufferAttachment
|
internal interface IFramebufferAttachment
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The handle that uniquely identifies the OpenGL object.
|
||||||
|
/// </summary>
|
||||||
int Handle { get; }
|
int Handle { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resizes the framebuffer attachment to the specified width and height.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parWidth">The new width of the framebuffer attachment.</param>
|
||||||
|
/// <param name="parHeight">The new height of the framebuffer attachment.</param>
|
||||||
void Resize(int parWidth, int parHeight);
|
void Resize(int parWidth, int parHeight);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attaches the framebuffer attachment to a specified framebuffer and attachment point.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parFramebuffer">The framebuffer to attach to.</param>
|
||||||
|
/// <param name="parAttachment">The attachment point of the framebuffer to attach to.</param>
|
||||||
void Attach(Framebuffer parFramebuffer, FramebufferAttachment parAttachment);
|
void Attach(Framebuffer parFramebuffer, FramebufferAttachment parAttachment);
|
||||||
}
|
}
|
||||||
@@ -2,13 +2,32 @@
|
|||||||
|
|
||||||
namespace Engine.Graphics.Framebuffer;
|
namespace Engine.Graphics.Framebuffer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a renderbuffer object in OpenGL, which can be used as an attachment to a framebuffer.
|
||||||
|
/// </summary>
|
||||||
public class Renderbuffer : OpenGlObject, IFramebufferAttachment
|
public class Renderbuffer : OpenGlObject, IFramebufferAttachment
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The width of the renderbuffer.
|
||||||
|
/// </summary>
|
||||||
public int Width { get; private set; }
|
public int Width { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The height of the renderbuffer.
|
||||||
|
/// </summary>
|
||||||
public int Height { get; private set; }
|
public int Height { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The format of the renderbuffer (e.g., depth, stencil, or color).
|
||||||
|
/// </summary>
|
||||||
private readonly RenderbufferStorage _format;
|
private readonly RenderbufferStorage _format;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Renderbuffer"/> class with specified dimensions and format.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parWidth">The width of the renderbuffer.</param>
|
||||||
|
/// <param name="parHeight">The height of the renderbuffer.</param>
|
||||||
|
/// <param name="parFormat">The format of the renderbuffer (storage format).</param>
|
||||||
public Renderbuffer(int parWidth, int parHeight, RenderbufferStorage parFormat)
|
public Renderbuffer(int parWidth, int parHeight, RenderbufferStorage parFormat)
|
||||||
{
|
{
|
||||||
Width = parWidth;
|
Width = parWidth;
|
||||||
@@ -21,6 +40,7 @@ public class Renderbuffer : OpenGlObject, IFramebufferAttachment
|
|||||||
GL.NamedRenderbufferStorage(Handle, _format, Width, Height);
|
GL.NamedRenderbufferStorage(Handle, _format, Width, Height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void Resize(int parWidth, int parHeight)
|
public void Resize(int parWidth, int parHeight)
|
||||||
{
|
{
|
||||||
if (Width == parWidth && Height == parHeight)
|
if (Width == parWidth && Height == parHeight)
|
||||||
@@ -34,21 +54,25 @@ public class Renderbuffer : OpenGlObject, IFramebufferAttachment
|
|||||||
GL.NamedRenderbufferStorage(Handle, _format, Width, Height);
|
GL.NamedRenderbufferStorage(Handle, _format, Width, Height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void Attach(Framebuffer parFramebuffer, FramebufferAttachment parAttachment)
|
public void Attach(Framebuffer parFramebuffer, FramebufferAttachment parAttachment)
|
||||||
{
|
{
|
||||||
GL.NamedFramebufferRenderbuffer(parFramebuffer.Handle, parAttachment, RenderbufferTarget.Renderbuffer, Handle);
|
GL.NamedFramebufferRenderbuffer(parFramebuffer.Handle, parAttachment, RenderbufferTarget.Renderbuffer, Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
internal override void Bind()
|
internal override void Bind()
|
||||||
{
|
{
|
||||||
GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Handle);
|
GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
internal override void Unbind()
|
internal override void Unbind()
|
||||||
{
|
{
|
||||||
GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, 0);
|
GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
protected override void Destroy()
|
protected override void Destroy()
|
||||||
{
|
{
|
||||||
GL.DeleteRenderbuffer(Handle);
|
GL.DeleteRenderbuffer(Handle);
|
||||||
|
|||||||
@@ -7,21 +7,62 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Graphics;
|
namespace Engine.Graphics;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A generic renderer that supports rendering quads, meshes, and text.
|
||||||
|
/// </summary>
|
||||||
public class GenericRenderer : IRenderer
|
public class GenericRenderer : IRenderer
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides functionality to render quads.
|
||||||
|
/// </summary>
|
||||||
public QuadRenderer QuadRenderer => _quadRenderer ??= new QuadRenderer(_engine, 1024 * 8);
|
public QuadRenderer QuadRenderer => _quadRenderer ??= new QuadRenderer(_engine, 1024 * 8);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides functionality to render any type of mesh.
|
||||||
|
/// </summary>
|
||||||
public AnyMeshRenderer AnyMeshRenderer => _anyMeshRenderer ??= new AnyMeshRenderer(_engine, 1024);
|
public AnyMeshRenderer AnyMeshRenderer => _anyMeshRenderer ??= new AnyMeshRenderer(_engine, 1024);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides functionality to render text.
|
||||||
|
/// </summary>
|
||||||
public TextRenderer TextRenderer => _textRenderer ??= new TextRenderer(_engine, 1024 * 8);
|
public TextRenderer TextRenderer => _textRenderer ??= new TextRenderer(_engine, 1024 * 8);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The framebuffer used for rendering.
|
||||||
|
/// </summary>
|
||||||
internal readonly Framebuffer.Framebuffer _framebuffer;
|
internal readonly Framebuffer.Framebuffer _framebuffer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The engine instance associated with this renderer.
|
||||||
|
/// </summary>
|
||||||
private readonly Engine _engine;
|
private readonly Engine _engine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The quad renderer instance, lazily initialized.
|
||||||
|
/// </summary>
|
||||||
private QuadRenderer? _quadRenderer;
|
private QuadRenderer? _quadRenderer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The mesh renderer instance, lazily initialized.
|
||||||
|
/// </summary>
|
||||||
private AnyMeshRenderer? _anyMeshRenderer;
|
private AnyMeshRenderer? _anyMeshRenderer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The text renderer instance, lazily initialized.
|
||||||
|
/// </summary>
|
||||||
private TextRenderer? _textRenderer;
|
private TextRenderer? _textRenderer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the frame has been started.
|
||||||
|
/// </summary>
|
||||||
private bool _frameStarted;
|
private bool _frameStarted;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="GenericRenderer"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parEngine">The engine instance to use for rendering.</param>
|
||||||
|
/// <param name="parWidth">The initial width of the framebuffer.</param>
|
||||||
|
/// <param name="parHeight">The initial height of the framebuffer.</param>
|
||||||
public GenericRenderer(Engine parEngine, int parWidth, int parHeight)
|
public GenericRenderer(Engine parEngine, int parWidth, int parHeight)
|
||||||
{
|
{
|
||||||
_engine = parEngine;
|
_engine = parEngine;
|
||||||
@@ -32,11 +73,13 @@ public class GenericRenderer : IRenderer
|
|||||||
.Build();
|
.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void StartFrame()
|
public void StartFrame()
|
||||||
{
|
{
|
||||||
_frameStarted = true;
|
_frameStarted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void EndFrame(in Matrix4 parProjectionMatrix, in Matrix4 parViewMatrix)
|
public void EndFrame(in Matrix4 parProjectionMatrix, in Matrix4 parViewMatrix)
|
||||||
{
|
{
|
||||||
if (!_frameStarted)
|
if (!_frameStarted)
|
||||||
@@ -72,6 +115,7 @@ public class GenericRenderer : IRenderer
|
|||||||
_frameStarted = false;
|
_frameStarted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void Resize(int parWidth, int parHeight)
|
public void Resize(int parWidth, int parHeight)
|
||||||
{
|
{
|
||||||
_framebuffer.Resize(parWidth, parHeight);
|
_framebuffer.Resize(parWidth, parHeight);
|
||||||
|
|||||||
@@ -4,14 +4,39 @@ using OpenTK.Windowing.Common;
|
|||||||
|
|
||||||
namespace Engine.Graphics;
|
namespace Engine.Graphics;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines an interface for a presenter that handles updates, rendering, and presentation.
|
||||||
|
/// </summary>
|
||||||
public interface IPresenter : IUpdate, IRender
|
public interface IPresenter : IUpdate, IRender
|
||||||
{
|
{
|
||||||
public event Action<ResizeEventArgs> Resize;
|
/// <summary>
|
||||||
|
/// Occurs when the presenter is resized.
|
||||||
|
/// </summary>
|
||||||
|
public event Action<ResizeEventArgs> OnResize;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current width of the presenter.
|
||||||
|
/// </summary>
|
||||||
public int Width { get; }
|
public int Width { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current height of the presenter.
|
||||||
|
/// </summary>
|
||||||
public int Height { get; }
|
public int Height { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the presenter is exiting.
|
||||||
|
/// </summary>
|
||||||
public bool IsExiting { get; }
|
public bool IsExiting { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Presents a texture to the rendering surface.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parTexture">The texture to present.</param>
|
||||||
public void Present(IConstTexture parTexture);
|
public void Present(IConstTexture parTexture);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signals the presenter to exit.
|
||||||
|
/// </summary>
|
||||||
public void Exit();
|
public void Exit();
|
||||||
}
|
}
|
||||||
@@ -2,10 +2,27 @@
|
|||||||
|
|
||||||
namespace Engine.Graphics;
|
namespace Engine.Graphics;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interface defining the essential functionality for a renderer.
|
||||||
|
/// </summary>
|
||||||
internal interface IRenderer
|
internal interface IRenderer
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Prepares the renderer for a new frame.
|
||||||
|
/// </summary>
|
||||||
public void StartFrame();
|
public void StartFrame();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finalizes the rendering pipeline for the current frame.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parProjectionMatrix">The projection matrix to use for rendering.</param>
|
||||||
|
/// <param name="parViewMatrix">The view matrix to use for rendering.</param>
|
||||||
public void EndFrame(in Matrix4 parProjectionMatrix, in Matrix4 parViewMatrix);
|
public void EndFrame(in Matrix4 parProjectionMatrix, in Matrix4 parViewMatrix);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resizes the renderer to accommodate changes in viewport dimensions.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parWidth">The new width of the viewport.</param>
|
||||||
|
/// <param name="parHeight">The new height of the viewport.</param>
|
||||||
public void Resize(int parWidth, int parHeight);
|
public void Resize(int parWidth, int parHeight);
|
||||||
}
|
}
|
||||||
@@ -2,15 +2,34 @@
|
|||||||
|
|
||||||
namespace Engine.Graphics;
|
namespace Engine.Graphics;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an abstract OpenGL object with a handle for managing GPU resources.
|
||||||
|
/// </summary>
|
||||||
public abstract class OpenGlObject
|
public abstract class OpenGlObject
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The handle that uniquely identifies the OpenGL object.
|
||||||
|
/// </summary>
|
||||||
public int Handle { get; protected set; } = -1;
|
public int Handle { get; protected set; } = -1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Binds the OpenGL object for rendering or other operations.
|
||||||
|
/// </summary>
|
||||||
internal abstract void Bind();
|
internal abstract void Bind();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unbinds the OpenGL object, restoring the previous state.
|
||||||
|
/// </summary>
|
||||||
internal abstract void Unbind();
|
internal abstract void Unbind();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Releases the GPU resources associated with the OpenGL object.
|
||||||
|
/// </summary>
|
||||||
protected abstract void Destroy();
|
protected abstract void Destroy();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finalizer to ensure the OpenGL object is destroyed and resources are freed.
|
||||||
|
/// </summary>
|
||||||
~OpenGlObject()
|
~OpenGlObject()
|
||||||
{
|
{
|
||||||
if (Handle == -1)
|
if (Handle == -1)
|
||||||
|
|||||||
@@ -1,32 +1,64 @@
|
|||||||
namespace Engine.Graphics.Pipeline;
|
namespace Engine.Graphics.Pipeline;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a render layer in the rendering pipeline, providing a way to order and compare render layers.
|
||||||
|
/// </summary>
|
||||||
public class RenderLayer : IComparable<RenderLayer>
|
public class RenderLayer : IComparable<RenderLayer>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The default render layer, with a priority of 0.
|
||||||
|
/// </summary>
|
||||||
public static readonly RenderLayer DEFAULT = new("default", 0);
|
public static readonly RenderLayer DEFAULT = new("default", 0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The overlay render layer, with a priority of 1.
|
||||||
|
/// </summary>
|
||||||
public static readonly RenderLayer OVERLAY = new("overlay", 1);
|
public static readonly RenderLayer OVERLAY = new("overlay", 1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The HUD render layer, with a priority of 2.
|
||||||
|
/// </summary>
|
||||||
public static readonly RenderLayer HUD = new("hud", 2);
|
public static readonly RenderLayer HUD = new("hud", 2);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A read-only list of all render layers.
|
||||||
|
/// </summary>
|
||||||
public static readonly IReadOnlyList<RenderLayer> ALL = new List<RenderLayer> { DEFAULT, OVERLAY, HUD }.AsReadOnly();
|
public static readonly IReadOnlyList<RenderLayer> ALL = new List<RenderLayer> { DEFAULT, OVERLAY, HUD }.AsReadOnly();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the render layer.
|
||||||
|
/// </summary>
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The priority of the render layer.
|
||||||
|
/// </summary>
|
||||||
private readonly int _order;
|
private readonly int _order;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="RenderLayer"/> class with the specified name and priority.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parName">The name of the render layer.</param>
|
||||||
|
/// <param name="parOrder">The priority of the render layer.</param>
|
||||||
private RenderLayer(string parName, int parOrder)
|
private RenderLayer(string parName, int parOrder)
|
||||||
{
|
{
|
||||||
Name = parName;
|
Name = parName;
|
||||||
_order = parOrder;
|
_order = parOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public int CompareTo(RenderLayer? parOther)
|
public int CompareTo(RenderLayer? parOther)
|
||||||
{
|
{
|
||||||
return parOther == null ? 1 : _order.CompareTo(parOther._order);
|
return parOther == null ? 1 : _order.CompareTo(parOther._order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return Name;
|
return Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return Name.GetHashCode();
|
return Name.GetHashCode();
|
||||||
|
|||||||
@@ -2,11 +2,29 @@ using OpenTK.Graphics.OpenGL;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Pixel;
|
namespace Engine.Graphics.Pixel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the basic structure for a pixel type, which includes information
|
||||||
|
/// about the pixel's format, type, internal format, and sized internal format.
|
||||||
|
/// </summary>
|
||||||
public interface IPixel
|
public interface IPixel
|
||||||
{
|
{
|
||||||
PixelFormat Format { get; }
|
/// <summary>
|
||||||
PixelType Type { get; }
|
/// The format of the pixel (e.g., Red, RGB, RGBA).
|
||||||
|
/// </summary>
|
||||||
|
public PixelFormat Format { get; }
|
||||||
|
|
||||||
PixelInternalFormat InternalFormat { get; }
|
/// <summary>
|
||||||
SizedInternalFormat SizedInternalFormat { get; }
|
/// The type of the pixel data (e.g., UnsignedByte).
|
||||||
|
/// </summary>
|
||||||
|
public PixelType Type { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The internal format for the pixel (e.g., R8, RGB8, etc.).
|
||||||
|
/// </summary>
|
||||||
|
public PixelInternalFormat InternalFormat { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The sized internal format for the pixel (e.g., R8, RGB8, etc.).
|
||||||
|
/// </summary>
|
||||||
|
public SizedInternalFormat SizedInternalFormat { get; }
|
||||||
}
|
}
|
||||||
@@ -3,14 +3,27 @@ using OpenTK.Graphics.OpenGL;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Pixel;
|
namespace Engine.Graphics.Pixel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a single-channel red pixel with an 8-bit format.
|
||||||
|
/// Implements the <see cref="IPixel"/> interface for defining pixel properties.
|
||||||
|
/// </summary>
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
public struct R8 : IPixel
|
public struct R8 : IPixel
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
public PixelFormat Format => PixelFormat.Red;
|
public PixelFormat Format => PixelFormat.Red;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public PixelType Type => PixelType.UnsignedByte;
|
public PixelType Type => PixelType.UnsignedByte;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public PixelInternalFormat InternalFormat => PixelInternalFormat.R8;
|
public PixelInternalFormat InternalFormat => PixelInternalFormat.R8;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public SizedInternalFormat SizedInternalFormat => SizedInternalFormat.R8;
|
public SizedInternalFormat SizedInternalFormat => SizedInternalFormat.R8;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The red channel of the pixel.
|
||||||
|
/// </summary>
|
||||||
public byte R;
|
public byte R;
|
||||||
}
|
}
|
||||||
@@ -6,15 +6,33 @@ namespace Engine.Graphics.Pixel;
|
|||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
public struct Rgb8 : IPixel
|
public struct Rgb8 : IPixel
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
public PixelFormat Format => PixelFormat.Rgb;
|
public PixelFormat Format => PixelFormat.Rgb;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public PixelType Type => PixelType.UnsignedByte;
|
public PixelType Type => PixelType.UnsignedByte;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public PixelInternalFormat InternalFormat => PixelInternalFormat.Rgb8;
|
public PixelInternalFormat InternalFormat => PixelInternalFormat.Rgb8;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public SizedInternalFormat SizedInternalFormat => SizedInternalFormat.Rgb8;
|
public SizedInternalFormat SizedInternalFormat => SizedInternalFormat.Rgb8;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The red channel of the pixel.
|
||||||
|
/// </summary>
|
||||||
public byte R;
|
public byte R;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The green channel of the pixel.
|
||||||
|
/// </summary>
|
||||||
public byte G;
|
public byte G;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The blue channel of the pixel.
|
||||||
|
/// </summary>
|
||||||
public byte B;
|
public byte B;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override string ToString() => $"{R}, {G}, {B}";
|
public override string ToString() => $"{R}, {G}, {B}";
|
||||||
}
|
}
|
||||||
@@ -3,19 +3,45 @@ using OpenTK.Graphics.OpenGL;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Pixel;
|
namespace Engine.Graphics.Pixel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a four-channel RGBA pixel with an 8-bit format for each channel.
|
||||||
|
/// Implements the <see cref="IPixel"/> interface for defining pixel properties.
|
||||||
|
/// </summary>
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
public struct Rgba8 : IPixel
|
public struct Rgba8 : IPixel
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
public PixelFormat Format => PixelFormat.Rgba;
|
public PixelFormat Format => PixelFormat.Rgba;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public PixelType Type => PixelType.UnsignedByte;
|
public PixelType Type => PixelType.UnsignedByte;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public PixelInternalFormat InternalFormat => PixelInternalFormat.Rgba8;
|
public PixelInternalFormat InternalFormat => PixelInternalFormat.Rgba8;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public SizedInternalFormat SizedInternalFormat => SizedInternalFormat.Rgba8;
|
public SizedInternalFormat SizedInternalFormat => SizedInternalFormat.Rgba8;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The red channel of the pixel.
|
||||||
|
/// </summary>
|
||||||
public byte R;
|
public byte R;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The green channel of the pixel.
|
||||||
|
/// </summary>
|
||||||
public byte G;
|
public byte G;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The blue channel of the pixel.
|
||||||
|
/// </summary>
|
||||||
public byte B;
|
public byte B;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The alpha channel of the pixel.
|
||||||
|
/// </summary>
|
||||||
public byte A;
|
public byte A;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override string ToString() => $"{R}, {G}, {B}, {A}";
|
public override string ToString() => $"{R}, {G}, {B}, {A}";
|
||||||
}
|
}
|
||||||
@@ -6,22 +6,70 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Render;
|
namespace Engine.Graphics.Render;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for rendering vertices with support for instancing.
|
||||||
|
/// This class is generic, allowing customization for the common vertex type <typeparamref name="C"/>
|
||||||
|
/// and the instance-specific vertex type <typeparamref name="I"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="C">The type of the common vertex, must implement <see cref="IVertex"/>.</typeparam>
|
||||||
|
/// <typeparam name="I">The type of the instance vertex, must implement <see cref="IVertex"/>.</typeparam>
|
||||||
public abstract class InstancedRenderer<C, I>
|
public abstract class InstancedRenderer<C, I>
|
||||||
where C : struct, IVertex
|
where C : struct, IVertex
|
||||||
where I : struct, IVertex
|
where I : struct, IVertex
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The number of instances that can be rendered.
|
||||||
|
/// </summary>
|
||||||
protected readonly int _instanceCount;
|
protected readonly int _instanceCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The array of instance vertices, each vertex is of type <typeparamref name="I"/>.
|
||||||
|
/// </summary>
|
||||||
protected readonly I[] _instanceVertices;
|
protected readonly I[] _instanceVertices;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The count of instances that are queued for rendering.
|
||||||
|
/// </summary>
|
||||||
protected int _queuedInstanceCount;
|
protected int _queuedInstanceCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The type of primitive to be rendered (e.g., triangles, lines).
|
||||||
|
/// </summary>
|
||||||
private readonly PrimitiveType _primitiveType;
|
private readonly PrimitiveType _primitiveType;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The shader program used for rendering.
|
||||||
|
/// </summary>
|
||||||
private readonly Program _program;
|
private readonly Program _program;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The index buffer used for the rendering of indexed primitives.
|
||||||
|
/// </summary>
|
||||||
private readonly IndexBuffer _indexBuffer;
|
private readonly IndexBuffer _indexBuffer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer containing the common vertex data, of type <typeparamref name="C"/>.
|
||||||
|
/// </summary>
|
||||||
private readonly VertexBuffer<C> _commonVertexBuffer;
|
private readonly VertexBuffer<C> _commonVertexBuffer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer containing the instance-specific vertex data, of type <typeparamref name="I"/>.
|
||||||
|
/// </summary>
|
||||||
private readonly VertexBuffer<I> _instanceVertexBuffer;
|
private readonly VertexBuffer<I> _instanceVertexBuffer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The vertex array object that binds all the buffers for rendering.
|
||||||
|
/// </summary>
|
||||||
private readonly VertexArray _vertexArray;
|
private readonly VertexArray _vertexArray;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="InstancedRenderer{C, I}"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parPrimitiveType">The type of primitive to be rendered.</param>
|
||||||
|
/// <param name="parInstanceCount">The total number of instances to be rendered.</param>
|
||||||
|
/// <param name="parIndexBuffer">An array of indices for indexed rendering.</param>
|
||||||
|
/// <param name="parInstanceBuffer">An array of instance-specific vertex data.</param>
|
||||||
|
/// <param name="parProgram">The shader program to use for rendering.</param>
|
||||||
protected InstancedRenderer(PrimitiveType parPrimitiveType, int parInstanceCount,
|
protected InstancedRenderer(PrimitiveType parPrimitiveType, int parInstanceCount,
|
||||||
uint[] parIndexBuffer, C[] parInstanceBuffer,
|
uint[] parIndexBuffer, C[] parInstanceBuffer,
|
||||||
Program parProgram)
|
Program parProgram)
|
||||||
@@ -43,6 +91,11 @@ public abstract class InstancedRenderer<C, I>
|
|||||||
_vertexArray.BindVertexBuffer(_instanceVertexBuffer, 1, 1);
|
_vertexArray.BindVertexBuffer(_instanceVertexBuffer, 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Renders the instanced objects with the provided projection and view matrices.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parProjectionMatrix">The projection matrix to be used in the shader.</param>
|
||||||
|
/// <param name="parViewMatrix">The view matrix to be used in the shader.</param>
|
||||||
public void Render(in Matrix4 parProjectionMatrix, in Matrix4 parViewMatrix)
|
public void Render(in Matrix4 parProjectionMatrix, in Matrix4 parViewMatrix)
|
||||||
{
|
{
|
||||||
if (_queuedInstanceCount <= 0)
|
if (_queuedInstanceCount <= 0)
|
||||||
@@ -67,15 +120,26 @@ public abstract class InstancedRenderer<C, I>
|
|||||||
_queuedInstanceCount);
|
_queuedInstanceCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets the renderer, clearing the queued instance count.
|
||||||
|
/// </summary>
|
||||||
public virtual void Reset()
|
public virtual void Reset()
|
||||||
{
|
{
|
||||||
_queuedInstanceCount = 0;
|
_queuedInstanceCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allows derived classes to set additional uniforms for the shader program.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parProgram">The shader program to set additional uniforms for.</param>
|
||||||
protected virtual void SetAdditionalUniforms(Program parProgram)
|
protected virtual void SetAdditionalUniforms(Program parProgram)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the instance data has changed and needs to be uploaded to the GPU.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><c>true</c> if the data has changed; otherwise, <c>false</c>.</returns>
|
||||||
protected virtual bool DataChanged()
|
protected virtual bool DataChanged()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -3,13 +3,50 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Render.Mesh;
|
namespace Engine.Graphics.Render.Mesh;
|
||||||
|
|
||||||
public class AnyMeshRenderer(Engine parEngine, int parMaxInstanceCount)
|
/// <summary>
|
||||||
|
/// A renderer class that manages multiple meshes and delegates rendering to individual mesh renderers.
|
||||||
|
/// Handles batching of mesh instances and ensures that only the necessary mesh renderers are created.
|
||||||
|
/// </summary>
|
||||||
|
public class AnyMeshRenderer
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A dictionary that maps each mesh to its corresponding <see cref="MeshRenderer"/>.
|
||||||
|
/// </summary>
|
||||||
private readonly Dictionary<Asset.Mesh.Mesh, MeshRenderer> _meshRenderers = new();
|
private readonly Dictionary<Asset.Mesh.Mesh, MeshRenderer> _meshRenderers = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A hash set that tracks the meshes that were rendered in the current frame.
|
||||||
|
/// </summary>
|
||||||
private readonly HashSet<Asset.Mesh.Mesh> _frameMeshes = [];
|
private readonly HashSet<Asset.Mesh.Mesh> _frameMeshes = [];
|
||||||
|
|
||||||
private readonly Program _program = parEngine.EngineResourceManager.Load<Program>("shader/mesh");
|
/// <summary>
|
||||||
|
/// The shader program used for rendering meshes.
|
||||||
|
/// </summary>
|
||||||
|
private readonly Program _program;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum number of instances that can be rendered in a single frame.
|
||||||
|
/// </summary>
|
||||||
|
private readonly int _maxInstanceCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="AnyMeshRenderer"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parEngine">The engine used to load resources.</param>
|
||||||
|
/// <param name="parMaxInstanceCount">The maximum number of instances to render per frame.</param>
|
||||||
|
public AnyMeshRenderer(Engine parEngine, int parMaxInstanceCount)
|
||||||
|
{
|
||||||
|
_maxInstanceCount = parMaxInstanceCount;
|
||||||
|
_program = parEngine.EngineResourceManager.Load<Program>("shader/mesh");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Commits an instance of a mesh to the renderer, adding it to the render queue with the specified model matrix and optional texture.
|
||||||
|
/// If the mesh is not already being tracked, a new <see cref="MeshRenderer"/> will be created for it.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parMesh">The mesh to render.</param>
|
||||||
|
/// <param name="parModelMatrix">The model transformation matrix to apply to the mesh.</param>
|
||||||
|
/// <param name="parAlbedo">An optional texture to apply to the mesh. If null, no texture is applied.</param>
|
||||||
public void Commit(Asset.Mesh.Mesh parMesh, Matrix4 parModelMatrix, Texture.Texture? parAlbedo = null)
|
public void Commit(Asset.Mesh.Mesh parMesh, Matrix4 parModelMatrix, Texture.Texture? parAlbedo = null)
|
||||||
{
|
{
|
||||||
if (_meshRenderers.TryGetValue(parMesh, out var meshRenderer))
|
if (_meshRenderers.TryGetValue(parMesh, out var meshRenderer))
|
||||||
@@ -18,7 +55,7 @@ public class AnyMeshRenderer(Engine parEngine, int parMaxInstanceCount)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var newMeshRenderer = new MeshRenderer(parMesh, parMaxInstanceCount, _program);
|
var newMeshRenderer = new MeshRenderer(parMesh, _maxInstanceCount, _program);
|
||||||
newMeshRenderer.Commit(parModelMatrix, parAlbedo);
|
newMeshRenderer.Commit(parModelMatrix, parAlbedo);
|
||||||
|
|
||||||
_meshRenderers.Add(parMesh, newMeshRenderer);
|
_meshRenderers.Add(parMesh, newMeshRenderer);
|
||||||
@@ -27,6 +64,11 @@ public class AnyMeshRenderer(Engine parEngine, int parMaxInstanceCount)
|
|||||||
_frameMeshes.Add(parMesh);
|
_frameMeshes.Add(parMesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Renders all queued mesh instances by calling the <see cref="MeshRenderer.Render"/> method for each mesh renderer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parProjectionMatrix">The projection matrix to use for rendering.</param>
|
||||||
|
/// <param name="parViewMatrix">The view matrix to use for rendering.</param>
|
||||||
public void Render(in Matrix4 parProjectionMatrix, in Matrix4 parViewMatrix)
|
public void Render(in Matrix4 parProjectionMatrix, in Matrix4 parViewMatrix)
|
||||||
{
|
{
|
||||||
foreach (var meshRenderer in _meshRenderers.Values)
|
foreach (var meshRenderer in _meshRenderers.Values)
|
||||||
@@ -35,6 +77,9 @@ public class AnyMeshRenderer(Engine parEngine, int parMaxInstanceCount)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets the state of the mesh renderers and removes any meshes that were not rendered in the current frame.
|
||||||
|
/// </summary>
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
foreach (var meshRenderer in _meshRenderers.Values)
|
foreach (var meshRenderer in _meshRenderers.Values)
|
||||||
|
|||||||
@@ -4,11 +4,23 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Render.Mesh;
|
namespace Engine.Graphics.Render.Mesh;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an instance vertex structure for a mesh.
|
||||||
|
/// This vertex structure includes texture ID and model matrix.
|
||||||
|
/// </summary>
|
||||||
public struct MeshInstanceVertex : IVertex
|
public struct MeshInstanceVertex : IVertex
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The texture ID of the vertex.
|
||||||
|
/// </summary>
|
||||||
[Vertex(VertexAttribType.Int)] public int _textureId;
|
[Vertex(VertexAttribType.Int)] public int _textureId;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The model matrix of the vertex.
|
||||||
|
/// </summary>
|
||||||
[Vertex(VertexAttribType.Float, 4, 4)] public Matrix4 _modelMatrix;
|
[Vertex(VertexAttribType.Float, 4, 4)] public Matrix4 _modelMatrix;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return HashCode.Combine(_textureId, _modelMatrix);
|
return HashCode.Combine(_textureId, _modelMatrix);
|
||||||
|
|||||||
@@ -5,21 +5,53 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Render.Mesh;
|
namespace Engine.Graphics.Render.Mesh;
|
||||||
|
|
||||||
public class MeshRenderer(Asset.Mesh.Mesh parMesh, int parInstanceCount, Program parProgram)
|
/// <summary>
|
||||||
: InstancedRenderer<Asset.Mesh.Mesh.Vertex, MeshInstanceVertex>(
|
/// A renderer class for rendering meshes using instancing.
|
||||||
PrimitiveType.Triangles,
|
/// Handles the instancing process for meshes, including texture binding and model transformations.
|
||||||
parInstanceCount,
|
/// </summary>
|
||||||
parMesh.Indices.ToArray(),
|
public class MeshRenderer : InstancedRenderer<Asset.Mesh.Mesh.Vertex, MeshInstanceVertex>
|
||||||
parMesh.Vertices.ToArray(),
|
|
||||||
parProgram
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Maps textures to texture units with a limit of 16 texture units.
|
||||||
|
/// </summary>
|
||||||
private readonly TextureUnitMap _textureUnitMap = new(16);
|
private readonly TextureUnitMap _textureUnitMap = new(16);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores the texture unit indices used for binding textures.
|
||||||
|
/// </summary>
|
||||||
private readonly int[] _textureUnitIndices = new int[16];
|
private readonly int[] _textureUnitIndices = new int[16];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores the hash of the current frame, used for detecting changes in instance data.
|
||||||
|
/// </summary>
|
||||||
private int _frameHash;
|
private int _frameHash;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores the hash of the previous frame.
|
||||||
|
/// </summary>
|
||||||
private int _previousHash;
|
private int _previousHash;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="MeshRenderer"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parMesh">The mesh to render.</param>
|
||||||
|
/// <param name="parInstanceCount">The number of instances to render.</param>
|
||||||
|
/// <param name="parProgram">The shader program to use for rendering the mesh.</param>
|
||||||
|
public MeshRenderer(Asset.Mesh.Mesh parMesh, int parInstanceCount, Program parProgram)
|
||||||
|
: base(
|
||||||
|
PrimitiveType.Triangles, parInstanceCount,
|
||||||
|
parMesh.Indices.ToArray(),
|
||||||
|
parMesh.Vertices.ToArray(),
|
||||||
|
parProgram
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Commits an instance to the renderer, adding it to the queue with the specified model matrix and optional texture.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parModelMatrix">The model transformation matrix for this instance.</param>
|
||||||
|
/// <param name="parTexture">An optional texture to apply to the mesh. If null, no texture is applied.</param>
|
||||||
public void Commit(Matrix4 parModelMatrix, Texture.Texture? parTexture = null)
|
public void Commit(Matrix4 parModelMatrix, Texture.Texture? parTexture = null)
|
||||||
{
|
{
|
||||||
if (_queuedInstanceCount >= _instanceCount)
|
if (_queuedInstanceCount >= _instanceCount)
|
||||||
@@ -41,6 +73,16 @@ public class MeshRenderer(Asset.Mesh.Mesh parMesh, int parInstanceCount, Program
|
|||||||
_queuedInstanceCount++;
|
_queuedInstanceCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Reset()
|
||||||
|
{
|
||||||
|
base.Reset();
|
||||||
|
_textureUnitMap.Reset();
|
||||||
|
_previousHash = _frameHash;
|
||||||
|
_frameHash = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
protected override void SetAdditionalUniforms(Program parProgram)
|
protected override void SetAdditionalUniforms(Program parProgram)
|
||||||
{
|
{
|
||||||
foreach (var (texture, unit) in _textureUnitMap.Textures)
|
foreach (var (texture, unit) in _textureUnitMap.Textures)
|
||||||
@@ -52,16 +94,9 @@ public class MeshRenderer(Asset.Mesh.Mesh parMesh, int parInstanceCount, Program
|
|||||||
parProgram.SetUniform("uTexture", _textureUnitIndices);
|
parProgram.SetUniform("uTexture", _textureUnitIndices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
protected override bool DataChanged()
|
protected override bool DataChanged()
|
||||||
{
|
{
|
||||||
return _frameHash != _previousHash;
|
return _frameHash != _previousHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Reset()
|
|
||||||
{
|
|
||||||
base.Reset();
|
|
||||||
_textureUnitMap.Reset();
|
|
||||||
_previousHash = _frameHash;
|
|
||||||
_frameHash = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -4,8 +4,19 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Render.Quad;
|
namespace Engine.Graphics.Render.Quad;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a common vertex structure for a quad.
|
||||||
|
/// This vertex structure includes position and UV coordinates.
|
||||||
|
/// </summary>
|
||||||
public struct QuadCommonVertex : IVertex
|
public struct QuadCommonVertex : IVertex
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The position of the vertex in 3D space.
|
||||||
|
/// </summary>
|
||||||
[Vertex(VertexAttribType.Float, 3)] public Vector3 _position;
|
[Vertex(VertexAttribType.Float, 3)] public Vector3 _position;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The UV coordinates of the vertex.
|
||||||
|
/// </summary>
|
||||||
[Vertex(VertexAttribType.Float, 2)] public Vector2 _uv;
|
[Vertex(VertexAttribType.Float, 2)] public Vector2 _uv;
|
||||||
}
|
}
|
||||||
@@ -4,12 +4,28 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Render.Quad;
|
namespace Engine.Graphics.Render.Quad;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an instance vertex structure for a quad.
|
||||||
|
/// This vertex structure includes color, texture ID, and model matrix.
|
||||||
|
/// </summary>
|
||||||
public struct QuadInstanceVertex : IVertex
|
public struct QuadInstanceVertex : IVertex
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The color of the vertex.
|
||||||
|
/// </summary>
|
||||||
[Vertex(VertexAttribType.Float, 4)] public Vector4 _color;
|
[Vertex(VertexAttribType.Float, 4)] public Vector4 _color;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The texture ID of the vertex.
|
||||||
|
/// </summary>
|
||||||
[Vertex(VertexAttribType.Float)] public float _textureId;
|
[Vertex(VertexAttribType.Float)] public float _textureId;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The model matrix of the vertex.
|
||||||
|
/// </summary>
|
||||||
[Vertex(VertexAttribType.Float, 4, 4)] public Matrix4 _modelMatrix;
|
[Vertex(VertexAttribType.Float, 4, 4)] public Matrix4 _modelMatrix;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return HashCode.Combine(_color, _textureId, _modelMatrix);
|
return HashCode.Combine(_color, _textureId, _modelMatrix);
|
||||||
|
|||||||
@@ -5,14 +5,37 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Render.Quad;
|
namespace Engine.Graphics.Render.Quad;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A renderer class for rendering quadrilaterals (quads) using instancing.
|
||||||
|
/// Supports dynamic texture binding and manages the state for rendering multiple instances of quads.
|
||||||
|
/// </summary>
|
||||||
public class QuadRenderer : InstancedRenderer<QuadCommonVertex, QuadInstanceVertex>
|
public class QuadRenderer : InstancedRenderer<QuadCommonVertex, QuadInstanceVertex>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Maps textures to texture units with a limit of 16 texture units.
|
||||||
|
/// </summary>
|
||||||
private readonly TextureUnitMap _textureUnitMap = new(16);
|
private readonly TextureUnitMap _textureUnitMap = new(16);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores the texture unit indices used for binding textures.
|
||||||
|
/// </summary>
|
||||||
private readonly int[] _textureUnitIndices = new int[16];
|
private readonly int[] _textureUnitIndices = new int[16];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores the hash of the current frame, used for detecting changes in instance data.
|
||||||
|
/// </summary>
|
||||||
private int _frameHash;
|
private int _frameHash;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores the hash of the previous frame.
|
||||||
|
/// </summary>
|
||||||
private int _previousHash;
|
private int _previousHash;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="QuadRenderer"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parEngine">The engine used to load resources.</param>
|
||||||
|
/// <param name="parInstanceCount">The number of instances to render.</param>
|
||||||
public QuadRenderer(Engine parEngine, int parInstanceCount)
|
public QuadRenderer(Engine parEngine, int parInstanceCount)
|
||||||
: base(PrimitiveType.Triangles, parInstanceCount, [0, 2, 1, 2, 3, 1], [
|
: base(PrimitiveType.Triangles, parInstanceCount, [0, 2, 1, 2, 3, 1], [
|
||||||
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, 0) },
|
||||||
@@ -24,6 +47,12 @@ public class QuadRenderer : InstancedRenderer<QuadCommonVertex, QuadInstanceVert
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Commits an instance to the renderer, adding it to the queue with the specified model matrix, color, and optional texture.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parModelMatrix">The model transformation matrix for this instance.</param>
|
||||||
|
/// <param name="parColor">The color to apply to this instance.</param>
|
||||||
|
/// <param name="parTexture">An optional texture to apply to the quad. If null, no texture is applied.</param>
|
||||||
public void Commit(in Matrix4 parModelMatrix, in Vector4 parColor, Texture.Texture? parTexture = null)
|
public void Commit(in Matrix4 parModelMatrix, in Vector4 parColor, Texture.Texture? parTexture = null)
|
||||||
{
|
{
|
||||||
if (_queuedInstanceCount >= _instanceCount)
|
if (_queuedInstanceCount >= _instanceCount)
|
||||||
@@ -46,6 +75,16 @@ public class QuadRenderer : InstancedRenderer<QuadCommonVertex, QuadInstanceVert
|
|||||||
_queuedInstanceCount++;
|
_queuedInstanceCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Reset()
|
||||||
|
{
|
||||||
|
base.Reset();
|
||||||
|
_textureUnitMap.Reset();
|
||||||
|
_previousHash = _frameHash;
|
||||||
|
_frameHash = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
protected override void SetAdditionalUniforms(Program parProgram)
|
protected override void SetAdditionalUniforms(Program parProgram)
|
||||||
{
|
{
|
||||||
foreach (var (texture, unit) in _textureUnitMap.Textures)
|
foreach (var (texture, unit) in _textureUnitMap.Textures)
|
||||||
@@ -57,16 +96,9 @@ public class QuadRenderer : InstancedRenderer<QuadCommonVertex, QuadInstanceVert
|
|||||||
parProgram.SetUniform("uTexture", _textureUnitIndices);
|
parProgram.SetUniform("uTexture", _textureUnitIndices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
protected override bool DataChanged()
|
protected override bool DataChanged()
|
||||||
{
|
{
|
||||||
return _frameHash != _previousHash;
|
return _frameHash != _previousHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Reset()
|
|
||||||
{
|
|
||||||
base.Reset();
|
|
||||||
_textureUnitMap.Reset();
|
|
||||||
_previousHash = _frameHash;
|
|
||||||
_frameHash = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -4,10 +4,29 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Render.Text;
|
namespace Engine.Graphics.Render.Text;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a common vertex structure for a glyph.
|
||||||
|
/// This vertex structure includes color, atlas ID, unit range, and model matrix.
|
||||||
|
/// </summary>
|
||||||
public struct GlyphCommonVertex : IVertex
|
public struct GlyphCommonVertex : IVertex
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The color of the vertex.
|
||||||
|
/// </summary>
|
||||||
[Vertex(VertexAttribType.Float, 4)] public Vector4 _color;
|
[Vertex(VertexAttribType.Float, 4)] public Vector4 _color;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The atlas ID of the vertex.
|
||||||
|
/// </summary>
|
||||||
[Vertex(VertexAttribType.Int)] public int _atlasId;
|
[Vertex(VertexAttribType.Int)] public int _atlasId;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The unit range of the vertex.
|
||||||
|
/// </summary>
|
||||||
[Vertex(VertexAttribType.Float, 2)] public Vector2 _unitRange;
|
[Vertex(VertexAttribType.Float, 2)] public Vector2 _unitRange;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The model matrix of the vertex.
|
||||||
|
/// </summary>
|
||||||
[Vertex(VertexAttribType.Float, 4, 4)] public Matrix4 _modelMatrix;
|
[Vertex(VertexAttribType.Float, 4, 4)] public Matrix4 _modelMatrix;
|
||||||
}
|
}
|
||||||
@@ -4,12 +4,39 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Render.Text;
|
namespace Engine.Graphics.Render.Text;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a vertex structure for a glyph.
|
||||||
|
/// This vertex structure includes position, UV coordinates, color, atlas ID, unit range, and model matrix.
|
||||||
|
/// </summary>
|
||||||
public struct GlyphVertex : IVertex
|
public struct GlyphVertex : IVertex
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The position of the vertex in 2D space.
|
||||||
|
/// </summary>
|
||||||
[Vertex(VertexAttribType.Float, 2)] public Vector2 _position;
|
[Vertex(VertexAttribType.Float, 2)] public Vector2 _position;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The UV coordinates of the vertex.
|
||||||
|
/// </summary>
|
||||||
[Vertex(VertexAttribType.Float, 2)] public Vector2 _uv;
|
[Vertex(VertexAttribType.Float, 2)] public Vector2 _uv;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The color of the vertex.
|
||||||
|
/// </summary>
|
||||||
[Vertex(VertexAttribType.Float, 4)] public Vector4 _color;
|
[Vertex(VertexAttribType.Float, 4)] public Vector4 _color;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The atlas ID of the vertex.
|
||||||
|
/// </summary>
|
||||||
[Vertex(VertexAttribType.Int)] public int _atlasId;
|
[Vertex(VertexAttribType.Int)] public int _atlasId;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The unit range of the vertex.
|
||||||
|
/// </summary>
|
||||||
[Vertex(VertexAttribType.Float, 2)] public Vector2 _unitRange;
|
[Vertex(VertexAttribType.Float, 2)] public Vector2 _unitRange;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The model matrix of the vertex.
|
||||||
|
/// </summary>
|
||||||
[Vertex(VertexAttribType.Float, 4, 4)] public Matrix4 _modelMatrix;
|
[Vertex(VertexAttribType.Float, 4, 4)] public Matrix4 _modelMatrix;
|
||||||
}
|
}
|
||||||
@@ -7,21 +7,62 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Render.Text;
|
namespace Engine.Graphics.Render.Text;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A renderer class for rendering text using glyphs from a font atlas.
|
||||||
|
/// Handles dynamic font rendering with support for textures.
|
||||||
|
/// </summary>
|
||||||
public class TextRenderer
|
public class TextRenderer
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The shader program used for rendering the text.
|
||||||
|
/// </summary>
|
||||||
private readonly Program _program;
|
private readonly Program _program;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The index buffer used for rendering the text glyphs.
|
||||||
|
/// </summary>
|
||||||
private readonly IndexBuffer _indexBuffer;
|
private readonly IndexBuffer _indexBuffer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The buffer holding vertex data for all glyphs to be rendered.
|
||||||
|
/// </summary>
|
||||||
private readonly VertexBuffer<GlyphVertex> _glyphVertexBuffer;
|
private readonly VertexBuffer<GlyphVertex> _glyphVertexBuffer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The vertex array object that binds the index and vertex buffers.
|
||||||
|
/// </summary>
|
||||||
private readonly VertexArray _vertexArray;
|
private readonly VertexArray _vertexArray;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of characters that can be rendered at once.
|
||||||
|
/// </summary>
|
||||||
private readonly int _characterCount;
|
private readonly int _characterCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The array of glyph vertices representing the text to be rendered.
|
||||||
|
/// </summary>
|
||||||
private readonly GlyphVertex[] _glyphVertices;
|
private readonly GlyphVertex[] _glyphVertices;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps textures to texture units for rendering the font atlas.
|
||||||
|
/// </summary>
|
||||||
private readonly TextureUnitMap _textureUnitMap = new(16);
|
private readonly TextureUnitMap _textureUnitMap = new(16);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores the texture unit indices for binding textures.
|
||||||
|
/// </summary>
|
||||||
private readonly int[] _textureUnitIndices = new int[16];
|
private readonly int[] _textureUnitIndices = new int[16];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of characters that have been queued for rendering.
|
||||||
|
/// </summary>
|
||||||
private int _queuedCharacterCount;
|
private int _queuedCharacterCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="TextRenderer"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parEngine">The engine used to load resources.</param>
|
||||||
|
/// <param name="parCharacterCount">The maximum number of characters to render.</param>
|
||||||
public TextRenderer(Engine parEngine, int parCharacterCount)
|
public TextRenderer(Engine parEngine, int parCharacterCount)
|
||||||
{
|
{
|
||||||
_characterCount = parCharacterCount;
|
_characterCount = parCharacterCount;
|
||||||
@@ -39,6 +80,13 @@ public class TextRenderer
|
|||||||
_vertexArray.BindVertexBuffer(_glyphVertexBuffer);
|
_vertexArray.BindVertexBuffer(_glyphVertexBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Commits a string of text to the renderer, creating the necessary glyphs and adding them to the render queue.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parFont">The font to use for rendering the text.</param>
|
||||||
|
/// <param name="parText">The text string to render.</param>
|
||||||
|
/// <param name="parColor">The color to apply to the text.</param>
|
||||||
|
/// <param name="parModelMatrix">The model transformation matrix to apply to the text.</param>
|
||||||
public void Commit(Font parFont, string parText, Vector4 parColor, in Matrix4 parModelMatrix)
|
public void Commit(Font parFont, string parText, Vector4 parColor, in Matrix4 parModelMatrix)
|
||||||
{
|
{
|
||||||
if (_queuedCharacterCount >= _characterCount)
|
if (_queuedCharacterCount >= _characterCount)
|
||||||
@@ -66,6 +114,11 @@ public class TextRenderer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Renders the queued text characters to the screen with the given projection and view matrices.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parProjectionMatrix">The projection matrix to use for the render.</param>
|
||||||
|
/// <param name="parViewMatrix">The view matrix to use for the render.</param>
|
||||||
public void Render(in Matrix4 parProjectionMatrix, in Matrix4 parViewMatrix)
|
public void Render(in Matrix4 parProjectionMatrix, in Matrix4 parViewMatrix)
|
||||||
{
|
{
|
||||||
if (_queuedCharacterCount <= 0)
|
if (_queuedCharacterCount <= 0)
|
||||||
@@ -90,12 +143,20 @@ public class TextRenderer
|
|||||||
GL.DrawElements(PrimitiveType.Triangles, _queuedCharacterCount * 6, DrawElementsType.UnsignedInt, 0);
|
GL.DrawElements(PrimitiveType.Triangles, _queuedCharacterCount * 6, DrawElementsType.UnsignedInt, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets the text renderer, clearing all queued characters and texture unit mappings.
|
||||||
|
/// </summary>
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
_textureUnitMap.Reset();
|
_textureUnitMap.Reset();
|
||||||
_queuedCharacterCount = 0;
|
_queuedCharacterCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates the indices for rendering the glyphs.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parCharacterCount">The number of characters to render.</param>
|
||||||
|
/// <returns>An array of indices for rendering the glyphs.</returns>
|
||||||
private static uint[] CreateIndices(int parCharacterCount)
|
private static uint[] CreateIndices(int parCharacterCount)
|
||||||
{
|
{
|
||||||
var indices = new uint[parCharacterCount * 6];
|
var indices = new uint[parCharacterCount * 6];
|
||||||
|
|||||||
@@ -7,22 +7,63 @@ using OpenTK.Windowing.Desktop;
|
|||||||
|
|
||||||
namespace Engine.Graphics;
|
namespace Engine.Graphics;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles the rendering pipeline, manages render layers, and provides tools for rendering graphics in the engine.
|
||||||
|
/// </summary>
|
||||||
public class Renderer
|
public class Renderer
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The width of the viewport.
|
||||||
|
/// </summary>
|
||||||
public int ViewportWidth => RenderFramebuffer.Width;
|
public int ViewportWidth => RenderFramebuffer.Width;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The height of the viewport.
|
||||||
|
/// </summary>
|
||||||
public int ViewportHeight => RenderFramebuffer.Height;
|
public int ViewportHeight => RenderFramebuffer.Height;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The framebuffer used for rendering.
|
||||||
|
/// </summary>
|
||||||
internal Framebuffer.Framebuffer RenderFramebuffer { get; }
|
internal Framebuffer.Framebuffer RenderFramebuffer { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The texture of the render framebuffer.
|
||||||
|
/// </summary>
|
||||||
internal Texture.Texture RenderTexture => RenderFramebuffer.TextureInternal!;
|
internal Texture.Texture RenderTexture => RenderFramebuffer.TextureInternal!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The native OpenGL window.
|
||||||
|
/// </summary>
|
||||||
internal NativeWindow NativeWindow { get; }
|
internal NativeWindow NativeWindow { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores renderers for each render layer in a sorted dictionary.
|
||||||
|
/// </summary>
|
||||||
private readonly SortedDictionary<RenderLayer, GenericRenderer> _renderers = new();
|
private readonly SortedDictionary<RenderLayer, GenericRenderer> _renderers = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The thread dedicated to rendering.
|
||||||
|
/// </summary>
|
||||||
private readonly Thread _renderThread;
|
private readonly Thread _renderThread;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Renderer for quad-based primitives.
|
||||||
|
/// </summary>
|
||||||
private QuadRenderer QuadRenderer { get; }
|
private QuadRenderer QuadRenderer { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queue of actions scheduled to run on the render thread.
|
||||||
|
/// </summary>
|
||||||
private readonly Queue<Action> _scheduleActions = new();
|
private readonly Queue<Action> _scheduleActions = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the renderer with the specified engine, dimensions, and native window settings.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parEngine">The engine instance.</param>
|
||||||
|
/// <param name="parWidth">The width of the rendering viewport.</param>
|
||||||
|
/// <param name="parHeight">The height of the rendering viewport.</param>
|
||||||
|
/// <param name="parSettings">Settings for the native window.</param>
|
||||||
public Renderer(Engine parEngine, int parWidth, int parHeight, NativeWindowSettings parSettings)
|
public Renderer(Engine parEngine, int parWidth, int parHeight, NativeWindowSettings parSettings)
|
||||||
{
|
{
|
||||||
_renderThread = Thread.CurrentThread;
|
_renderThread = Thread.CurrentThread;
|
||||||
@@ -48,6 +89,12 @@ public class Renderer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the renderer for the specified render layer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parRenderLayer">The render layer to retrieve.</param>
|
||||||
|
/// <returns>The <see cref="GenericRenderer"/> for the specified render layer.</returns>
|
||||||
|
/// <exception cref="InvalidOperationException">Thrown if the render layer does not exist.</exception>
|
||||||
public GenericRenderer this[RenderLayer parRenderLayer]
|
public GenericRenderer this[RenderLayer parRenderLayer]
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -61,24 +108,11 @@ public class Renderer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeOpenGl(int parWidth, int parHeight)
|
/// <summary>
|
||||||
{
|
/// Ensures that the current thread is the render thread.
|
||||||
#if DEBUG
|
/// </summary>
|
||||||
Debug.Setup();
|
/// <exception cref="InvalidOperationException">Thrown if not on the render thread in debug mode.</exception>
|
||||||
#endif
|
internal void EnsureRenderThread()
|
||||||
|
|
||||||
GL.Enable(EnableCap.DepthTest);
|
|
||||||
// GL.Enable(EnableCap.CullFace);
|
|
||||||
|
|
||||||
// GL.Enable(EnableCap.FramebufferSrgb);
|
|
||||||
|
|
||||||
GL.Enable(EnableCap.Blend);
|
|
||||||
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
|
|
||||||
|
|
||||||
GL.Viewport(0, 0, parWidth, parHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EnsureRenderThread()
|
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
if (Thread.CurrentThread != _renderThread)
|
if (Thread.CurrentThread != _renderThread)
|
||||||
@@ -88,11 +122,21 @@ public class Renderer
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Schedules an action to be executed on the render thread.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parAction">The action to execute.</param>
|
||||||
internal void Schedule(Action parAction)
|
internal void Schedule(Action parAction)
|
||||||
{
|
{
|
||||||
_scheduleActions.Enqueue(parAction);
|
_scheduleActions.Enqueue(parAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Schedules a function to be executed on the render thread and returns a task representing the result.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The return type of the function.</typeparam>
|
||||||
|
/// <param name="parAction">The function to execute.</param>
|
||||||
|
/// <returns>A task representing the function's result.</returns>
|
||||||
internal Task<T> Schedule<T>(Func<T> parAction)
|
internal Task<T> Schedule<T>(Func<T> parAction)
|
||||||
{
|
{
|
||||||
var completionSource = new TaskCompletionSource<T>();
|
var completionSource = new TaskCompletionSource<T>();
|
||||||
@@ -112,6 +156,9 @@ public class Renderer
|
|||||||
return completionSource.Task;
|
return completionSource.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prepares the rendering pipeline for a new frame.
|
||||||
|
/// </summary>
|
||||||
internal void StartFrame()
|
internal void StartFrame()
|
||||||
{
|
{
|
||||||
EnsureRenderThread();
|
EnsureRenderThread();
|
||||||
@@ -122,6 +169,10 @@ public class Renderer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finalizes the rendering pipeline for the current frame.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parMatrices">A dictionary of matrices for render layers.</param>
|
||||||
internal void EndFrame(Dictionary<RenderLayer, (Matrix4, Matrix4)> parMatrices)
|
internal void EndFrame(Dictionary<RenderLayer, (Matrix4, Matrix4)> parMatrices)
|
||||||
{
|
{
|
||||||
EnsureRenderThread();
|
EnsureRenderThread();
|
||||||
@@ -158,6 +209,11 @@ public class Renderer
|
|||||||
RenderFramebuffer.Unbind();
|
RenderFramebuffer.Unbind();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resizes the rendering viewport and updates associated resources.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parWidth">The new width of the viewport.</param>
|
||||||
|
/// <param name="parHeight">The new height of the viewport.</param>
|
||||||
internal void Resize(int parWidth, int parHeight)
|
internal void Resize(int parWidth, int parHeight)
|
||||||
{
|
{
|
||||||
RenderFramebuffer.Resize(parWidth, parHeight);
|
RenderFramebuffer.Resize(parWidth, parHeight);
|
||||||
@@ -169,6 +225,9 @@ public class Renderer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes all scheduled actions on the render thread.
|
||||||
|
/// </summary>
|
||||||
internal void RunScheduledActions()
|
internal void RunScheduledActions()
|
||||||
{
|
{
|
||||||
while (_scheduleActions.TryDequeue(out var action))
|
while (_scheduleActions.TryDequeue(out var action))
|
||||||
@@ -176,4 +235,26 @@ public class Renderer
|
|||||||
action();
|
action();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes OpenGL settings for rendering.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parWidth">The width of the viewport.</param>
|
||||||
|
/// <param name="parHeight">The height of the viewport.</param>
|
||||||
|
private void InitializeOpenGl(int parWidth, int parHeight)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
Debug.Setup();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
GL.Enable(EnableCap.DepthTest);
|
||||||
|
// GL.Enable(EnableCap.CullFace);
|
||||||
|
|
||||||
|
// GL.Enable(EnableCap.FramebufferSrgb);
|
||||||
|
|
||||||
|
GL.Enable(EnableCap.Blend);
|
||||||
|
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
|
||||||
|
|
||||||
|
GL.Viewport(0, 0, parWidth, parHeight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,10 +4,22 @@ using Serilog;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Shader;
|
namespace Engine.Graphics.Shader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an OpenGL shader program that can be used for rendering.
|
||||||
|
/// The class handles the compilation, linking, and management of shaders, as well as setting uniform values.
|
||||||
|
/// </summary>
|
||||||
public class Program : OpenGlObject
|
public class Program : OpenGlObject
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A dictionary mapping uniform names to their locations in the shader program.
|
||||||
|
/// </summary>
|
||||||
private readonly Dictionary<string, int> _uniforms = new();
|
private readonly Dictionary<string, int> _uniforms = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Program"/> class with the provided vertex and fragment shader sources.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parVertexSource">The source code for the vertex shader.</param>
|
||||||
|
/// <param name="parFragmentSource">The source code for the fragment shader.</param>
|
||||||
public Program(string parVertexSource, string parFragmentSource)
|
public Program(string parVertexSource, string parFragmentSource)
|
||||||
{
|
{
|
||||||
var vertexShader = CompileSource(parVertexSource, ShaderType.VertexShader);
|
var vertexShader = CompileSource(parVertexSource, ShaderType.VertexShader);
|
||||||
@@ -16,6 +28,13 @@ public class Program : OpenGlObject
|
|||||||
Handle = LinkProgram(vertexShader, fragmentShader);
|
Handle = LinkProgram(vertexShader, fragmentShader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets a uniform variable in the shader program with the given value.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the uniform value.</typeparam>
|
||||||
|
/// <param name="parName">The name of the uniform variable in the shader.</param>
|
||||||
|
/// <param name="parValue">The value to set for the uniform.</param>
|
||||||
|
/// <exception cref="ArgumentException">Thrown when an unsupported uniform type is provided.</exception>
|
||||||
public void SetUniform<T>(string parName, in T parValue)
|
public void SetUniform<T>(string parName, in T parValue)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -62,21 +81,31 @@ public class Program : OpenGlObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
internal override void Bind()
|
internal override void Bind()
|
||||||
{
|
{
|
||||||
GL.UseProgram(Handle);
|
GL.UseProgram(Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
internal override void Unbind()
|
internal override void Unbind()
|
||||||
{
|
{
|
||||||
GL.UseProgram(0);
|
GL.UseProgram(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
protected override void Destroy()
|
protected override void Destroy()
|
||||||
{
|
{
|
||||||
GL.DeleteProgram(Handle);
|
GL.DeleteProgram(Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the location of a uniform variable in the shader program.
|
||||||
|
/// If the location is not cached, it will be retrieved from OpenGL.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parName">The name of the uniform variable.</param>
|
||||||
|
/// <returns>The location of the uniform variable.</returns>
|
||||||
|
/// <exception cref="ArgumentException">Thrown when the uniform is not found in the shader program.</exception>
|
||||||
private int GetUniformLocation(string parName)
|
private int GetUniformLocation(string parName)
|
||||||
{
|
{
|
||||||
if (_uniforms.TryGetValue(parName, out var location))
|
if (_uniforms.TryGetValue(parName, out var location))
|
||||||
@@ -94,6 +123,13 @@ public class Program : OpenGlObject
|
|||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compiles a shader from the given source code and shader type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parSource">The source code of the shader.</param>
|
||||||
|
/// <param name="parType">The type of shader (vertex or fragment).</param>
|
||||||
|
/// <returns>The compiled shader handle.</returns>
|
||||||
|
/// <exception cref="ShaderCompilationException">Thrown when the shader fails to compile.</exception>
|
||||||
private static int CompileSource(string parSource, ShaderType parType)
|
private static int CompileSource(string parSource, ShaderType parType)
|
||||||
{
|
{
|
||||||
var shaderId = GL.CreateShader(parType);
|
var shaderId = GL.CreateShader(parType);
|
||||||
@@ -112,6 +148,13 @@ public class Program : OpenGlObject
|
|||||||
return shaderId;
|
return shaderId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Links a vertex and fragment shader into a single shader program.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parVertexShader">The compiled vertex shader handle.</param>
|
||||||
|
/// <param name="parFragmentShader">The compiled fragment shader handle.</param>
|
||||||
|
/// <returns>The linked shader program handle.</returns>
|
||||||
|
/// <exception cref="ShaderLinkException">Thrown when the shader program fails to link.</exception>
|
||||||
private static int LinkProgram(int parVertexShader, int parFragmentShader)
|
private static int LinkProgram(int parVertexShader, int parFragmentShader)
|
||||||
{
|
{
|
||||||
var programId = GL.CreateProgram();
|
var programId = GL.CreateProgram();
|
||||||
@@ -152,13 +195,52 @@ public class Program : OpenGlObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ShaderCompilationException(ShaderType parType, string parMessage)
|
/// <summary>
|
||||||
: Exception($"Failed to compile {parType} shader: {parMessage}")
|
/// Exception thrown when a shader fails to compile.
|
||||||
|
/// </summary>
|
||||||
|
public class ShaderCompilationException : Exception
|
||||||
{
|
{
|
||||||
public ShaderType ShaderType { get; } = parType;
|
/// <summary>
|
||||||
|
/// The type of the shader that failed to compile.
|
||||||
|
/// </summary>
|
||||||
|
public ShaderType ShaderType { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ShaderCompilationException"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parType">The type of shader that failed to compile.</param>
|
||||||
|
/// <param name="parMessage">The error message from the shader compilation failure.</param>
|
||||||
|
public ShaderCompilationException(ShaderType parType, string parMessage) : base(
|
||||||
|
$"Failed to compile {parType} shader: {parMessage}")
|
||||||
|
{
|
||||||
|
ShaderType = parType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ShaderLinkException(string parMessage) : Exception($"Failed to link shader program: {parMessage}");
|
/// <summary>
|
||||||
|
/// Exception thrown when a shader program fails to link.
|
||||||
|
/// </summary>
|
||||||
|
public class ShaderLinkException : Exception
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ShaderLinkException"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parMessage">The error message from the shader linking failure.</param>
|
||||||
|
public ShaderLinkException(string parMessage) : base($"Failed to link shader program: {parMessage}")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class ShaderValidationException(string parMessage)
|
/// <summary>
|
||||||
: Exception($"Failed to validate shader program: {parMessage}");
|
/// Exception thrown when a shader program fails validation.
|
||||||
|
/// </summary>
|
||||||
|
public class ShaderValidationException : Exception
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ShaderValidationException"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parMessage">The error message from the shader validation failure.</param>
|
||||||
|
public ShaderValidationException(string parMessage) : base($"Failed to validate shader program: {parMessage}")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,12 +5,35 @@ using Serilog;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Texture;
|
namespace Engine.Graphics.Texture;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a dynamic texture that can be resized and attached to a framebuffer.
|
||||||
|
/// </summary>
|
||||||
public class DynamicTexture : Texture, IFramebufferAttachment
|
public class DynamicTexture : Texture, IFramebufferAttachment
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The pixel format of the texture.
|
||||||
|
/// </summary>
|
||||||
private readonly PixelFormat _format;
|
private readonly PixelFormat _format;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The pixel type of the texture.
|
||||||
|
/// </summary>
|
||||||
private readonly PixelType _type;
|
private readonly PixelType _type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The internal format of the texture.
|
||||||
|
/// </summary>
|
||||||
private readonly PixelInternalFormat _internalFormat;
|
private readonly PixelInternalFormat _internalFormat;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DynamicTexture"/> class.
|
||||||
|
/// The texture is created with the specified format, type, and internal format.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parWidth">The width of the texture.</param>
|
||||||
|
/// <param name="parHeight">The height of the texture.</param>
|
||||||
|
/// <param name="parFormat">The format of the texture.</param>
|
||||||
|
/// <param name="parType">The type of the texture.</param>
|
||||||
|
/// <param name="parInternalFormat">The internal format of the texture.</param>
|
||||||
internal DynamicTexture(int parWidth, int parHeight, PixelFormat parFormat, PixelType parType,
|
internal DynamicTexture(int parWidth, int parHeight, PixelFormat parFormat, PixelType parType,
|
||||||
PixelInternalFormat parInternalFormat)
|
PixelInternalFormat parInternalFormat)
|
||||||
: base(parWidth, parHeight)
|
: base(parWidth, parHeight)
|
||||||
@@ -24,6 +47,13 @@ public class DynamicTexture : Texture, IFramebufferAttachment
|
|||||||
IntPtr.Zero);
|
IntPtr.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="DynamicTexture"/> with the specified width, height, and pixel type.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of pixel used for the texture.</typeparam>
|
||||||
|
/// <param name="parWidth">The width of the texture.</param>
|
||||||
|
/// <param name="parHeight">The height of the texture.</param>
|
||||||
|
/// <returns>A new instance of <see cref="DynamicTexture"/>.</returns>
|
||||||
public static DynamicTexture Create<T>(int parWidth, int parHeight)
|
public static DynamicTexture Create<T>(int parWidth, int parHeight)
|
||||||
where T : struct, IPixel
|
where T : struct, IPixel
|
||||||
{
|
{
|
||||||
@@ -31,6 +61,11 @@ public class DynamicTexture : Texture, IFramebufferAttachment
|
|||||||
return new DynamicTexture(parWidth, parHeight, pixel.Format, pixel.Type, pixel.InternalFormat);
|
return new DynamicTexture(parWidth, parHeight, pixel.Format, pixel.Type, pixel.InternalFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resizes the texture to the specified width and height.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parWidth">The new width of the texture.</param>
|
||||||
|
/// <param name="parHeight">The new height of the texture.</param>
|
||||||
public void Resize(int parWidth, int parHeight)
|
public void Resize(int parWidth, int parHeight)
|
||||||
{
|
{
|
||||||
if (Width == parWidth && Height == parHeight)
|
if (Width == parWidth && Height == parHeight)
|
||||||
@@ -48,6 +83,7 @@ public class DynamicTexture : Texture, IFramebufferAttachment
|
|||||||
Log.Debug("Texture {Handle} resized to {Width}x{Height}", Handle, Width, Height);
|
Log.Debug("Texture {Handle} resized to {Width}x{Height}", Handle, Width, Height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void Attach(Framebuffer.Framebuffer parFramebuffer, FramebufferAttachment parAttachment)
|
public void Attach(Framebuffer.Framebuffer parFramebuffer, FramebufferAttachment parAttachment)
|
||||||
{
|
{
|
||||||
GL.NamedFramebufferTexture(parFramebuffer.Handle, parAttachment, Handle, 0);
|
GL.NamedFramebufferTexture(parFramebuffer.Handle, parAttachment, Handle, 0);
|
||||||
|
|||||||
@@ -3,22 +3,60 @@ using Engine.Graphics.Pixel;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Texture;
|
namespace Engine.Graphics.Texture;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a read-only texture with dimensions and pixel access capabilities.
|
||||||
|
/// </summary>
|
||||||
public interface IConstTexture
|
public interface IConstTexture
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The width of the texture.
|
||||||
|
/// </summary>
|
||||||
public int Width { get; }
|
public int Width { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The height of the texture.
|
||||||
|
/// </summary>
|
||||||
public int Height { get; }
|
public int Height { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads pixels from the texture into the provided pixel array.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The pixel type, which must implement <see cref="IPixel"/>.</typeparam>
|
||||||
|
/// <param name="parX">The x-coordinate of the region to read.</param>
|
||||||
|
/// <param name="parY">The y-coordinate of the region to read.</param>
|
||||||
|
/// <param name="parWidth">The width of the region to read.</param>
|
||||||
|
/// <param name="parHeight">The height of the region to read.</param>
|
||||||
|
/// <param name="parPixels">The array to store the read pixels.</param>
|
||||||
public void ReadPixels<T>(int parX, int parY, int parWidth, int parHeight, T[,] parPixels)
|
public void ReadPixels<T>(int parX, int parY, int parWidth, int parHeight, T[,] parPixels)
|
||||||
where T : struct, IPixel;
|
where T : struct, IPixel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for <see cref="IConstTexture"/>.
|
||||||
|
/// </summary>
|
||||||
public static class ConstTextureExtensions
|
public static class ConstTextureExtensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Reads all pixels from the texture.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The pixel type, which must implement <see cref="IPixel"/>.</typeparam>
|
||||||
|
/// <param name="parTexture">The texture to read from.</param>
|
||||||
|
/// <returns>A 2D array containing the pixels.</returns>
|
||||||
public static T[,] ReadPixels<T>(this IConstTexture parTexture) where T : struct, IPixel
|
public static T[,] ReadPixels<T>(this IConstTexture parTexture) where T : struct, IPixel
|
||||||
{
|
{
|
||||||
return parTexture.ReadPixels<T>(0, 0, parTexture.Width, parTexture.Height);
|
return parTexture.ReadPixels<T>(0, 0, parTexture.Width, parTexture.Height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a specified region of pixels from the texture.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The pixel type, which must implement <see cref="IPixel"/>.</typeparam>
|
||||||
|
/// <param name="parTexture">The texture to read from.</param>
|
||||||
|
/// <param name="parX">The x-coordinate of the region to read.</param>
|
||||||
|
/// <param name="parY">The y-coordinate of the region to read.</param>
|
||||||
|
/// <param name="parWidth">The width of the region to read.</param>
|
||||||
|
/// <param name="parHeight">The height of the region to read.</param>
|
||||||
|
/// <returns>A 2D array containing the pixels.</returns>
|
||||||
public static T[,] ReadPixels<T>(this IConstTexture parTexture, int parX, int parY, int parWidth, int parHeight)
|
public static T[,] ReadPixels<T>(this IConstTexture parTexture, int parX, int parY, int parWidth, int parHeight)
|
||||||
where T : struct, IPixel
|
where T : struct, IPixel
|
||||||
{
|
{
|
||||||
@@ -28,17 +66,37 @@ public static class ConstTextureExtensions
|
|||||||
return pixels;
|
return pixels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads all pixels from the texture into the specified image.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The pixel type, which must implement <see cref="IPixel"/>.</typeparam>
|
||||||
|
/// <param name="parTexture">The texture to read from.</param>
|
||||||
|
/// <param name="parImage">The image to store the pixels.</param>
|
||||||
public static void ReadPixels<T>(this IConstTexture parTexture, Image<T> parImage) where T : struct, IPixel
|
public static void ReadPixels<T>(this IConstTexture parTexture, Image<T> parImage) where T : struct, IPixel
|
||||||
{
|
{
|
||||||
parTexture.ReadPixels(0, 0, parImage);
|
parTexture.ReadPixels(0, 0, parImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a specified region of pixels from the texture into the specified image.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The pixel type, which must implement <see cref="IPixel"/>.</typeparam>
|
||||||
|
/// <param name="parTexture">The texture to read from.</param>
|
||||||
|
/// <param name="parX">The x-coordinate of the region to read.</param>
|
||||||
|
/// <param name="parY">The y-coordinate of the region to read.</param>
|
||||||
|
/// <param name="parImage">The image to store the pixels.</param>
|
||||||
public static void ReadPixels<T>(this IConstTexture parTexture, int parX, int parY, Image<T> parImage)
|
public static void ReadPixels<T>(this IConstTexture parTexture, int parX, int parY, Image<T> parImage)
|
||||||
where T : struct, IPixel
|
where T : struct, IPixel
|
||||||
{
|
{
|
||||||
parTexture.ReadPixels(parX, parY, parImage.Width, parImage.Height, parImage.Pixels);
|
parTexture.ReadPixels(parX, parY, parImage.Width, parImage.Height, parImage.Pixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads all pixels from the texture into the specified 2D array.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The pixel type, which must implement <see cref="IPixel"/>.</typeparam>
|
||||||
|
/// <param name="parTexture">The texture to read from.</param>
|
||||||
|
/// <param name="parPixels">The array to store the pixels.</param>
|
||||||
public static void ReadPixels<T>(this IConstTexture parTexture, T[,] parPixels) where T : struct, IPixel
|
public static void ReadPixels<T>(this IConstTexture parTexture, T[,] parPixels) where T : struct, IPixel
|
||||||
{
|
{
|
||||||
parTexture.ReadPixels(0, 0, parTexture.Width, parTexture.Height, parPixels);
|
parTexture.ReadPixels(0, 0, parTexture.Width, parTexture.Height, parPixels);
|
||||||
|
|||||||
@@ -3,25 +3,60 @@ using Engine.Graphics.Pixel;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Texture;
|
namespace Engine.Graphics.Texture;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a writable texture with dimensions and pixel access capabilities.
|
||||||
|
/// </summary>
|
||||||
public interface ITexture : IConstTexture
|
public interface ITexture : IConstTexture
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Uploads pixels to the texture.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The pixel type, which must implement <see cref="IPixel"/>.</typeparam>
|
||||||
|
/// <param name="parX">The x-coordinate of the region to write.</param>
|
||||||
|
/// <param name="parY">The y-coordinate of the region to write.</param>
|
||||||
|
/// <param name="parWidth">The width of the region to write.</param>
|
||||||
|
/// <param name="parHeight">The height of the region to write.</param>
|
||||||
|
/// <param name="parPixels">The pixel data to upload.</param>
|
||||||
public void UploadPixels<T>(int parX, int parY, int parWidth, int parHeight, T[,] parPixels)
|
public void UploadPixels<T>(int parX, int parY, int parWidth, int parHeight, T[,] parPixels)
|
||||||
where T : struct, IPixel;
|
where T : struct, IPixel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for <see cref="ITexture"/>.
|
||||||
|
/// </summary>
|
||||||
public static class TextureExtensions
|
public static class TextureExtensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Uploads the entire image to the texture.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The pixel type, which must implement <see cref="IPixel"/>.</typeparam>
|
||||||
|
/// <param name="parTexture">The texture to upload to.</param>
|
||||||
|
/// <param name="parImage">The image containing the pixel data.</param>
|
||||||
public static void UploadPixels<T>(this ITexture parTexture, Image<T> parImage) where T : struct, IPixel
|
public static void UploadPixels<T>(this ITexture parTexture, Image<T> parImage) where T : struct, IPixel
|
||||||
{
|
{
|
||||||
parTexture.UploadPixels(0, 0, parImage);
|
parTexture.UploadPixels(0, 0, parImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uploads a specified region of the image to the texture.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The pixel type, which must implement <see cref="IPixel"/>.</typeparam>
|
||||||
|
/// <param name="parTexture">The texture to upload to.</param>
|
||||||
|
/// <param name="parX">The x-coordinate of the region in the image.</param>
|
||||||
|
/// <param name="parY">The y-coordinate of the region in the image.</param>
|
||||||
|
/// <param name="parImage">The image containing the pixel data.</param>
|
||||||
public static void UploadPixels<T>(this ITexture parTexture, int parX, int parY, Image<T> parImage)
|
public static void UploadPixels<T>(this ITexture parTexture, int parX, int parY, Image<T> parImage)
|
||||||
where T : struct, IPixel
|
where T : struct, IPixel
|
||||||
{
|
{
|
||||||
parTexture.UploadPixels(parX, parY, parImage.Width, parImage.Height, parImage.Pixels);
|
parTexture.UploadPixels(parX, parY, parImage.Width, parImage.Height, parImage.Pixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Uploads the entire 2D pixel array to the texture.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The pixel type, which must implement <see cref="IPixel"/>.</typeparam>
|
||||||
|
/// <param name="parTexture">The texture to upload to.</param>
|
||||||
|
/// <param name="parPixels">The pixel data to upload.</param>
|
||||||
public static void UploadPixels<T>(this ITexture parTexture, T[,] parPixels) where T : struct, IPixel
|
public static void UploadPixels<T>(this ITexture parTexture, T[,] parPixels) where T : struct, IPixel
|
||||||
{
|
{
|
||||||
parTexture.UploadPixels(0, 0, parTexture.Width, parTexture.Height, parPixels);
|
parTexture.UploadPixels(0, 0, parTexture.Width, parTexture.Height, parPixels);
|
||||||
|
|||||||
@@ -3,13 +3,30 @@ using OpenTK.Graphics.OpenGL;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Texture;
|
namespace Engine.Graphics.Texture;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a static texture in OpenGL, created with fixed storage and format.
|
||||||
|
/// </summary>
|
||||||
public class StaticTexture : Texture
|
public class StaticTexture : Texture
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="StaticTexture"/> class.
|
||||||
|
/// The texture is created with the specified width, height, and internal format.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parWidth">The width of the texture.</param>
|
||||||
|
/// <param name="parHeight">The height of the texture.</param>
|
||||||
|
/// <param name="parFormat">The internal format of the texture.</param>
|
||||||
private StaticTexture(int parWidth, int parHeight, SizedInternalFormat parFormat) : base(parWidth, parHeight)
|
private StaticTexture(int parWidth, int parHeight, SizedInternalFormat parFormat) : base(parWidth, parHeight)
|
||||||
{
|
{
|
||||||
GL.TextureStorage2D(Handle, 1, parFormat, Width, Height);
|
GL.TextureStorage2D(Handle, 1, parFormat, Width, Height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="StaticTexture"/> with the specified width and height.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of pixel format used for the texture.</typeparam>
|
||||||
|
/// <param name="parWidth">The width of the texture.</param>
|
||||||
|
/// <param name="parHeight">The height of the texture.</param>
|
||||||
|
/// <returns>A new instance of <see cref="StaticTexture"/>.</returns>
|
||||||
public static StaticTexture Create<T>(int parWidth, int parHeight)
|
public static StaticTexture Create<T>(int parWidth, int parHeight)
|
||||||
where T : struct, IPixel
|
where T : struct, IPixel
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,8 +5,13 @@ using Serilog;
|
|||||||
|
|
||||||
namespace Engine.Graphics.Texture;
|
namespace Engine.Graphics.Texture;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an abstract texture object used in OpenGL for 2D textures.
|
||||||
|
/// Handles the creation, binding, and destruction of textures and provides functionality for uploading and reading pixel data.
|
||||||
|
/// </summary>
|
||||||
public abstract class Texture : OpenGlObject, ITexture
|
public abstract class Texture : OpenGlObject, ITexture
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
public int Width
|
public int Width
|
||||||
{
|
{
|
||||||
get => _width;
|
get => _width;
|
||||||
@@ -21,6 +26,7 @@ public abstract class Texture : OpenGlObject, ITexture
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public int Height
|
public int Height
|
||||||
{
|
{
|
||||||
get => _height;
|
get => _height;
|
||||||
@@ -35,9 +41,22 @@ public abstract class Texture : OpenGlObject, ITexture
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The width of the texture.
|
||||||
|
/// </summary>
|
||||||
private int _width;
|
private int _width;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The height of the texture.
|
||||||
|
/// </summary>
|
||||||
private int _height;
|
private int _height;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Texture"/> class with the specified width and height.
|
||||||
|
/// Creates an OpenGL texture handle and sets default texture parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parWidth">The width of the texture.</param>
|
||||||
|
/// <param name="parHeight">The height of the texture.</param>
|
||||||
protected Texture(int parWidth, int parHeight)
|
protected Texture(int parWidth, int parHeight)
|
||||||
{
|
{
|
||||||
Width = parWidth;
|
Width = parWidth;
|
||||||
@@ -54,6 +73,7 @@ public abstract class Texture : OpenGlObject, ITexture
|
|||||||
Log.Debug("Texture {Handle} created with {Width}x{Height}", Handle, Width, Height);
|
Log.Debug("Texture {Handle} created with {Width}x{Height}", Handle, Width, Height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void UploadPixels<T>(int parX, int parY, int parWidth, int parHeight, T[,] parPixels)
|
public void UploadPixels<T>(int parX, int parY, int parWidth, int parHeight, T[,] parPixels)
|
||||||
where T : struct, IPixel
|
where T : struct, IPixel
|
||||||
{
|
{
|
||||||
@@ -83,6 +103,7 @@ public abstract class Texture : OpenGlObject, ITexture
|
|||||||
GL.TextureSubImage2D(Handle, 0, parX, parY, parWidth, parHeight, format, type, parPixels);
|
GL.TextureSubImage2D(Handle, 0, parX, parY, parWidth, parHeight, format, type, parPixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void ReadPixels<T>(int parX, int parY, int parWidth, int parHeight, T[,] parPixels)
|
public void ReadPixels<T>(int parX, int parY, int parWidth, int parHeight, T[,] parPixels)
|
||||||
where T : struct, IPixel
|
where T : struct, IPixel
|
||||||
{
|
{
|
||||||
@@ -118,21 +139,28 @@ public abstract class Texture : OpenGlObject, ITexture
|
|||||||
parPixels);
|
parPixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Binds the texture to a texture unit.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parUnit">The texture unit to which the texture should be bound.</param>
|
||||||
public void BindUnit(int parUnit = 0)
|
public void BindUnit(int parUnit = 0)
|
||||||
{
|
{
|
||||||
GL.BindTextureUnit(parUnit, Handle);
|
GL.BindTextureUnit(parUnit, Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
internal override void Bind()
|
internal override void Bind()
|
||||||
{
|
{
|
||||||
GL.BindTexture(TextureTarget.Texture2D, Handle);
|
GL.BindTexture(TextureTarget.Texture2D, Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
internal override void Unbind()
|
internal override void Unbind()
|
||||||
{
|
{
|
||||||
GL.BindTexture(TextureTarget.Texture2D, 0);
|
GL.BindTexture(TextureTarget.Texture2D, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
protected override void Destroy()
|
protected override void Destroy()
|
||||||
{
|
{
|
||||||
GL.DeleteTexture(Handle);
|
GL.DeleteTexture(Handle);
|
||||||
|
|||||||
@@ -1,13 +1,48 @@
|
|||||||
namespace Engine.Graphics.Texture;
|
namespace Engine.Graphics.Texture;
|
||||||
|
|
||||||
public class TextureUnitMap(int parCapacity)
|
/// <summary>
|
||||||
|
/// Manages the mapping of textures to texture units.
|
||||||
|
/// Allows tracking of textures and their associated texture units within a given capacity.
|
||||||
|
/// </summary>
|
||||||
|
public class TextureUnitMap
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum number of textures that can be mapped.
|
||||||
|
/// </summary>
|
||||||
|
public int Capacity { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current number of textures in the unit map.
|
||||||
|
/// </summary>
|
||||||
public int Size => _textures.Count;
|
public int Size => _textures.Count;
|
||||||
public int Capacity => parCapacity;
|
|
||||||
|
/// <summary>
|
||||||
|
/// A read-only dictionary that holds the textures and their associated unit numbers.
|
||||||
|
/// </summary>
|
||||||
public IReadOnlyDictionary<Texture, int> Textures => _textures;
|
public IReadOnlyDictionary<Texture, int> Textures => _textures;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dictionary that stores the textures and their corresponding texture units.
|
||||||
|
/// </summary>
|
||||||
private readonly Dictionary<Texture, int> _textures = new();
|
private readonly Dictionary<Texture, int> _textures = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="TextureUnitMap"/> class with the specified capacity.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parCapacity">The maximum number of textures that can be mapped in this unit map.</param>
|
||||||
|
public TextureUnitMap(int parCapacity)
|
||||||
|
{
|
||||||
|
Capacity = parCapacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the texture unit assigned to the specified texture.
|
||||||
|
/// If the texture is not already mapped, a new unit is assigned.
|
||||||
|
/// Throws an exception if the map has reached its capacity.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parTexture">The texture for which to retrieve the assigned unit.</param>
|
||||||
|
/// <returns>The texture unit assigned to the texture.</returns>
|
||||||
|
/// <exception cref="InvalidOperationException">Thrown when the map has reached its capacity.</exception>
|
||||||
public int GetUnit(Texture parTexture)
|
public int GetUnit(Texture parTexture)
|
||||||
{
|
{
|
||||||
if (_textures.TryGetValue(parTexture, out var unit))
|
if (_textures.TryGetValue(parTexture, out var unit))
|
||||||
@@ -15,7 +50,7 @@ public class TextureUnitMap(int parCapacity)
|
|||||||
return unit;
|
return unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_textures.Count >= parCapacity)
|
if (_textures.Count >= Capacity)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Texture unit map is full");
|
throw new InvalidOperationException("Texture unit map is full");
|
||||||
}
|
}
|
||||||
@@ -26,6 +61,9 @@ public class TextureUnitMap(int parCapacity)
|
|||||||
return unit;
|
return unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears all textures from the unit map, effectively resetting it.
|
||||||
|
/// </summary>
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
_textures.Clear();
|
_textures.Clear();
|
||||||
|
|||||||
@@ -4,15 +4,46 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Input;
|
namespace Engine.Input;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an input handler interface that processes keyboard and mouse input.
|
||||||
|
/// </summary>
|
||||||
public interface IInputHandler : IUpdate
|
public interface IInputHandler : IUpdate
|
||||||
{
|
{
|
||||||
CultureInfo CurrentInputLanguage { get; }
|
/// <summary>
|
||||||
|
/// The current input language.
|
||||||
|
/// </summary>
|
||||||
|
public CultureInfo CurrentInputLanguage { get; }
|
||||||
|
|
||||||
Vector2 MousePosition { get; }
|
/// <summary>
|
||||||
|
/// The current mouse position in screen coordinates.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 MousePosition { get; }
|
||||||
|
|
||||||
bool IsKeyPressed(KeyboardButtonCode parKeyboardButtonCode);
|
/// <summary>
|
||||||
bool IsKeyJustPressed(KeyboardButtonCode parKeyboardButtonCode);
|
/// Checks if a specific keyboard button is currently pressed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parKeyboardButtonCode">The keyboard button code to check.</param>
|
||||||
|
/// <returns>True if the button is pressed; otherwise, false.</returns>
|
||||||
|
public bool IsKeyPressed(KeyboardButtonCode parKeyboardButtonCode);
|
||||||
|
|
||||||
bool IsMouseButtonPressed(MouseButtonCode parButtonCode);
|
/// <summary>
|
||||||
bool IsMouseButtonJustPressed(MouseButtonCode parButtonCode);
|
/// Checks if a specific keyboard button was just pressed in the current frame.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parKeyboardButtonCode">The keyboard button code to check.</param>
|
||||||
|
/// <returns>True if the button was just pressed; otherwise, false.</returns>
|
||||||
|
public bool IsKeyJustPressed(KeyboardButtonCode parKeyboardButtonCode);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a specific mouse button is currently pressed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parButtonCode">The mouse button code to check.</param>
|
||||||
|
/// <returns>True if the button is pressed; otherwise, false.</returns>
|
||||||
|
public bool IsMouseButtonPressed(MouseButtonCode parButtonCode);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a specific mouse button was just pressed in the current frame.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parButtonCode">The mouse button code to check.</param>
|
||||||
|
/// <returns>True if the button was just pressed; otherwise, false.</returns>
|
||||||
|
public bool IsMouseButtonJustPressed(MouseButtonCode parButtonCode);
|
||||||
}
|
}
|
||||||
@@ -61,22 +61,42 @@ public enum KeyboardButtonCode
|
|||||||
PageUp,
|
PageUp,
|
||||||
PageDown,
|
PageDown,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the total count of keyboard button codes.
|
||||||
|
/// </summary>
|
||||||
TotalCount = PageDown + 1
|
TotalCount = PageDown + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides helper methods for working with keyboard button codes.
|
||||||
|
/// </summary>
|
||||||
public static class KeyboardButtonCodeHelper
|
public static class KeyboardButtonCodeHelper
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of all printable keyboard button codes.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A list of printable keyboard button codes.</returns>
|
||||||
public static List<KeyboardButtonCode> GetAllPrintableKeys()
|
public static List<KeyboardButtonCode> GetAllPrintableKeys()
|
||||||
{
|
{
|
||||||
return Enum.GetValues<KeyboardButtonCode>().Where(parX => parX.IsPrintableKey()).ToList();
|
return Enum.GetValues<KeyboardButtonCode>().Where(parX => parX.IsPrintableKey()).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether a keyboard button code is printable.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parKey">The keyboard button code to check.</param>
|
||||||
|
/// <returns>True if the key is printable; otherwise, false.</returns>
|
||||||
public static bool IsPrintableKey(this KeyboardButtonCode parKey)
|
public static bool IsPrintableKey(this KeyboardButtonCode parKey)
|
||||||
{
|
{
|
||||||
return parKey is >= KeyboardButtonCode.A and <= KeyboardButtonCode.Z
|
return parKey is >= KeyboardButtonCode.A and <= KeyboardButtonCode.Z
|
||||||
or >= KeyboardButtonCode.D1 and <= KeyboardButtonCode.D0 or KeyboardButtonCode.Space;
|
or >= KeyboardButtonCode.D1 and <= KeyboardButtonCode.D0 or KeyboardButtonCode.Space;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the character representation of a keyboard button code based on the current input language.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parKey">The keyboard button code to convert.</param>
|
||||||
|
/// <returns>The character representation of the key.</returns>
|
||||||
public static char GetChar(this KeyboardButtonCode parKey)
|
public static char GetChar(this KeyboardButtonCode parKey)
|
||||||
{
|
{
|
||||||
return Engine.Instance.InputHandler!.CurrentInputLanguage.Name switch
|
return Engine.Instance.InputHandler!.CurrentInputLanguage.Name switch
|
||||||
@@ -87,6 +107,11 @@ public static class KeyboardButtonCodeHelper
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the character representation of a keyboard button code in English.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parKey">The keyboard button code to convert.</param>
|
||||||
|
/// <returns>The character representation of the key.</returns>
|
||||||
private static char GetEnChar(this KeyboardButtonCode parKey)
|
private static char GetEnChar(this KeyboardButtonCode parKey)
|
||||||
{
|
{
|
||||||
return parKey switch
|
return parKey switch
|
||||||
@@ -134,6 +159,11 @@ public static class KeyboardButtonCodeHelper
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the character representation of a keyboard button code in Russian.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parKey">The keyboard button code to convert.</param>
|
||||||
|
/// <returns>The character representation of the key.</returns>
|
||||||
private static char GetRuChar(this KeyboardButtonCode parKey)
|
private static char GetRuChar(this KeyboardButtonCode parKey)
|
||||||
{
|
{
|
||||||
return parKey switch
|
return parKey switch
|
||||||
|
|||||||
@@ -1,10 +1,27 @@
|
|||||||
namespace Engine.Input;
|
namespace Engine.Input;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the different mouse button codes.
|
||||||
|
/// </summary>
|
||||||
public enum MouseButtonCode
|
public enum MouseButtonCode
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the left mouse button.
|
||||||
|
/// </summary>
|
||||||
Left,
|
Left,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the right mouse button.
|
||||||
|
/// </summary>
|
||||||
Right,
|
Right,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the middle mouse button.
|
||||||
|
/// </summary>
|
||||||
Middle,
|
Middle,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the total count of mouse button codes.
|
||||||
|
/// </summary>
|
||||||
TotalCount = Middle + 1
|
TotalCount = Middle + 1
|
||||||
}
|
}
|
||||||
@@ -4,47 +4,98 @@ using OpenTK.Windowing.GraphicsLibraryFramework;
|
|||||||
|
|
||||||
namespace Engine.Input;
|
namespace Engine.Input;
|
||||||
|
|
||||||
public class WindowInputHandler(Window parWindow) : IInputHandler
|
/// <summary>
|
||||||
|
/// Handles input for a window, processing keyboard and mouse input states.
|
||||||
|
/// </summary>
|
||||||
|
public class WindowInputHandler : IInputHandler
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
public CultureInfo CurrentInputLanguage => new(1033);
|
public CultureInfo CurrentInputLanguage => new(1033);
|
||||||
public Vector2 MousePosition => parWindow.NativeWindow.MouseState.Position;
|
|
||||||
|
|
||||||
private KeyboardState _keyboardState = parWindow.NativeWindow.KeyboardState.GetSnapshot();
|
/// <inheritdoc/>
|
||||||
private KeyboardState _previousKeyboardState = parWindow.NativeWindow.KeyboardState.GetSnapshot();
|
public Vector2 MousePosition => _window.NativeWindow.MouseState.Position;
|
||||||
private MouseState _mouseState = parWindow.NativeWindow.MouseState.GetSnapshot();
|
|
||||||
private MouseState _previousMouseState = parWindow.NativeWindow.MouseState.GetSnapshot();
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The window to capture input from.
|
||||||
|
/// </summary>
|
||||||
|
private readonly Window _window;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Keyboard state in the current frame.
|
||||||
|
/// </summary>
|
||||||
|
private KeyboardState _keyboardState;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Keyboard state in the previous frame.
|
||||||
|
/// </summary>
|
||||||
|
private KeyboardState _previousKeyboardState;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Mouse state in the current frame.
|
||||||
|
/// </summary>
|
||||||
|
private MouseState _mouseState;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Mouse state in the previous frame.
|
||||||
|
/// </summary>
|
||||||
|
private MouseState _previousMouseState;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="WindowInputHandler"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parWindow">The window to capture input from.</param>
|
||||||
|
public WindowInputHandler(Window parWindow)
|
||||||
|
{
|
||||||
|
_window = parWindow;
|
||||||
|
|
||||||
|
_keyboardState = parWindow.NativeWindow.KeyboardState.GetSnapshot();
|
||||||
|
_previousKeyboardState = parWindow.NativeWindow.KeyboardState.GetSnapshot();
|
||||||
|
_mouseState = parWindow.NativeWindow.MouseState.GetSnapshot();
|
||||||
|
_previousMouseState = parWindow.NativeWindow.MouseState.GetSnapshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void Update(double parDeltaTime)
|
public void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
_previousKeyboardState = _keyboardState;
|
_previousKeyboardState = _keyboardState;
|
||||||
_keyboardState = parWindow.NativeWindow.KeyboardState.GetSnapshot();
|
_keyboardState = _window.NativeWindow.KeyboardState.GetSnapshot();
|
||||||
|
|
||||||
_previousMouseState = _mouseState;
|
_previousMouseState = _mouseState;
|
||||||
_mouseState = parWindow.NativeWindow.MouseState.GetSnapshot();
|
_mouseState = _window.NativeWindow.MouseState.GetSnapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public bool IsKeyPressed(KeyboardButtonCode parKeyboardButtonCode)
|
public bool IsKeyPressed(KeyboardButtonCode parKeyboardButtonCode)
|
||||||
{
|
{
|
||||||
return _keyboardState.IsKeyDown(MapKeyCode(parKeyboardButtonCode));
|
return _keyboardState.IsKeyDown(MapKeyCode(parKeyboardButtonCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public bool IsKeyJustPressed(KeyboardButtonCode parKeyboardButtonCode)
|
public bool IsKeyJustPressed(KeyboardButtonCode parKeyboardButtonCode)
|
||||||
{
|
{
|
||||||
return _keyboardState.IsKeyDown(MapKeyCode(parKeyboardButtonCode)) &&
|
return _keyboardState.IsKeyDown(MapKeyCode(parKeyboardButtonCode)) &&
|
||||||
!_previousKeyboardState.IsKeyDown(MapKeyCode(parKeyboardButtonCode));
|
!_previousKeyboardState.IsKeyDown(MapKeyCode(parKeyboardButtonCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public bool IsMouseButtonPressed(MouseButtonCode parButtonCode)
|
public bool IsMouseButtonPressed(MouseButtonCode parButtonCode)
|
||||||
{
|
{
|
||||||
return _mouseState.IsButtonDown(MapMouseButtonCode(parButtonCode));
|
return _mouseState.IsButtonDown(MapMouseButtonCode(parButtonCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public bool IsMouseButtonJustPressed(MouseButtonCode parButtonCode)
|
public bool IsMouseButtonJustPressed(MouseButtonCode parButtonCode)
|
||||||
{
|
{
|
||||||
return _mouseState.IsButtonDown(MapMouseButtonCode(parButtonCode)) &&
|
return _mouseState.IsButtonDown(MapMouseButtonCode(parButtonCode)) &&
|
||||||
!_previousMouseState.IsButtonDown(MapMouseButtonCode(parButtonCode));
|
!_previousMouseState.IsButtonDown(MapMouseButtonCode(parButtonCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps a custom <see cref="MouseButtonCode"/> to the GLFW <see cref="MouseButton"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parButton">The custom mouse button code.</param>
|
||||||
|
/// <returns>The corresponding GLFW mouse button.</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">Thrown if the mouse button code is not recognized.</exception>
|
||||||
private static MouseButton MapMouseButtonCode(MouseButtonCode parButton)
|
private static MouseButton MapMouseButtonCode(MouseButtonCode parButton)
|
||||||
{
|
{
|
||||||
return parButton switch
|
return parButton switch
|
||||||
@@ -56,6 +107,12 @@ public class WindowInputHandler(Window parWindow) : IInputHandler
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps a custom <see cref="KeyboardButtonCode"/> to the GLFW <see cref="Keys"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parKeyboardButtonCode">The custom keyboard button code.</param>
|
||||||
|
/// <returns>The corresponding GLFW key.</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">Thrown if the keyboard button code is not recognized.</exception>
|
||||||
private static Keys MapKeyCode(KeyboardButtonCode parKeyboardButtonCode)
|
private static Keys MapKeyCode(KeyboardButtonCode parKeyboardButtonCode)
|
||||||
{
|
{
|
||||||
return parKeyboardButtonCode switch
|
return parKeyboardButtonCode switch
|
||||||
|
|||||||
@@ -1,9 +1,31 @@
|
|||||||
namespace Engine.Resource;
|
namespace Engine.Resource;
|
||||||
|
|
||||||
public class FilesystemResourceStreamProvider(string parBasePath) : IResourceStreamProvider
|
/// <summary>
|
||||||
|
/// Provides resource streams from the file system, using a specified base path.
|
||||||
|
/// </summary>
|
||||||
|
public class FilesystemResourceStreamProvider : IResourceStreamProvider
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The base path for locating resources.
|
||||||
|
/// </summary>
|
||||||
|
private readonly string _basePath;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="FilesystemResourceStreamProvider"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parBasePath">The base path for locating resources.</param>
|
||||||
|
public FilesystemResourceStreamProvider(string parBasePath)
|
||||||
|
{
|
||||||
|
_basePath = parBasePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a stream for the resource located at the specified path, relative to the base path.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parPath">The relative path to the resource.</param>
|
||||||
|
/// <returns>A stream providing access to the resource data.</returns>
|
||||||
public Stream GetStream(string parPath)
|
public Stream GetStream(string parPath)
|
||||||
{
|
{
|
||||||
return File.OpenRead(Path.Combine(parBasePath, parPath));
|
return File.OpenRead(Path.Combine(_basePath, parPath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,15 @@
|
|||||||
namespace Engine.Resource;
|
namespace Engine.Resource;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the interface for a resource loader that can load objects from a path using a stream provider.
|
||||||
|
/// </summary>
|
||||||
public interface IResourceLoader
|
public interface IResourceLoader
|
||||||
{
|
{
|
||||||
object Load(string parPath, IResourceStreamProvider parStreamProvider);
|
/// <summary>
|
||||||
|
/// Loads a resource object from the specified path using the provided stream provider.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parPath">The path to the resource.</param>
|
||||||
|
/// <param name="parStreamProvider">The stream provider used to access the resource data.</param>
|
||||||
|
/// <returns>The loaded resource object.</returns>
|
||||||
|
public object Load(string parPath, IResourceStreamProvider parStreamProvider);
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,15 @@
|
|||||||
namespace Engine.Resource;
|
namespace Engine.Resource;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the interface for a resource manager that handles loading resources of specific types.
|
||||||
|
/// </summary>
|
||||||
public interface IResourceManager
|
public interface IResourceManager
|
||||||
{
|
{
|
||||||
T Load<T>(string parPath) where T : class;
|
/// <summary>
|
||||||
|
/// Loads a resource of the specified type from the given path.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the resource to load.</typeparam>
|
||||||
|
/// <param name="parPath">The path to the resource.</param>
|
||||||
|
/// <returns>The loaded resource as an instance of type T.</returns>
|
||||||
|
public T Load<T>(string parPath) where T : class;
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,14 @@
|
|||||||
namespace Engine.Resource;
|
namespace Engine.Resource;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the interface for a resource stream provider, responsible for retrieving streams to access resource data.
|
||||||
|
/// </summary>
|
||||||
public interface IResourceStreamProvider
|
public interface IResourceStreamProvider
|
||||||
{
|
{
|
||||||
Stream GetStream(string parPath);
|
/// <summary>
|
||||||
|
/// Retrieves a stream for the resource located at the specified path.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parPath">The path to the resource.</param>
|
||||||
|
/// <returns>A stream providing access to the resource data.</returns>
|
||||||
|
public Stream GetStream(string parPath);
|
||||||
}
|
}
|
||||||
@@ -4,8 +4,18 @@ using Engine.Asset.Font.Metadata;
|
|||||||
|
|
||||||
namespace Engine.Resource.Loader;
|
namespace Engine.Resource.Loader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads font resources, including metadata and texture atlas.
|
||||||
|
/// </summary>
|
||||||
public class FontLoader : IResourceLoader
|
public class FontLoader : IResourceLoader
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Loads a font resource by loading its metadata and texture atlas.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parPath">The path to the font resource directory.</param>
|
||||||
|
/// <param name="parStreamProvider">The stream provider used to read files.</param>
|
||||||
|
/// <returns>A <see cref="Font"/> instance containing the loaded metadata and texture atlas.</returns>
|
||||||
|
/// <exception cref="InvalidOperationException">Thrown if the metadata cannot be loaded.</exception>
|
||||||
public object Load(string parPath, IResourceStreamProvider parStreamProvider)
|
public object Load(string parPath, IResourceStreamProvider parStreamProvider)
|
||||||
{
|
{
|
||||||
var metadataPath = Path.Combine(parPath, "metadata.json");
|
var metadataPath = Path.Combine(parPath, "metadata.json");
|
||||||
|
|||||||
@@ -6,14 +6,29 @@ using SixLabors.ImageSharp.Processing;
|
|||||||
|
|
||||||
namespace Engine.Resource.Loader;
|
namespace Engine.Resource.Loader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads image resources, including processing the image data.
|
||||||
|
/// </summary>
|
||||||
public class ImageLoader : IResourceLoader
|
public class ImageLoader : IResourceLoader
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the image from the specified path using the provided stream provider.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parPath">The path to the image resource.</param>
|
||||||
|
/// <param name="parStreamProvider">The provider for accessing resource streams.</param>
|
||||||
|
/// <returns>The loaded image resource.</returns>
|
||||||
public object Load(string parPath, IResourceStreamProvider parStreamProvider)
|
public object Load(string parPath, IResourceStreamProvider parStreamProvider)
|
||||||
{
|
{
|
||||||
using var stream = parStreamProvider.GetStream(parPath);
|
using var stream = parStreamProvider.GetStream(parPath);
|
||||||
return Load(stream);
|
return Load(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the image from a stream and processes it.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parStream">The stream containing the image data.</param>
|
||||||
|
/// <returns>The processed image resource.</returns>
|
||||||
|
/// <exception cref="InvalidOperationException">Thrown if the image cannot be loaded from the stream.</exception>
|
||||||
internal static Asset.Image<Rgba8> Load(Stream parStream)
|
internal static Asset.Image<Rgba8> Load(Stream parStream)
|
||||||
{
|
{
|
||||||
var sharpImage = Image.Load<Rgba32>(parStream);
|
var sharpImage = Image.Load<Rgba32>(parStream);
|
||||||
|
|||||||
@@ -2,8 +2,18 @@
|
|||||||
|
|
||||||
namespace Engine.Resource.Loader;
|
namespace Engine.Resource.Loader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads mesh resources from various formats, such as .obj and .stl.
|
||||||
|
/// </summary>
|
||||||
public class MeshLoader : IResourceLoader
|
public class MeshLoader : IResourceLoader
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Loads a mesh resource based on the file extension and stream provider.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parPath">The path to the mesh resource.</param>
|
||||||
|
/// <param name="parStreamProvider">The provider for accessing resource streams.</param>
|
||||||
|
/// <returns>The loaded mesh resource.</returns>
|
||||||
|
/// <exception cref="InvalidOperationException">Thrown if the mesh format is unsupported.</exception>
|
||||||
public object Load(string parPath, IResourceStreamProvider parStreamProvider)
|
public object Load(string parPath, IResourceStreamProvider parStreamProvider)
|
||||||
{
|
{
|
||||||
var extension = Path.GetExtension(parPath);
|
var extension = Path.GetExtension(parPath);
|
||||||
|
|||||||
@@ -4,8 +4,17 @@ using Engine.Graphics.Shader;
|
|||||||
|
|
||||||
namespace Engine.Resource.Loader;
|
namespace Engine.Resource.Loader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads shader program resources, including vertex and fragment shaders.
|
||||||
|
/// </summary>
|
||||||
public partial class ProgramLoader : IResourceLoader
|
public partial class ProgramLoader : IResourceLoader
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Loads a shader program from the specified path and stream provider, separating vertex and fragment shaders.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parPath">The path to the shader program resource.</param>
|
||||||
|
/// <param name="parStreamProvider">The provider for accessing resource streams.</param>
|
||||||
|
/// <returns>The loaded shader program.</returns>
|
||||||
public object Load(string parPath, IResourceStreamProvider parStreamProvider)
|
public object Load(string parPath, IResourceStreamProvider parStreamProvider)
|
||||||
{
|
{
|
||||||
var textReader = new StreamReader(parStreamProvider.GetStream(parPath));
|
var textReader = new StreamReader(parStreamProvider.GetStream(parPath));
|
||||||
|
|||||||
@@ -1,7 +1,16 @@
|
|||||||
namespace Engine.Resource.Loader;
|
namespace Engine.Resource.Loader;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads texture resources, including image processing and conversion to static textures.
|
||||||
|
/// </summary>
|
||||||
public class TextureLoader : IResourceLoader
|
public class TextureLoader : IResourceLoader
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the texture from the specified path and stream provider.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parPath">The path to the texture resource.</param>
|
||||||
|
/// <param name="parStreamProvider">The provider for accessing resource streams.</param>
|
||||||
|
/// <returns>The loaded texture resource.</returns>
|
||||||
public object Load(string parPath, IResourceStreamProvider parStreamProvider)
|
public object Load(string parPath, IResourceStreamProvider parStreamProvider)
|
||||||
{
|
{
|
||||||
using var stream = parStreamProvider.GetStream(parPath);
|
using var stream = parStreamProvider.GetStream(parPath);
|
||||||
|
|||||||
@@ -1,14 +1,30 @@
|
|||||||
namespace Engine.Resource;
|
namespace Engine.Resource;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides resource streams from in-memory storage.
|
||||||
|
/// </summary>
|
||||||
public class MemoryResourceStreamProvider : IResourceStreamProvider
|
public class MemoryResourceStreamProvider : IResourceStreamProvider
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A dictionary mapping resource paths to their byte data.
|
||||||
|
/// </summary>
|
||||||
private readonly Dictionary<string, byte[]> _resources = new();
|
private readonly Dictionary<string, byte[]> _resources = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a stream for the resource stored in memory at the specified path.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parPath">The path to the in-memory resource.</param>
|
||||||
|
/// <returns>A stream providing access to the resource data.</returns>
|
||||||
public Stream GetStream(string parPath)
|
public Stream GetStream(string parPath)
|
||||||
{
|
{
|
||||||
return new MemoryStream(_resources[parPath]);
|
return new MemoryStream(_resources[parPath]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a resource to the in-memory storage.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parPath">The path to associate with the resource.</param>
|
||||||
|
/// <param name="parData">The byte array representing the resource data.</param>
|
||||||
internal void AddResource(string parPath, byte[] parData)
|
internal void AddResource(string parPath, byte[] parData)
|
||||||
{
|
{
|
||||||
_resources.Add(parPath, parData);
|
_resources.Add(parPath, parData);
|
||||||
|
|||||||
@@ -1,17 +1,42 @@
|
|||||||
namespace Engine.Resource;
|
namespace Engine.Resource;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manages loading and caching of resources.
|
||||||
|
/// </summary>
|
||||||
public class ResourceManager : IResourceManager
|
public class ResourceManager : IResourceManager
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The provider for resource streams.
|
||||||
|
/// </summary>
|
||||||
internal IResourceStreamProvider StreamProvider { get; }
|
internal IResourceStreamProvider StreamProvider { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dictionary mapping resource types to their respective loaders.
|
||||||
|
/// </summary>
|
||||||
private readonly Dictionary<Type, IResourceLoader> _loaders = new();
|
private readonly Dictionary<Type, IResourceLoader> _loaders = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dictionary mapping resource types to their cached resources.
|
||||||
|
/// </summary>
|
||||||
private readonly Dictionary<Type, ResourceStorage> _storages = new();
|
private readonly Dictionary<Type, ResourceStorage> _storages = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ResourceManager"/> class with the specified stream provider.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parStreamProvider">The stream provider used for resource loading.</param>
|
||||||
public ResourceManager(IResourceStreamProvider parStreamProvider)
|
public ResourceManager(IResourceStreamProvider parStreamProvider)
|
||||||
{
|
{
|
||||||
StreamProvider = parStreamProvider;
|
StreamProvider = parStreamProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads a resource of the specified type from the given path, using the appropriate loader.
|
||||||
|
/// If the resource is already cached, it is returned from the cache.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the resource to load.</typeparam>
|
||||||
|
/// <param name="parPath">The path to the resource.</param>
|
||||||
|
/// <returns>The loaded resource.</returns>
|
||||||
|
/// <exception cref="InvalidOperationException">Thrown when no loader is registered for the specified resource type.</exception>
|
||||||
public T Load<T>(string parPath) where T : class
|
public T Load<T>(string parPath) where T : class
|
||||||
{
|
{
|
||||||
if (!_storages.TryGetValue(typeof(T), out var storage))
|
if (!_storages.TryGetValue(typeof(T), out var storage))
|
||||||
@@ -38,30 +63,60 @@ public class ResourceManager : IResourceManager
|
|||||||
return (T)resource;
|
return (T)resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a loader for a specific resource type.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of resource the loader is responsible for.</typeparam>
|
||||||
|
/// <param name="parLoader">The loader to register.</param>
|
||||||
internal void RegisterLoader<T>(IResourceLoader parLoader) where T : class
|
internal void RegisterLoader<T>(IResourceLoader parLoader) where T : class
|
||||||
{
|
{
|
||||||
_loaders.Add(typeof(T), parLoader);
|
_loaders.Add(typeof(T), parLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears all cached resources.
|
||||||
|
/// </summary>
|
||||||
internal void Reset()
|
internal void Reset()
|
||||||
{
|
{
|
||||||
_storages.Clear();
|
_storages.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A helper class for storing resources in memory, indexed by their path.
|
||||||
|
/// </summary>
|
||||||
private class ResourceStorage
|
private class ResourceStorage
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A dictionary mapping resource paths to the resources themselves.
|
||||||
|
/// </summary>
|
||||||
private readonly Dictionary<string, object> _resources = new();
|
private readonly Dictionary<string, object> _resources = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a resource to the storage.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of resource to store.</typeparam>
|
||||||
|
/// <param name="parPath">The path of the resource.</param>
|
||||||
|
/// <param name="parResource">The resource to store.</param>
|
||||||
public void Add<T>(string parPath, T parResource) where T : class
|
public void Add<T>(string parPath, T parResource) where T : class
|
||||||
{
|
{
|
||||||
_resources.Add(parPath, parResource);
|
_resources.Add(parPath, parResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a resource from storage by its path.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of resource to retrieve.</typeparam>
|
||||||
|
/// <param name="parPath">The path of the resource.</param>
|
||||||
|
/// <returns>The resource, or <c>null</c> if not found.</returns>
|
||||||
public T? Get<T>(string parPath) where T : class
|
public T? Get<T>(string parPath) where T : class
|
||||||
{
|
{
|
||||||
return _resources.TryGetValue(parPath, out var resource) ? (T)resource : null;
|
return _resources.TryGetValue(parPath, out var resource) ? (T)resource : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a resource from storage by its path.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parPath">The path of the resource to remove.</param>
|
||||||
public void Remove(string parPath)
|
public void Remove(string parPath)
|
||||||
{
|
{
|
||||||
_resources.Remove(parPath);
|
_resources.Remove(parPath);
|
||||||
|
|||||||
@@ -4,22 +4,18 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Scene.Component.BuiltIn;
|
namespace Engine.Scene.Component.BuiltIn;
|
||||||
|
|
||||||
public abstract class Camera(
|
/// <summary>
|
||||||
float parNearPlane,
|
/// Abstract base class for cameras that provide view and projection matrices.
|
||||||
float parFarPlane
|
/// </summary>
|
||||||
) : Component, ICamera
|
public abstract class Camera : Component, ICamera
|
||||||
{
|
{
|
||||||
public float AspectRatio { get; private set; } = 1;
|
/// <inheritdoc/>
|
||||||
public float NearPlane { get; set; } = parNearPlane;
|
|
||||||
public float FarPlane { get; set; } = parFarPlane;
|
|
||||||
|
|
||||||
public RenderLayer RenderLayer { get; set; } = RenderLayer.DEFAULT;
|
|
||||||
|
|
||||||
private Vector2i _screenSize = new(1, 1);
|
|
||||||
|
|
||||||
public abstract Matrix4 View { get; }
|
public abstract Matrix4 View { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public abstract Matrix4 Projection { get; }
|
public abstract Matrix4 Projection { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public Vector2i ScreenSize
|
public Vector2i ScreenSize
|
||||||
{
|
{
|
||||||
get => _screenSize;
|
get => _screenSize;
|
||||||
@@ -30,6 +26,54 @@ public abstract class Camera(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The aspect ratio of the camera, calculated as width divided by height.
|
||||||
|
/// </summary>
|
||||||
|
public float AspectRatio { get; private set; } = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The near clipping plane distance.
|
||||||
|
/// </summary>
|
||||||
|
public float NearPlane { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The far clipping plane distance.
|
||||||
|
/// </summary>
|
||||||
|
public float FarPlane { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The render layer for the camera.
|
||||||
|
/// </summary>
|
||||||
|
public RenderLayer RenderLayer { get; set; } = RenderLayer.DEFAULT;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The screen size of the camera.
|
||||||
|
/// </summary>
|
||||||
|
private Vector2i _screenSize = new(1, 1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Camera"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parNearPlane">The near clipping plane distance.</param>
|
||||||
|
/// <param name="parFarPlane">The far clipping plane distance.</param>
|
||||||
|
protected Camera(float parNearPlane, float parFarPlane)
|
||||||
|
{
|
||||||
|
NearPlane = parNearPlane;
|
||||||
|
FarPlane = parFarPlane;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a screen position to a world position in 3D space.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parScreenPosition">The screen position to convert.</param>
|
||||||
|
/// <returns>The corresponding world position.</returns>
|
||||||
public abstract Vector3 ScreenToWorld(Vector2 parScreenPosition);
|
public abstract Vector3 ScreenToWorld(Vector2 parScreenPosition);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a world position to a screen position in 2D space.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parWorldPosition">The world position to convert.</param>
|
||||||
|
/// <returns>The corresponding screen position.</returns>
|
||||||
public abstract Vector2 WorldToScreen(Vector3 parWorldPosition);
|
public abstract Vector2 WorldToScreen(Vector3 parWorldPosition);
|
||||||
}
|
}
|
||||||
@@ -3,33 +3,56 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Scene.Component.BuiltIn;
|
namespace Engine.Scene.Component.BuiltIn;
|
||||||
|
|
||||||
public class OrthographicCamera(
|
/// <summary>
|
||||||
float parNearPlane = -10000f,
|
/// Represents a camera using an orthographic projection.
|
||||||
float parFarPlane = 10000f,
|
/// </summary>
|
||||||
float parSize = 10f,
|
public class OrthographicCamera : Camera
|
||||||
OrthographicCamera.Axis parAxis = OrthographicCamera.Axis.Y
|
|
||||||
)
|
|
||||||
: Camera(parNearPlane, parFarPlane)
|
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The axis fixed during scaling.
|
||||||
|
/// </summary>
|
||||||
public enum Axis
|
public enum Axis
|
||||||
{
|
{
|
||||||
X,
|
X,
|
||||||
Y
|
Y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override Matrix4 Projection => GetProjectionMatrix();
|
public override Matrix4 Projection => GetProjectionMatrix();
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override Matrix4 View => GameObject.Transform.TransformMatrix.Inverted();
|
public override Matrix4 View => GameObject.Transform.TransformMatrix.Inverted();
|
||||||
|
|
||||||
public float Size { get; set; } = parSize;
|
/// <summary>
|
||||||
public bool UseScreenSize { get; set; } = false;
|
/// The size of the orthographic view.
|
||||||
public Axis FixedAxis { get; set; } = parAxis;
|
/// </summary>
|
||||||
|
public float Size { get; set; }
|
||||||
|
|
||||||
private Matrix4 GetProjectionMatrix()
|
/// <summary>
|
||||||
|
/// Indicates whether to scale based on screen dimensions.
|
||||||
|
/// </summary>
|
||||||
|
public bool UseScreenSize { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The axis to keep fixed when scaling the view.
|
||||||
|
/// </summary>
|
||||||
|
public Axis FixedAxis { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="OrthographicCamera"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parNearPlane">The near clipping plane distance.</param>
|
||||||
|
/// <param name="parFarPlane">The far clipping plane distance.</param>
|
||||||
|
/// <param name="parSize">The size of the orthographic view.</param>
|
||||||
|
/// <param name="parAxis">The fixed axis for scaling.</param>
|
||||||
|
public OrthographicCamera(float parNearPlane = -10000f, float parFarPlane = 10000f, float parSize = 10f,
|
||||||
|
Axis parAxis = Axis.Y) : base(parNearPlane, parFarPlane)
|
||||||
{
|
{
|
||||||
var size = GameObject.Transform.Size.Xy;
|
Size = parSize;
|
||||||
return Matrix4.CreateOrthographic(size.X, size.Y, -NearPlane, -FarPlane);
|
FixedAxis = parAxis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override Vector3 ScreenToWorld(Vector2 parScreenPosition)
|
public override Vector3 ScreenToWorld(Vector2 parScreenPosition)
|
||||||
{
|
{
|
||||||
var normalized = (parScreenPosition / ScreenSize) - new Vector2(0.5f);
|
var normalized = (parScreenPosition / ScreenSize) - new Vector2(0.5f);
|
||||||
@@ -42,6 +65,7 @@ public class OrthographicCamera(
|
|||||||
.Xyz;
|
.Xyz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override Vector2 WorldToScreen(Vector3 parWorldPosition)
|
public override Vector2 WorldToScreen(Vector3 parWorldPosition)
|
||||||
{
|
{
|
||||||
var normalized = new Vector4(parWorldPosition, 1)
|
var normalized = new Vector4(parWorldPosition, 1)
|
||||||
@@ -55,6 +79,7 @@ public class OrthographicCamera(
|
|||||||
return (normalized + new Vector2(0.5f)) * ScreenSize;
|
return (normalized + new Vector2(0.5f)) * ScreenSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override void Update(double parDeltaTime)
|
public override void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
if (UseScreenSize)
|
if (UseScreenSize)
|
||||||
@@ -75,4 +100,14 @@ public class OrthographicCamera(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates the orthographic projection matrix for the camera.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The orthographic projection matrix.</returns>
|
||||||
|
private Matrix4 GetProjectionMatrix()
|
||||||
|
{
|
||||||
|
var size = GameObject.Transform.Size.Xy;
|
||||||
|
return Matrix4.CreateOrthographic(size.X, size.Y, -NearPlane, -FarPlane);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,33 +3,42 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Scene.Component.BuiltIn;
|
namespace Engine.Scene.Component.BuiltIn;
|
||||||
|
|
||||||
public class PerspectiveCamera(
|
/// <summary>
|
||||||
float parFieldOfView = 60.0f,
|
/// Represents a camera using a perspective projection.
|
||||||
float parNearPlane = 0.01f,
|
/// </summary>
|
||||||
float parFarPlane = 1000f
|
public class PerspectiveCamera : Camera
|
||||||
)
|
|
||||||
: Camera(parNearPlane, parFarPlane)
|
|
||||||
{
|
{
|
||||||
public override Matrix4 View
|
/// <inheritdoc/>
|
||||||
{
|
public override Matrix4 View => GetProjectionMatrix();
|
||||||
get
|
|
||||||
{
|
|
||||||
var transformMatrix = GameObject.Transform.TransformMatrix;
|
|
||||||
var forward = new Vector4(0, 1, 0, 1).MulProject(in transformMatrix);
|
|
||||||
var eye = new Vector4(0, 0, 0, 1).MulProject(in transformMatrix);
|
|
||||||
var up = (new Vector4(0, 0, 1, 1).MulProject(in transformMatrix) - eye).Normalized();
|
|
||||||
|
|
||||||
return Matrix4.LookAt(eye.Xyz, forward.Xyz, up.Xyz);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override Matrix4 Projection =>
|
public override Matrix4 Projection =>
|
||||||
Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(FieldOfView), AspectRatio,
|
Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(FieldOfView), AspectRatio,
|
||||||
NearPlane, FarPlane);
|
NearPlane, FarPlane);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The forward direction of the camera.
|
||||||
|
/// </summary>
|
||||||
public Vector3 Forward => new Vector4(0, 1, 0, 1).MulProject(GameObject.Transform.TransformMatrix).Xyz;
|
public Vector3 Forward => new Vector4(0, 1, 0, 1).MulProject(GameObject.Transform.TransformMatrix).Xyz;
|
||||||
public float FieldOfView { get; set; } = parFieldOfView;
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The vertical field of view of the camera, in degrees.
|
||||||
|
/// </summary>
|
||||||
|
public float FieldOfView { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="PerspectiveCamera"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parFieldOfView">The vertical field of view, in degrees.</param>
|
||||||
|
/// <param name="parNearPlane">The near clipping plane distance.</param>
|
||||||
|
/// <param name="parFarPlane">The far clipping plane distance.</param>
|
||||||
|
public PerspectiveCamera(float parFieldOfView = 60.0f, float parNearPlane = 0.01f, float parFarPlane = 1000f) : base(
|
||||||
|
parNearPlane, parFarPlane)
|
||||||
|
{
|
||||||
|
FieldOfView = parFieldOfView;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override Vector3 ScreenToWorld(Vector2 parScreenPosition)
|
public override Vector3 ScreenToWorld(Vector2 parScreenPosition)
|
||||||
{
|
{
|
||||||
var normalized = (parScreenPosition / ScreenSize) - new Vector2(0.5f);
|
var normalized = (parScreenPosition / ScreenSize) - new Vector2(0.5f);
|
||||||
@@ -42,6 +51,7 @@ public class PerspectiveCamera(
|
|||||||
.Xyz;
|
.Xyz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override Vector2 WorldToScreen(Vector3 parWorldPosition)
|
public override Vector2 WorldToScreen(Vector3 parWorldPosition)
|
||||||
{
|
{
|
||||||
var normalized = new Vector4(parWorldPosition, 1)
|
var normalized = new Vector4(parWorldPosition, 1)
|
||||||
@@ -54,4 +64,18 @@ public class PerspectiveCamera(
|
|||||||
|
|
||||||
return (normalized + new Vector2(0.5f)) * ScreenSize;
|
return (normalized + new Vector2(0.5f)) * ScreenSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates the perspective projection matrix for the camera.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The perspective projection matrix.</returns>
|
||||||
|
private Matrix4 GetProjectionMatrix()
|
||||||
|
{
|
||||||
|
var transformMatrix = GameObject.Transform.TransformMatrix;
|
||||||
|
var forward = new Vector4(0, 1, 0, 1).MulProject(in transformMatrix);
|
||||||
|
var eye = new Vector4(0, 0, 0, 1).MulProject(in transformMatrix);
|
||||||
|
var up = (new Vector4(0, 0, 1, 1).MulProject(in transformMatrix) - eye).Normalized();
|
||||||
|
|
||||||
|
return Matrix4.LookAt(eye.Xyz, forward.Xyz, up.Xyz);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,14 +4,32 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Scene.Component.BuiltIn.Renderer;
|
namespace Engine.Scene.Component.BuiltIn.Renderer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A component for rendering 2D boxes with an optional texture and color.
|
||||||
|
/// </summary>
|
||||||
public class Box2DRenderer : Component
|
public class Box2DRenderer : Component
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The color of the box.
|
||||||
|
/// </summary>
|
||||||
public ref Vector4 Color => ref _color;
|
public ref Vector4 Color => ref _color;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The texture of the box.
|
||||||
|
/// </summary>
|
||||||
public Texture? Texture { get; set; }
|
public Texture? Texture { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The render layer for the box.
|
||||||
|
/// </summary>
|
||||||
public RenderLayer RenderLayer { get; set; } = RenderLayer.DEFAULT;
|
public RenderLayer RenderLayer { get; set; } = RenderLayer.DEFAULT;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The color of the box.
|
||||||
|
/// </summary>
|
||||||
private Vector4 _color = Vector4.One;
|
private Vector4 _color = Vector4.One;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override void Render()
|
public override void Render()
|
||||||
{
|
{
|
||||||
Engine.Instance.Renderer[RenderLayer].QuadRenderer
|
Engine.Instance.Renderer[RenderLayer].QuadRenderer
|
||||||
|
|||||||
@@ -4,12 +4,27 @@ using Engine.Graphics.Texture;
|
|||||||
|
|
||||||
namespace Engine.Scene.Component.BuiltIn.Renderer;
|
namespace Engine.Scene.Component.BuiltIn.Renderer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A component for rendering 3D meshes with optional albedo textures.
|
||||||
|
/// </summary>
|
||||||
public class MeshRenderer : Component
|
public class MeshRenderer : Component
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The mesh to be rendered.
|
||||||
|
/// </summary>
|
||||||
public Mesh Mesh { get; set; } = null!;
|
public Mesh Mesh { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The albedo texture for the mesh.
|
||||||
|
/// </summary>
|
||||||
public Texture? Albedo { get; set; } = null;
|
public Texture? Albedo { get; set; } = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The render layer for the mesh.
|
||||||
|
/// </summary>
|
||||||
public RenderLayer RenderLayer { get; set; } = RenderLayer.DEFAULT;
|
public RenderLayer RenderLayer { get; set; } = RenderLayer.DEFAULT;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override void Render()
|
public override void Render()
|
||||||
{
|
{
|
||||||
Engine.Instance.Renderer[RenderLayer].AnyMeshRenderer
|
Engine.Instance.Renderer[RenderLayer].AnyMeshRenderer
|
||||||
|
|||||||
@@ -4,15 +4,37 @@ using OpenTK.Mathematics;
|
|||||||
|
|
||||||
namespace Engine.Scene.Component.BuiltIn.Renderer;
|
namespace Engine.Scene.Component.BuiltIn.Renderer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A component for rendering text with a specified font, color, and render layer.
|
||||||
|
/// </summary>
|
||||||
public class TextRenderer : Component
|
public class TextRenderer : Component
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The font used for rendering the text.
|
||||||
|
/// </summary>
|
||||||
public Font Font { get; set; } = null!;
|
public Font Font { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The color of the text.
|
||||||
|
/// </summary>
|
||||||
public ref Vector4 Color => ref _color;
|
public ref Vector4 Color => ref _color;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The text to be rendered.
|
||||||
|
/// </summary>
|
||||||
public string? Text { get; set; }
|
public string? Text { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The render layer for the text.
|
||||||
|
/// </summary>
|
||||||
public RenderLayer RenderLayer { get; set; } = RenderLayer.DEFAULT;
|
public RenderLayer RenderLayer { get; set; } = RenderLayer.DEFAULT;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The color of the text.
|
||||||
|
/// </summary>
|
||||||
private Vector4 _color = Vector4.One;
|
private Vector4 _color = Vector4.One;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override void Render()
|
public override void Render()
|
||||||
{
|
{
|
||||||
if (Text == null)
|
if (Text == null)
|
||||||
|
|||||||
@@ -2,20 +2,51 @@
|
|||||||
|
|
||||||
namespace Engine.Scene.Component.BuiltIn;
|
namespace Engine.Scene.Component.BuiltIn;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the transform component of a game object, encapsulating its position, rotation, scale, and size.
|
||||||
|
/// </summary>
|
||||||
public class Transform : Component
|
public class Transform : Component
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The size of the object in local space.
|
||||||
|
/// </summary>
|
||||||
public ref Vector3 Size => ref _size;
|
public ref Vector3 Size => ref _size;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The scaling factor applied to the object in local space.
|
||||||
|
/// </summary>
|
||||||
public ref Vector3 Scale => ref _scale;
|
public ref Vector3 Scale => ref _scale;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The rotation of the object in local space, represented as a quaternion.
|
||||||
|
/// </summary>
|
||||||
public ref Quaternion Rotation => ref _rotation;
|
public ref Quaternion Rotation => ref _rotation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The translation (position) of the object in local space.
|
||||||
|
/// </summary>
|
||||||
public ref Vector3 Translation => ref _translation;
|
public ref Vector3 Translation => ref _translation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The full transformation matrix that combines size, scaling, rotation, and translation.
|
||||||
|
/// </summary>
|
||||||
public Matrix4 FullTransformMatrix => Matrix4.CreateScale(Size) * TransformMatrix;
|
public Matrix4 FullTransformMatrix => Matrix4.CreateScale(Size) * TransformMatrix;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The combined transformation matrix, accounting for local and parent transformations.
|
||||||
|
/// </summary>
|
||||||
public Matrix4 TransformMatrix => LocalTransformMatrix * ParentTransformMatrix;
|
public Matrix4 TransformMatrix => LocalTransformMatrix * ParentTransformMatrix;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The local transformation matrix, derived from scale, rotation, and translation.
|
||||||
|
/// </summary>
|
||||||
public Matrix4 LocalTransformMatrix => Matrix4.CreateScale(Scale) *
|
public Matrix4 LocalTransformMatrix => Matrix4.CreateScale(Scale) *
|
||||||
Matrix4.CreateFromQuaternion(Rotation) *
|
Matrix4.CreateFromQuaternion(Rotation) *
|
||||||
Matrix4.CreateTranslation(Translation);
|
Matrix4.CreateTranslation(Translation);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The parent transformation matrix, retrieved from the parent object in the hierarchy.
|
||||||
|
/// </summary>
|
||||||
private Matrix4 ParentTransformMatrix
|
private Matrix4 ParentTransformMatrix
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -25,16 +56,39 @@ public class Transform : Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The size of the object in local space.
|
||||||
|
/// </summary>
|
||||||
private Vector3 _size = Vector3.One;
|
private Vector3 _size = Vector3.One;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The scaling factor applied to the object in local space.
|
||||||
|
/// </summary>
|
||||||
private Vector3 _scale = Vector3.One;
|
private Vector3 _scale = Vector3.One;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The rotation of the object in local space, represented as a quaternion.
|
||||||
|
/// </summary>
|
||||||
private Quaternion _rotation = Quaternion.Identity;
|
private Quaternion _rotation = Quaternion.Identity;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The translation (position) of the object in local space.
|
||||||
|
/// </summary>
|
||||||
private Vector3 _translation = Vector3.Zero;
|
private Vector3 _translation = Vector3.Zero;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the full translation of the object in world space.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A <see cref="Vector3"/> representing the object's world position.</returns>
|
||||||
public Vector3 GetFullTranslation()
|
public Vector3 GetFullTranslation()
|
||||||
{
|
{
|
||||||
return FullTransformMatrix.ExtractTranslation();
|
return FullTransformMatrix.ExtractTranslation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a clone of the current transform.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A new <see cref="Transform"/> instance with the same properties as the original.</returns>
|
||||||
public Transform Clone()
|
public Transform Clone()
|
||||||
{
|
{
|
||||||
var clone =
|
var clone =
|
||||||
|
|||||||
@@ -1,46 +1,89 @@
|
|||||||
namespace Engine.Scene.Component;
|
namespace Engine.Scene.Component;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for all components, defining common lifecycle and behavior methods.
|
||||||
|
/// </summary>
|
||||||
public abstract class Component : IUpdate, IRender
|
public abstract class Component : IUpdate, IRender
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A unique identifier for the component.
|
||||||
|
/// </summary>
|
||||||
public Guid Id { get; } = Guid.NewGuid();
|
public Guid Id { get; } = Guid.NewGuid();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The game object to which this component is attached.
|
||||||
|
/// </summary>
|
||||||
public GameObject GameObject { get; internal set; }
|
public GameObject GameObject { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the component is initialized.
|
||||||
|
/// </summary>
|
||||||
public virtual void Awake()
|
public virtual void Awake()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called once at the start of the component's lifecycle.
|
||||||
|
/// </summary>
|
||||||
public virtual void Start()
|
public virtual void Start()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called before the main update loop.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parDeltaTime">Time elapsed since the last frame.</param>
|
||||||
public virtual void PreUpdate(double parDeltaTime)
|
public virtual void PreUpdate(double parDeltaTime)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called during the main update loop.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parDeltaTime">Time elapsed since the last frame.</param>
|
||||||
public virtual void Update(double parDeltaTime)
|
public virtual void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called during the main render loop.
|
||||||
|
/// </summary>
|
||||||
public virtual void Render()
|
public virtual void Render()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the component is destroyed.
|
||||||
|
/// </summary>
|
||||||
public virtual void Destroy()
|
public virtual void Destroy()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables the component, allowing it to participate in updates and rendering.
|
||||||
|
/// </summary>
|
||||||
public virtual void Enable()
|
public virtual void Enable()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disables the component, stopping it from participating in updates and rendering.
|
||||||
|
/// </summary>
|
||||||
public virtual void Disable()
|
public virtual void Disable()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ComponentTypeExtensions
|
/// <summary>
|
||||||
|
/// Provides extension methods for component type analysis.
|
||||||
|
/// </summary>
|
||||||
|
internal static class ComponentTypeExtensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the base type of a component, accounting for inheritance chains.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parType">The type of the component.</param>
|
||||||
|
/// <returns>The base <see cref="Type"/> of the component.</returns>
|
||||||
internal static Type GetComponentBaseType(this Type parType)
|
internal static Type GetComponentBaseType(this Type parType)
|
||||||
{
|
{
|
||||||
var baseType = parType.BaseType;
|
var baseType = parType.BaseType;
|
||||||
|
|||||||
@@ -4,32 +4,83 @@ using Engine.Scene.Component.BuiltIn;
|
|||||||
|
|
||||||
namespace Engine.Scene;
|
namespace Engine.Scene;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a game object in the scene, containing components and providing lifecycle management.
|
||||||
|
/// </summary>
|
||||||
public sealed class GameObject : IUpdate, IRender
|
public sealed class GameObject : IUpdate, IRender
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A unique identifier of the game object.
|
||||||
|
/// </summary>
|
||||||
public Guid Id { get; } = Guid.NewGuid();
|
public Guid Id { get; } = Guid.NewGuid();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the game object is enabled based on its own state and its parent's state.
|
||||||
|
/// </summary>
|
||||||
public bool IsEnabled
|
public bool IsEnabled
|
||||||
{
|
{
|
||||||
get => IsSelfEnabled && IsParentEnabled;
|
get => IsSelfEnabled && IsParentEnabled;
|
||||||
set => _nextIsSelfEnabled = value;
|
set => _nextIsSelfEnabled = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The transform component associated with this game object.
|
||||||
|
/// </summary>
|
||||||
public Transform Transform { get; }
|
public Transform Transform { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The scene this game object belongs to.
|
||||||
|
/// </summary>
|
||||||
internal Scene? Scene { get; set; }
|
internal Scene? Scene { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether game object's parent is enabled.
|
||||||
|
/// </summary>
|
||||||
private bool IsParentEnabled => Scene?.Hierarchy.GetParent(this)?.IsEnabled ?? true;
|
private bool IsParentEnabled => Scene?.Hierarchy.GetParent(this)?.IsEnabled ?? true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether game object is enabled.
|
||||||
|
/// </summary>
|
||||||
private bool IsSelfEnabled { get; set; } = true;
|
private bool IsSelfEnabled { get; set; } = true;
|
||||||
|
|
||||||
private readonly HashSet<Component.Component> _addedComponents = [];
|
/// <summary>
|
||||||
|
/// A list of component types added to the game object.
|
||||||
|
/// </summary>
|
||||||
private readonly HashSet<Type> _addedComponentTypes = [];
|
private readonly HashSet<Type> _addedComponentTypes = [];
|
||||||
private readonly Queue<Action> _componentActions = new();
|
|
||||||
|
/// <summary>
|
||||||
|
/// A list of components attached to the game object.
|
||||||
|
/// </summary>
|
||||||
private readonly List<Component.Component> _components = [];
|
private readonly List<Component.Component> _components = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A queue of actions to be performed on the game object.
|
||||||
|
/// </summary>
|
||||||
|
private readonly Queue<Action> _componentActions = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A list of components to be added to the game object.
|
||||||
|
/// </summary>
|
||||||
|
private readonly HashSet<Component.Component> _addedComponents = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A list of components to be removed from the game object.
|
||||||
|
/// </summary>
|
||||||
private readonly HashSet<Component.Component> _removedComponents = [];
|
private readonly HashSet<Component.Component> _removedComponents = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the game object is going to be enabled next frame.
|
||||||
|
/// </summary>
|
||||||
private bool _nextIsSelfEnabled = true;
|
private bool _nextIsSelfEnabled = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the game object was enabled last frame.
|
||||||
|
/// </summary>
|
||||||
private bool _prevIsSelfEnabled = true;
|
private bool _prevIsSelfEnabled = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="GameObject"/> class with a default transform component.
|
||||||
|
/// </summary>
|
||||||
public GameObject()
|
public GameObject()
|
||||||
{
|
{
|
||||||
AddComponent<Transform>();
|
AddComponent<Transform>();
|
||||||
@@ -38,6 +89,10 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
Transform = GetComponent<Transform>()!;
|
Transform = GetComponent<Transform>()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="GameObject"/> class with a specified transform.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parTransform">The transform to associate with the game object.</param>
|
||||||
public GameObject(Transform parTransform)
|
public GameObject(Transform parTransform)
|
||||||
{
|
{
|
||||||
AddComponent(parTransform.Clone());
|
AddComponent(parTransform.Clone());
|
||||||
@@ -46,6 +101,10 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
Transform = GetComponent<Transform>()!;
|
Transform = GetComponent<Transform>()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs pre-update operations for all components.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parDeltaTime">The time delta since the last update.</param>
|
||||||
public void PreUpdate(double parDeltaTime)
|
public void PreUpdate(double parDeltaTime)
|
||||||
{
|
{
|
||||||
ProcessAddedComponents();
|
ProcessAddedComponents();
|
||||||
@@ -57,6 +116,7 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void Update(double parDeltaTime)
|
public void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
if (!IsEnabled)
|
if (!IsEnabled)
|
||||||
@@ -91,6 +151,7 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void Render()
|
public void Render()
|
||||||
{
|
{
|
||||||
if (!IsEnabled)
|
if (!IsEnabled)
|
||||||
@@ -104,6 +165,9 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Destroys the game object and its components.
|
||||||
|
/// </summary>
|
||||||
public void Destroy()
|
public void Destroy()
|
||||||
{
|
{
|
||||||
foreach (var component in _components)
|
foreach (var component in _components)
|
||||||
@@ -112,6 +176,11 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a component of a specified type if it exists on the game object.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the component to retrieve.</typeparam>
|
||||||
|
/// <returns>The component if found, otherwise null.</returns>
|
||||||
public T? GetComponent<T>() where T : Component.Component
|
public T? GetComponent<T>() where T : Component.Component
|
||||||
{
|
{
|
||||||
if (!HasComponent<T>())
|
if (!HasComponent<T>())
|
||||||
@@ -130,6 +199,11 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a component of a specified type from the game object or its children.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the component to retrieve.</typeparam>
|
||||||
|
/// <returns>The component if found, otherwise null.</returns>
|
||||||
public T? GetComponentAny<T>() where T : Component.Component
|
public T? GetComponentAny<T>() where T : Component.Component
|
||||||
{
|
{
|
||||||
var component = GetComponent<T>();
|
var component = GetComponent<T>();
|
||||||
@@ -142,6 +216,11 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
return component;
|
return component;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a component of a specified type from the game object's children.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the component to retrieve.</typeparam>
|
||||||
|
/// <returns>The component if found, otherwise null.</returns>
|
||||||
public T? GetComponentInChildren<T>() where T : Component.Component
|
public T? GetComponentInChildren<T>() where T : Component.Component
|
||||||
{
|
{
|
||||||
var children = Scene!.Hierarchy.GetChildren(this);
|
var children = Scene!.Hierarchy.GetChildren(this);
|
||||||
@@ -164,12 +243,21 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a new component of a specified type to the game object.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the component to add.</typeparam>
|
||||||
public void AddComponent<T>() where T : Component.Component, new()
|
public void AddComponent<T>() where T : Component.Component, new()
|
||||||
{
|
{
|
||||||
var component = new T();
|
var component = new T();
|
||||||
AddComponent(component);
|
AddComponent(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a new component of a specified type to the game object with optional constructor arguments.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the component to add.</typeparam>
|
||||||
|
/// <param name="parArgs">The arguments to pass to the component's constructor.</param>
|
||||||
public void AddComponent<T>(params object?[] parArgs) where T : Component.Component
|
public void AddComponent<T>(params object?[] parArgs) where T : Component.Component
|
||||||
{
|
{
|
||||||
var component = (T?)Activator.CreateInstance(
|
var component = (T?)Activator.CreateInstance(
|
||||||
@@ -189,6 +277,11 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
AddComponent(component);
|
AddComponent(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds an existing component to the game object.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the component to add.</typeparam>
|
||||||
|
/// <param name="parComponent">The component to add.</param>
|
||||||
public void AddComponent<T>(T parComponent) where T : Component.Component
|
public void AddComponent<T>(T parComponent) where T : Component.Component
|
||||||
{
|
{
|
||||||
parComponent.GameObject = this;
|
parComponent.GameObject = this;
|
||||||
@@ -206,6 +299,10 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a component of a specified type from the game object.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the component to remove.</typeparam>
|
||||||
public void RemoveComponent<T>() where T : Component.Component
|
public void RemoveComponent<T>() where T : Component.Component
|
||||||
{
|
{
|
||||||
if (typeof(T) == typeof(Transform))
|
if (typeof(T) == typeof(Transform))
|
||||||
@@ -232,12 +329,20 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether the game object contains a component of a specified type.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the component to check for.</typeparam>
|
||||||
|
/// <returns>True if the component exists, otherwise false.</returns>
|
||||||
public bool HasComponent<T>() where T : Component.Component
|
public bool HasComponent<T>() where T : Component.Component
|
||||||
{
|
{
|
||||||
var baseType = typeof(T).GetComponentBaseType();
|
var baseType = typeof(T).GetComponentBaseType();
|
||||||
return _addedComponentTypes.Contains(baseType);
|
return _addedComponentTypes.Contains(baseType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Processes changes to the game object's components.
|
||||||
|
/// </summary>
|
||||||
internal void ProcessChanges()
|
internal void ProcessChanges()
|
||||||
{
|
{
|
||||||
IsSelfEnabled = _nextIsSelfEnabled;
|
IsSelfEnabled = _nextIsSelfEnabled;
|
||||||
@@ -248,6 +353,9 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes newly added components.
|
||||||
|
/// </summary>
|
||||||
private void ProcessAddedComponents()
|
private void ProcessAddedComponents()
|
||||||
{
|
{
|
||||||
foreach (var component in _addedComponents)
|
foreach (var component in _addedComponents)
|
||||||
@@ -259,6 +367,9 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
_addedComponents.Clear();
|
_addedComponents.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cleans up and removes components that were marked for removal.
|
||||||
|
/// </summary>
|
||||||
private void ProcessRemovedComponents()
|
private void ProcessRemovedComponents()
|
||||||
{
|
{
|
||||||
foreach (var component in _removedComponents)
|
foreach (var component in _removedComponents)
|
||||||
@@ -270,16 +381,19 @@ public sealed class GameObject : IUpdate, IRender
|
|||||||
_removedComponents.Clear();
|
_removedComponents.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return Id.ToString();
|
return Id.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override bool Equals(object? parObj)
|
public override bool Equals(object? parObj)
|
||||||
{
|
{
|
||||||
return parObj is GameObject gameObject && Id == gameObject.Id;
|
return parObj is GameObject gameObject && Id == gameObject.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return HashCode.Combine(Id);
|
return HashCode.Combine(Id);
|
||||||
|
|||||||
@@ -3,20 +3,47 @@ using Engine.Util;
|
|||||||
|
|
||||||
namespace Engine.Scene;
|
namespace Engine.Scene;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manages a hierarchical relationship between objects, supporting parent-child relationships
|
||||||
|
/// and enabling traversal, adding/removing of objects within the hierarchy.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the objects in the hierarchy. Must be a reference type.</typeparam>
|
||||||
public class Hierarchy<T>
|
public class Hierarchy<T>
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A collection of all objects in the hierarchy (all keys in the parent lookup dictionary).
|
||||||
|
/// </summary>
|
||||||
internal Dictionary<T, T?>.KeyCollection Objects => _parentLookup.Keys;
|
internal Dictionary<T, T?>.KeyCollection Objects => _parentLookup.Keys;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dictionary mapping objects to their children.
|
||||||
|
/// </summary>
|
||||||
private readonly Dictionary<NullableObject<T>, IList<T>> _childrenLookup = new();
|
private readonly Dictionary<NullableObject<T>, IList<T>> _childrenLookup = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dictionary mapping objects to their parent.
|
||||||
|
/// </summary>
|
||||||
private readonly Dictionary<T, T?> _parentLookup = new();
|
private readonly Dictionary<T, T?> _parentLookup = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A queue of actions to be processed.
|
||||||
|
/// </summary>
|
||||||
private readonly ConcurrentQueue<Action> _hierarchyActions = new();
|
private readonly ConcurrentQueue<Action> _hierarchyActions = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Hierarchy{T}"/> class.
|
||||||
|
/// Adds a root element with a null value to represent the root node.
|
||||||
|
/// </summary>
|
||||||
public Hierarchy()
|
public Hierarchy()
|
||||||
{
|
{
|
||||||
_childrenLookup.Add(new NullableObject<T>(), new List<T>());
|
_childrenLookup.Add(new NullableObject<T>(), new List<T>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Processes queued actions related to changes in the hierarchy (e.g., adding/removing objects).
|
||||||
|
/// This method is invoked to ensure the changes are executed in the proper order.
|
||||||
|
/// </summary>
|
||||||
internal void ProcessChanges()
|
internal void ProcessChanges()
|
||||||
{
|
{
|
||||||
while (_hierarchyActions.TryDequeue(out var action))
|
while (_hierarchyActions.TryDequeue(out var action))
|
||||||
@@ -25,6 +52,11 @@ public class Hierarchy<T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds an object to the hierarchy. This method will create a new entry in the hierarchy
|
||||||
|
/// with the object having no parent initially, and will add it as a child of the root.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parObj">The object to add to the hierarchy.</param>
|
||||||
public void Add(T parObj)
|
public void Add(T parObj)
|
||||||
{
|
{
|
||||||
_hierarchyActions.Enqueue(() =>
|
_hierarchyActions.Enqueue(() =>
|
||||||
@@ -40,6 +72,11 @@ public class Hierarchy<T>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes an object from the hierarchy and all its descendants.
|
||||||
|
/// The object will be deleted, and its references will be removed from both parent and child lookup dictionaries.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parObj">The object to remove from the hierarchy.</param>
|
||||||
public void Remove(T parObj)
|
public void Remove(T parObj)
|
||||||
{
|
{
|
||||||
foreach (var child in GetChildren(parObj))
|
foreach (var child in GetChildren(parObj))
|
||||||
@@ -62,11 +99,23 @@ public class Hierarchy<T>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a child object to a specific parent in the hierarchy.
|
||||||
|
/// This sets the given object's parent and adds it to the parent's list of children.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parParent">The parent object.</param>
|
||||||
|
/// <param name="parChild">The child object to add to the parent.</param>
|
||||||
public void AddChild(T parParent, T parChild)
|
public void AddChild(T parParent, T parChild)
|
||||||
{
|
{
|
||||||
SetParent(parChild, parParent);
|
SetParent(parChild, parParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the parent of an object in the hierarchy.
|
||||||
|
/// This method will remove the object from its current parent and add it to the new parent.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parChild">The child object whose parent is being changed.</param>
|
||||||
|
/// <param name="parParent">The new parent of the child object.</param>
|
||||||
private void SetParent(T parChild, T? parParent)
|
private void SetParent(T parChild, T? parParent)
|
||||||
{
|
{
|
||||||
if (parChild.Equals(parParent))
|
if (parChild.Equals(parParent))
|
||||||
@@ -89,11 +138,22 @@ public class Hierarchy<T>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if an object exists in the hierarchy.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parObj">The object to check for presence in the hierarchy.</param>
|
||||||
|
/// <returns>True if the object exists in the hierarchy, otherwise false.</returns>
|
||||||
public bool Contains(T parObj)
|
public bool Contains(T parObj)
|
||||||
{
|
{
|
||||||
return _parentLookup.ContainsKey(parObj) && _childrenLookup.ContainsKey(parObj);
|
return _parentLookup.ContainsKey(parObj) && _childrenLookup.ContainsKey(parObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the parent of a given child object in the hierarchy.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parChild">The child object whose parent is to be retrieved.</param>
|
||||||
|
/// <returns>The parent object of the given child.</returns>
|
||||||
|
/// <exception cref="InvalidOperationException">Thrown if the child is not found in the hierarchy.</exception>
|
||||||
public T? GetParent(T parChild)
|
public T? GetParent(T parChild)
|
||||||
{
|
{
|
||||||
return _parentLookup.TryGetValue(parChild, out var parent)
|
return _parentLookup.TryGetValue(parChild, out var parent)
|
||||||
@@ -101,11 +161,22 @@ public class Hierarchy<T>
|
|||||||
: throw new InvalidOperationException($"Child {parChild} is not in hierarchy");
|
: throw new InvalidOperationException($"Child {parChild} is not in hierarchy");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the children of a given parent object in the hierarchy.
|
||||||
|
/// If no parent is provided, it retrieves the children of the root node.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parParent">The parent object whose children are to be retrieved. Null refers to the root.</param>
|
||||||
|
/// <returns>A collection of child objects of the specified parent.</returns>
|
||||||
public IEnumerable<T> GetChildren(T? parParent = null)
|
public IEnumerable<T> GetChildren(T? parParent = null)
|
||||||
{
|
{
|
||||||
return _childrenLookup.TryGetValue(parParent, out var children) ? children : Enumerable.Empty<T>();
|
return _childrenLookup.TryGetValue(parParent, out var children) ? children : Enumerable.Empty<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all descendants of a given parent object in the hierarchy, recursively.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parParent">The parent object whose descendants are to be retrieved. Null refers to the root.</param>
|
||||||
|
/// <returns>A collection of all descendant objects of the specified parent.</returns>
|
||||||
public IEnumerable<T> GetAllChildren(T? parParent = null)
|
public IEnumerable<T> GetAllChildren(T? parParent = null)
|
||||||
{
|
{
|
||||||
var children = GetChildren(parParent);
|
var children = GetChildren(parParent);
|
||||||
@@ -121,6 +192,12 @@ public class Hierarchy<T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a given child object is in the hierarchy of a given ancestor object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parAncestor">The ancestor object to check.</param>
|
||||||
|
/// <param name="parChild">The child object to check for inclusion in the hierarchy.</param>
|
||||||
|
/// <returns>True if the child is in the hierarchy of the ancestor, otherwise false.</returns>
|
||||||
public bool IsInHierarchy(T? parAncestor, T? parChild)
|
public bool IsInHierarchy(T? parAncestor, T? parChild)
|
||||||
{
|
{
|
||||||
if (parChild == null) // if child is null (root), then it is not in hierarchy, as root can not have a parent
|
if (parChild == null) // if child is null (root), then it is not in hierarchy, as root can not have a parent
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
namespace Engine.Scene;
|
namespace Engine.Scene;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines an interface for rendering objects.
|
||||||
|
/// </summary>
|
||||||
public interface IRender
|
public interface IRender
|
||||||
{
|
{
|
||||||
void Render();
|
/// <summary>
|
||||||
|
/// Renders the object.
|
||||||
|
/// </summary>
|
||||||
|
public void Render();
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,13 @@
|
|||||||
namespace Engine.Scene;
|
namespace Engine.Scene;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines an interface for updating logic over time.
|
||||||
|
/// </summary>
|
||||||
public interface IUpdate
|
public interface IUpdate
|
||||||
{
|
{
|
||||||
void Update(double parDeltaTime);
|
/// <summary>
|
||||||
|
/// Updates the state of the object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parDeltaTime">The time elapsed since the last update, in seconds.</param>
|
||||||
|
public void Update(double parDeltaTime);
|
||||||
}
|
}
|
||||||
@@ -4,17 +4,43 @@ using Engine.Scene.Component.BuiltIn;
|
|||||||
|
|
||||||
namespace Engine.Scene;
|
namespace Engine.Scene;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a scene in the game, managing the scene hierarchy and rendering the scene.
|
||||||
|
/// </summary>
|
||||||
public class Scene : IUpdate, IRender
|
public class Scene : IUpdate, IRender
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the scene is currently playing.
|
||||||
|
/// </summary>
|
||||||
public bool IsPlaying { get; private set; }
|
public bool IsPlaying { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The time scale for updating the scene. A value of 1.0 means normal speed.
|
||||||
|
/// </summary>
|
||||||
public float TimeScale { get; set; } = 1.0f;
|
public float TimeScale { get; set; } = 1.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A hierarchy of game objects in the scene.
|
||||||
|
/// </summary>
|
||||||
internal Hierarchy<GameObject> Hierarchy { get; } = new();
|
internal Hierarchy<GameObject> Hierarchy { get; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A read-only dictionary of cameras, categorized by render layer.
|
||||||
|
/// </summary>
|
||||||
internal IReadOnlyDictionary<RenderLayer, ICamera> Cameras => _cameras;
|
internal IReadOnlyDictionary<RenderLayer, ICamera> Cameras => _cameras;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dictionary of cameras, categorized by render layer.
|
||||||
|
/// </summary>
|
||||||
private readonly Dictionary<RenderLayer, ICamera> _cameras = new();
|
private readonly Dictionary<RenderLayer, ICamera> _cameras = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A private queue used to enqueue scene-related actions that should be processed later.
|
||||||
|
/// Actions are executed when the scene is processed for changes.
|
||||||
|
/// </summary>
|
||||||
private readonly Queue<Action> _sceneActions = [];
|
private readonly Queue<Action> _sceneActions = [];
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void Update(double parDeltaTime)
|
public void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
if (!IsPlaying)
|
if (!IsPlaying)
|
||||||
@@ -37,6 +63,7 @@ public class Scene : IUpdate, IRender
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void Render()
|
public void Render()
|
||||||
{
|
{
|
||||||
if (!IsPlaying)
|
if (!IsPlaying)
|
||||||
@@ -50,6 +77,12 @@ public class Scene : IUpdate, IRender
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds all components of type <typeparamref name="T"/> in the scene.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of component to find.</typeparam>
|
||||||
|
/// <param name="parOnlyEnabled">If true, only enabled components are returned.</param>
|
||||||
|
/// <returns>A list of components of type <typeparamref name="T"/>.</returns>
|
||||||
public List<T> FindAllComponents<T>(bool parOnlyEnabled = true) where T : Component.Component
|
public List<T> FindAllComponents<T>(bool parOnlyEnabled = true) where T : Component.Component
|
||||||
{
|
{
|
||||||
return Hierarchy.Objects
|
return Hierarchy.Objects
|
||||||
@@ -60,24 +93,42 @@ public class Scene : IUpdate, IRender
|
|||||||
.ToList()!;
|
.ToList()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds the first component of type <typeparamref name="T"/> in the scene.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of component to find.</typeparam>
|
||||||
|
/// <returns>The first component of type <typeparamref name="T"/>, or null if not found.</returns>
|
||||||
public T? FindFirstComponent<T>() where T : Component.Component
|
public T? FindFirstComponent<T>() where T : Component.Component
|
||||||
{
|
{
|
||||||
return Hierarchy.Objects.Select(parGameObject => parGameObject.GetComponent<T>())
|
return Hierarchy.Objects.Select(parGameObject => parGameObject.GetComponent<T>())
|
||||||
.FirstOrDefault(parComponent => parComponent != null);
|
.FirstOrDefault(parComponent => parComponent != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a game object to the scene.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parGameObject">The game object to add.</param>
|
||||||
public void Add(GameObject parGameObject)
|
public void Add(GameObject parGameObject)
|
||||||
{
|
{
|
||||||
parGameObject.Scene = this;
|
parGameObject.Scene = this;
|
||||||
Hierarchy.Add(parGameObject);
|
Hierarchy.Add(parGameObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a child game object to the specified parent game object in the scene.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parParent">The parent game object.</param>
|
||||||
|
/// <param name="parChild">The child game object to add.</param>
|
||||||
public void AddChild(GameObject parParent, GameObject parChild)
|
public void AddChild(GameObject parParent, GameObject parChild)
|
||||||
{
|
{
|
||||||
Add(parChild);
|
Add(parChild);
|
||||||
Hierarchy.AddChild(parParent, parChild);
|
Hierarchy.AddChild(parParent, parChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a game object from the scene.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parGameObject">The game object to remove.</param>
|
||||||
public void Remove(GameObject parGameObject)
|
public void Remove(GameObject parGameObject)
|
||||||
{
|
{
|
||||||
var children = Hierarchy.GetAllChildren(parGameObject).ToList();
|
var children = Hierarchy.GetAllChildren(parGameObject).ToList();
|
||||||
@@ -97,11 +148,20 @@ public class Scene : IUpdate, IRender
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the children of a parent game object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parParent">The parent game object.</param>
|
||||||
|
/// <param name="parRecursive">If true, recursively retrieves all children; otherwise, retrieves only direct children.</param>
|
||||||
|
/// <returns>An enumeration of the child game objects.</returns>
|
||||||
public IEnumerable<GameObject> GetChildren(GameObject parParent, bool parRecursive = false)
|
public IEnumerable<GameObject> GetChildren(GameObject parParent, bool parRecursive = false)
|
||||||
{
|
{
|
||||||
return parRecursive ? Hierarchy.GetAllChildren(parParent) : Hierarchy.GetChildren(parParent);
|
return parRecursive ? Hierarchy.GetAllChildren(parParent) : Hierarchy.GetChildren(parParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enters the scene by adding all cameras and setting the scene state to playing.
|
||||||
|
/// </summary>
|
||||||
internal void Enter()
|
internal void Enter()
|
||||||
{
|
{
|
||||||
if (IsPlaying)
|
if (IsPlaying)
|
||||||
@@ -120,6 +180,9 @@ public class Scene : IUpdate, IRender
|
|||||||
IsPlaying = true;
|
IsPlaying = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exits the scene and destroys all game objects in the scene
|
||||||
|
/// </summary>
|
||||||
internal void Exit()
|
internal void Exit()
|
||||||
{
|
{
|
||||||
if (!IsPlaying)
|
if (!IsPlaying)
|
||||||
@@ -135,6 +198,9 @@ public class Scene : IUpdate, IRender
|
|||||||
IsPlaying = false;
|
IsPlaying = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Processes changes in the hierarchy and scene actions.
|
||||||
|
/// </summary>
|
||||||
private void ProcessChanges()
|
private void ProcessChanges()
|
||||||
{
|
{
|
||||||
Hierarchy.ProcessChanges();
|
Hierarchy.ProcessChanges();
|
||||||
|
|||||||
@@ -1,11 +1,22 @@
|
|||||||
namespace Engine.Scene;
|
namespace Engine.Scene;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manages the current scene in the game, handles scene transitions, and facilitates updating and rendering the current scene.
|
||||||
|
/// </summary>
|
||||||
public class SceneManager : IUpdate, IRender
|
public class SceneManager : IUpdate, IRender
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The current scene being managed by the scene manager.
|
||||||
|
/// </summary>
|
||||||
public Scene? CurrentScene { get; private set; }
|
public Scene? CurrentScene { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A function to be called to transition to the next scene.
|
||||||
|
/// This is set by the scene manager when transitioning to a new scene and reset after the transition is complete.
|
||||||
|
/// </summary>
|
||||||
private Func<Scene>? _nextScene;
|
private Func<Scene>? _nextScene;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void Update(double parDeltaTime)
|
public void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
if (_nextScene != null)
|
if (_nextScene != null)
|
||||||
@@ -22,11 +33,16 @@ public class SceneManager : IUpdate, IRender
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void Render()
|
public void Render()
|
||||||
{
|
{
|
||||||
CurrentScene?.Render();
|
CurrentScene?.Render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queues a scene transition by setting the next scene to transition to.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parScene">A function that provides the next scene to transition to.</param>
|
||||||
public void TransitionTo(Func<Scene>? parScene)
|
public void TransitionTo(Func<Scene>? parScene)
|
||||||
{
|
{
|
||||||
_nextScene = parScene;
|
_nextScene = parScene;
|
||||||
|
|||||||
@@ -4,19 +4,45 @@ using Engine.Scene;
|
|||||||
|
|
||||||
namespace Engine.Util;
|
namespace Engine.Util;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides utility functions and properties for interacting with the engine's core services,
|
||||||
|
/// including input handling, scene management, and asset management.
|
||||||
|
/// </summary>
|
||||||
public static class EngineUtil
|
public static class EngineUtil
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The engine's input handler, which processes user input.
|
||||||
|
/// </summary>
|
||||||
public static IInputHandler InputHandler => Engine.Instance.InputHandler!;
|
public static IInputHandler InputHandler => Engine.Instance.InputHandler!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The engine's scene manager, which handles the current scene and scene transitions.
|
||||||
|
/// </summary>
|
||||||
public static SceneManager SceneManager => Engine.Instance.SceneManager;
|
public static SceneManager SceneManager => Engine.Instance.SceneManager;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The engine's asset resource manager, which handles loading and caching of assets.
|
||||||
|
/// </summary>
|
||||||
public static IResourceManager AssetResourceManager => Engine.Instance.AssetResourceManager;
|
public static IResourceManager AssetResourceManager => Engine.Instance.AssetResourceManager;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The engine's data folder, which contains assets and other data files.
|
||||||
|
/// </summary>
|
||||||
public static string DataFolder => Engine.Instance.DataFolder;
|
public static string DataFolder => Engine.Instance.DataFolder;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a game object and adds it to the current scene.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parGameObject">The game object to be added to the scene.</param>
|
||||||
public static void CreateObject(GameObject parGameObject)
|
public static void CreateObject(GameObject parGameObject)
|
||||||
{
|
{
|
||||||
var scene = Engine.Instance.SceneManager.CurrentScene!;
|
var scene = Engine.Instance.SceneManager.CurrentScene!;
|
||||||
scene.Add(parGameObject);
|
scene.Add(parGameObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Closes the engine, shutting down any running systems and freeing resources.
|
||||||
|
/// </summary>
|
||||||
public static void Close()
|
public static void Close()
|
||||||
{
|
{
|
||||||
Engine.Instance.Close();
|
Engine.Instance.Close();
|
||||||
|
|||||||
@@ -2,8 +2,17 @@
|
|||||||
|
|
||||||
namespace Engine.Util;
|
namespace Engine.Util;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains mathematical utility methods.
|
||||||
|
/// </summary>
|
||||||
public static class Math
|
public static class Math
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Multiplies a <see cref="Vector4"/> by a <see cref="Matrix4"/> and performs projective division.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parA">The vector to be multiplied.</param>
|
||||||
|
/// <param name="parM">The matrix to multiply the vector with.</param>
|
||||||
|
/// <returns>A transformed <see cref="Vector4"/> after multiplication and projective division.</returns>
|
||||||
public static Vector4 MulProject(this Vector4 parA, in Matrix4 parM)
|
public static Vector4 MulProject(this Vector4 parA, in Matrix4 parM)
|
||||||
{
|
{
|
||||||
var result = parA * parM;
|
var result = parA * parM;
|
||||||
|
|||||||
@@ -1,35 +1,67 @@
|
|||||||
namespace Engine.Util;
|
namespace Engine.Util;
|
||||||
|
|
||||||
public readonly struct NullableObject<T>(T? parValue) : IEquatable<NullableObject<T>>
|
/// <summary>
|
||||||
|
/// A struct that wraps nullable reference types, providing a way to check if a value is null or not.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the wrapped object, constrained to be a class type.</typeparam>
|
||||||
|
public readonly struct NullableObject<T> : IEquatable<NullableObject<T>>
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if the wrapped object is null.
|
||||||
|
/// </summary>
|
||||||
public bool IsNull => Value == null;
|
public bool IsNull => Value == null;
|
||||||
public T? Value { get; } = parValue;
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The wrapped object.
|
||||||
|
/// </summary>
|
||||||
|
public T? Value { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a <see cref="NullableObject{T}"/> with a null value.
|
||||||
|
/// </summary>
|
||||||
public NullableObject() : this(null)
|
public NullableObject() : this(null)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a <see cref="NullableObject{T}"/> with the specified value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parValue">The value to wrap.</param>
|
||||||
|
public NullableObject(T? parValue)
|
||||||
|
{
|
||||||
|
Value = parValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implicitly converts the <see cref="NullableObject{T}"/> to a nullable value of type <typeparamref name="T"/>.
|
||||||
|
/// </summary>
|
||||||
public static implicit operator T?(NullableObject<T> parNullableObject)
|
public static implicit operator T?(NullableObject<T> parNullableObject)
|
||||||
{
|
{
|
||||||
return parNullableObject.Value;
|
return parNullableObject.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implicitly converts a nullable value of type <typeparamref name="T"/> to a <see cref="NullableObject{T}"/>.
|
||||||
|
/// </summary>
|
||||||
public static implicit operator NullableObject<T>(T? parValue)
|
public static implicit operator NullableObject<T>(T? parValue)
|
||||||
{
|
{
|
||||||
return new NullableObject<T>(parValue);
|
return new NullableObject<T>(parValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return Value?.ToString() ?? "null";
|
return Value?.ToString() ?? "null";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override bool Equals(object? parObj)
|
public override bool Equals(object? parObj)
|
||||||
{
|
{
|
||||||
return parObj is NullableObject<T> other && Equals(other);
|
return parObj is NullableObject<T> other && Equals(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
if (IsNull)
|
if (IsNull)
|
||||||
@@ -47,16 +79,23 @@ public readonly struct NullableObject<T>(T? parValue) : IEquatable<NullableObjec
|
|||||||
return hashCode;
|
return hashCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public bool Equals(NullableObject<T> parOther)
|
public bool Equals(NullableObject<T> parOther)
|
||||||
{
|
{
|
||||||
return EqualityComparer<T?>.Default.Equals(Value, parOther.Value);
|
return EqualityComparer<T?>.Default.Equals(Value, parOther.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether two <see cref="NullableObject{T}"/> instances are equal.
|
||||||
|
/// </summary>
|
||||||
public static bool operator ==(NullableObject<T> parLeft, NullableObject<T> parRight)
|
public static bool operator ==(NullableObject<T> parLeft, NullableObject<T> parRight)
|
||||||
{
|
{
|
||||||
return parLeft.Equals(parRight);
|
return parLeft.Equals(parRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether two <see cref="NullableObject{T}"/> instances are not equal.
|
||||||
|
/// </summary>
|
||||||
public static bool operator !=(NullableObject<T> parLeft, NullableObject<T> parRight)
|
public static bool operator !=(NullableObject<T> parLeft, NullableObject<T> parRight)
|
||||||
{
|
{
|
||||||
return !(parLeft == parRight);
|
return !(parLeft == parRight);
|
||||||
|
|||||||
@@ -1,12 +1,27 @@
|
|||||||
namespace Engine.Util;
|
using Engine.Scene;
|
||||||
|
|
||||||
public class TickableTimer
|
namespace Engine.Util;
|
||||||
|
|
||||||
|
public class TickableTimer : IUpdate
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Event that is invoked on every update, passing the current time.
|
||||||
|
/// </summary>
|
||||||
public event Action<double>? OnUpdate;
|
public event Action<double>? OnUpdate;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event that is invoked when the timer reaches zero and is finished.
|
||||||
|
/// </summary>
|
||||||
public event Action? OnFinished;
|
public event Action? OnFinished;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the timer has finished.
|
||||||
|
/// </summary>
|
||||||
public bool IsFinished => _currentTime <= 0;
|
public bool IsFinished => _currentTime <= 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current time remaining on the timer.
|
||||||
|
/// </summary>
|
||||||
public double CurrentTime
|
public double CurrentTime
|
||||||
{
|
{
|
||||||
get => _currentTime;
|
get => _currentTime;
|
||||||
@@ -37,6 +52,9 @@ public class TickableTimer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The total duration of the timer.
|
||||||
|
/// </summary>
|
||||||
public double TotalTime
|
public double TotalTime
|
||||||
{
|
{
|
||||||
get => _totalTime;
|
get => _totalTime;
|
||||||
@@ -52,9 +70,20 @@ public class TickableTimer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current time remaining on the timer.
|
||||||
|
/// </summary>
|
||||||
private double _currentTime;
|
private double _currentTime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The total duration of the timer.
|
||||||
|
/// </summary>
|
||||||
private double _totalTime;
|
private double _totalTime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new <see cref="TickableTimer"/> with a specified total time.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parTotalTime">The total duration of the timer.</param>
|
||||||
public TickableTimer(double parTotalTime)
|
public TickableTimer(double parTotalTime)
|
||||||
{
|
{
|
||||||
if (parTotalTime <= 0)
|
if (parTotalTime <= 0)
|
||||||
@@ -66,11 +95,15 @@ public class TickableTimer
|
|||||||
_currentTime = parTotalTime;
|
_currentTime = parTotalTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void Update(double parDeltaTime)
|
public void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
CurrentTime -= parDeltaTime;
|
CurrentTime -= parDeltaTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resets the timer back to its total duration.
|
||||||
|
/// </summary>
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
CurrentTime = TotalTime;
|
CurrentTime = TotalTime;
|
||||||
|
|||||||
@@ -7,19 +7,44 @@ using OpenTK.Windowing.GraphicsLibraryFramework;
|
|||||||
|
|
||||||
namespace Engine;
|
namespace Engine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a window used for rendering and interaction with the user.
|
||||||
|
/// </summary>
|
||||||
public class Window : IPresenter
|
public class Window : IPresenter
|
||||||
{
|
{
|
||||||
public event Action<ResizeEventArgs>? Resize;
|
/// <inheritdoc/>
|
||||||
|
public event Action<ResizeEventArgs>? OnResize;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public int Width { get; private set; }
|
public int Width { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public int Height { get; private set; }
|
public int Height { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public bool IsExiting => NativeWindow.IsExiting;
|
public bool IsExiting => NativeWindow.IsExiting;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The internal native window instance used for rendering and event handling.
|
||||||
|
/// </summary>
|
||||||
internal NativeWindow NativeWindow { get; }
|
internal NativeWindow NativeWindow { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The reference to the engine instance associated with this window.
|
||||||
|
/// </summary>
|
||||||
private readonly Engine _engine;
|
private readonly Engine _engine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the window is running in headless mode.
|
||||||
|
/// </summary>
|
||||||
private readonly bool _headless;
|
private readonly bool _headless;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Window"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parEngine">The engine instance managing this window.</param>
|
||||||
|
/// <param name="parWindow">The native window instance.</param>
|
||||||
|
/// <param name="parHeadless">Indicates whether the window is headless.</param>
|
||||||
public Window(Engine parEngine, NativeWindow parWindow, bool parHeadless)
|
public Window(Engine parEngine, NativeWindow parWindow, bool parHeadless)
|
||||||
{
|
{
|
||||||
_engine = parEngine;
|
_engine = parEngine;
|
||||||
@@ -33,16 +58,18 @@ public class Window : IPresenter
|
|||||||
{
|
{
|
||||||
Width = parArgs.Width;
|
Width = parArgs.Width;
|
||||||
Height = parArgs.Height;
|
Height = parArgs.Height;
|
||||||
Resize?.Invoke(parArgs);
|
OnResize?.Invoke(parArgs);
|
||||||
};
|
};
|
||||||
|
|
||||||
NativeWindow.VSync = VSyncMode.On;
|
NativeWindow.VSync = VSyncMode.On;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void Update(double parDeltaTime)
|
public void Update(double parDeltaTime)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void Render()
|
public void Render()
|
||||||
{
|
{
|
||||||
if (_headless)
|
if (_headless)
|
||||||
@@ -55,6 +82,7 @@ public class Window : IPresenter
|
|||||||
NativeWindow.SwapBuffers();
|
NativeWindow.SwapBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void Present(IConstTexture parTexture)
|
public void Present(IConstTexture parTexture)
|
||||||
{
|
{
|
||||||
if (_headless)
|
if (_headless)
|
||||||
@@ -70,14 +98,22 @@ public class Window : IPresenter
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void Exit()
|
public void Exit()
|
||||||
{
|
{
|
||||||
NativeWindow.Close();
|
NativeWindow.Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for the <see cref="NativeWindow"/> class.
|
||||||
|
/// </summary>
|
||||||
public static class NativeWindowExtensions
|
public static class NativeWindowExtensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Swaps the buffers of the specified native window.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parWindow">The native window instance.</param>
|
||||||
public static unsafe void SwapBuffers(this NativeWindow parWindow)
|
public static unsafe void SwapBuffers(this NativeWindow parWindow)
|
||||||
{
|
{
|
||||||
GLFW.SwapBuffers(parWindow.WindowPtr);
|
GLFW.SwapBuffers(parWindow.WindowPtr);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace PresenterConsole;
|
|||||||
|
|
||||||
public class ConsolePresenter : IPresenter
|
public class ConsolePresenter : IPresenter
|
||||||
{
|
{
|
||||||
public event Action<ResizeEventArgs>? Resize;
|
public event Action<ResizeEventArgs>? OnResize;
|
||||||
|
|
||||||
public int Width { get; private set; } = 2;
|
public int Width { get; private set; } = 2;
|
||||||
public int Height { get; private set; } = 1;
|
public int Height { get; private set; } = 1;
|
||||||
@@ -104,7 +104,7 @@ public class ConsolePresenter : IPresenter
|
|||||||
Width = consoleWidth;
|
Width = consoleWidth;
|
||||||
Height = consoleHeight;
|
Height = consoleHeight;
|
||||||
|
|
||||||
Resize?.Invoke(new ResizeEventArgs(Width / 2 * 4, Height * 4));
|
OnResize?.Invoke(new ResizeEventArgs(Width / 2 * 4, Height * 4));
|
||||||
|
|
||||||
_framebuffer.Resize(Width / 2, Height);
|
_framebuffer.Resize(Width / 2, Height);
|
||||||
_consoleOutput.Resize(Width, Height);
|
_consoleOutput.Resize(Width, Height);
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ public partial class App : Application
|
|||||||
|
|
||||||
private class PresenterWrapper : IPresenter
|
private class PresenterWrapper : IPresenter
|
||||||
{
|
{
|
||||||
public event Action<ResizeEventArgs>? Resize;
|
public event Action<ResizeEventArgs>? OnResize;
|
||||||
|
|
||||||
public IPresenter? Presenter
|
public IPresenter? Presenter
|
||||||
{
|
{
|
||||||
@@ -100,12 +100,12 @@ public partial class App : Application
|
|||||||
{
|
{
|
||||||
if (_presenter != null)
|
if (_presenter != null)
|
||||||
{
|
{
|
||||||
_presenter.Resize -= PresenterResize;
|
_presenter.OnResize -= PresenterResize;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value != null)
|
if (value != null)
|
||||||
{
|
{
|
||||||
value.Resize += PresenterResize;
|
value.OnResize += PresenterResize;
|
||||||
}
|
}
|
||||||
|
|
||||||
_presenter = value;
|
_presenter = value;
|
||||||
@@ -140,7 +140,7 @@ public partial class App : Application
|
|||||||
|
|
||||||
private void PresenterResize(ResizeEventArgs e)
|
private void PresenterResize(ResizeEventArgs e)
|
||||||
{
|
{
|
||||||
Resize?.Invoke(e);
|
OnResize?.Invoke(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,7 @@ namespace PresenterWpf;
|
|||||||
|
|
||||||
public partial class MainWindow : Window, IPresenter
|
public partial class MainWindow : Window, IPresenter
|
||||||
{
|
{
|
||||||
public event Action<ResizeEventArgs>? Resize;
|
public event Action<ResizeEventArgs>? OnResize;
|
||||||
|
|
||||||
public new int Width { get; private set; }
|
public new int Width { get; private set; }
|
||||||
public new int Height { get; private set; }
|
public new int Height { get; private set; }
|
||||||
@@ -46,7 +46,7 @@ public partial class MainWindow : Window, IPresenter
|
|||||||
if (_scheduledResize)
|
if (_scheduledResize)
|
||||||
{
|
{
|
||||||
_scheduledResize = false;
|
_scheduledResize = false;
|
||||||
Resize?.Invoke(new ResizeEventArgs(Width, Height));
|
OnResize?.Invoke(new ResizeEventArgs(Width, Height));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user