using Engine.Graphics.Shader;
using Engine.Graphics.Texture;
using OpenTK.Graphics.OpenGL;
using OpenTK.Mathematics;
namespace Engine.Graphics.Render.Mesh;
///
/// A renderer class for rendering meshes using instancing.
/// Handles the instancing process for meshes, including texture binding and model transformations.
///
public class MeshRenderer : InstancedRenderer
{
///
/// Maps textures to texture units with a limit of 16 texture units.
///
private readonly TextureUnitMap _textureUnitMap = new(16);
///
/// Stores the texture unit indices used for binding textures.
///
private readonly int[] _textureUnitIndices = new int[16];
///
/// Stores the hash of the current frame, used for detecting changes in instance data.
///
private int _frameHash;
///
/// Stores the hash of the previous frame.
///
private int _previousHash;
///
/// Initializes a new instance of the class.
///
/// The mesh to render.
/// The number of instances to render.
/// The shader program to use for rendering the mesh.
public MeshRenderer(Asset.Mesh.Mesh parMesh, int parInstanceCount, Program parProgram)
: base(
PrimitiveType.Triangles, parInstanceCount,
parMesh.Indices.ToArray(),
parMesh.Vertices.ToArray(),
parProgram
)
{
}
///
/// Commits an instance to the renderer, adding it to the queue with the specified model matrix and optional texture.
///
/// The model transformation matrix for this instance.
/// An optional texture to apply to the mesh. If null, no texture is applied.
public void Commit(Matrix4 parModelMatrix, Texture.Texture? parTexture = null)
{
if (_queuedInstanceCount >= _instanceCount)
{
throw new InvalidOperationException("Instance count exceeded");
}
var textureId = -1;
if (parTexture != null)
{
textureId = _textureUnitMap.GetUnit(parTexture);
}
_instanceVertices[_queuedInstanceCount]._textureId = textureId;
_instanceVertices[_queuedInstanceCount]._modelMatrix = parModelMatrix;
_frameHash = HashCode.Combine(_frameHash, _instanceVertices[_queuedInstanceCount]);
_queuedInstanceCount++;
}
///
public override void Reset()
{
base.Reset();
_textureUnitMap.Reset();
_previousHash = _frameHash;
_frameHash = 0;
}
///
protected override void SetAdditionalUniforms(Program parProgram)
{
foreach (var (texture, unit) in _textureUnitMap.Textures)
{
texture.BindUnit(unit);
_textureUnitIndices[unit] = unit;
}
parProgram.SetUniform("uTexture", _textureUnitIndices);
}
///
protected override bool DataChanged()
{
return _frameHash != _previousHash;
}
}