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