475 lines
19 KiB
C#
475 lines
19 KiB
C#
using System.Drawing;
|
|
using System.Reflection.Emit;
|
|
using System.Runtime.InteropServices;
|
|
using Dashboard.Drawing;
|
|
using OpenTK.Graphics.OpenGL;
|
|
using OpenTK.Mathematics;
|
|
using UniformType = Dashboard.Drawing.UniformType;
|
|
using Vector2 = System.Numerics.Vector2;
|
|
|
|
namespace Dashboard.OpenGL.Drawing
|
|
{
|
|
public static class GLStateExtensions
|
|
{
|
|
extension(VertexAttributeType type)
|
|
{
|
|
public All OpenGL => type switch
|
|
{
|
|
VertexAttributeType.UnsignedByte => All.UnsignedByte,
|
|
VertexAttributeType.UnsignedShort => All.UnsignedShort,
|
|
VertexAttributeType.UnsignedInt => All.UnsignedInt,
|
|
VertexAttributeType.Byte => All.Byte,
|
|
VertexAttributeType.Short => All.Short,
|
|
VertexAttributeType.Int => All.Int,
|
|
VertexAttributeType.Half => All.HalfFloat,
|
|
VertexAttributeType.Float => All.Float,
|
|
VertexAttributeType.Double => All.Double,
|
|
_ => throw new ArgumentOutOfRangeException(),
|
|
};
|
|
|
|
public VertexAttribType VertexAttribType => (VertexAttribType)type.OpenGL;
|
|
public VertexAttribPointerType VertexAttribPointerType => (VertexAttribPointerType)type.OpenGL;
|
|
}
|
|
|
|
extension(TextureType type)
|
|
{
|
|
public TextureTarget OpenGL => type switch
|
|
{
|
|
TextureType.Texture1D => TextureTarget.Texture1D,
|
|
TextureType.Texture2D => TextureTarget.Texture2D,
|
|
TextureType.Texture2DArray => TextureTarget.Texture2DArray,
|
|
TextureType.Texture2DCube => TextureTarget.TextureCubeMap,
|
|
TextureType.Texture3D => TextureTarget.Texture3D,
|
|
_ => throw new ArgumentOutOfRangeException(),
|
|
};
|
|
}
|
|
|
|
extension(BlendEquation eq)
|
|
{
|
|
public BlendEquationMode OpenGL => eq switch
|
|
{
|
|
BlendEquation.Add => BlendEquationMode.FuncAdd,
|
|
BlendEquation.Subtract => BlendEquationMode.FuncSubtract,
|
|
BlendEquation.Max => BlendEquationMode.Max,
|
|
BlendEquation.Min => BlendEquationMode.Min,
|
|
BlendEquation.ReverseSubtract => BlendEquationMode.FuncReverseSubtract,
|
|
_ => throw new ArgumentOutOfRangeException(),
|
|
};
|
|
}
|
|
|
|
extension(BlendFunction mode)
|
|
{
|
|
public BlendingFactor OpenGL => mode switch
|
|
{
|
|
BlendFunction.One => BlendingFactor.One,
|
|
BlendFunction.Zero => BlendingFactor.Zero,
|
|
BlendFunction.SourceAlpha => BlendingFactor.SrcAlpha,
|
|
BlendFunction.OneMinusSourceAlpha => BlendingFactor.OneMinusSrcAlpha,
|
|
BlendFunction.DestinationAlpha => BlendingFactor.DstAlpha,
|
|
BlendFunction.OneMinusDestinationAlpha => BlendingFactor.OneMinusDstAlpha,
|
|
BlendFunction.SourceColor => BlendingFactor.SrcColor,
|
|
BlendFunction.OneMinusSourceColor => BlendingFactor.OneMinusSrcColor,
|
|
BlendFunction.DestinationColor => BlendingFactor.DstColor,
|
|
BlendFunction.OneMinusDestinationColor => BlendingFactor.OneMinusDstColor,
|
|
BlendFunction.ConstantColor => BlendingFactor.OneMinusConstantColor,
|
|
BlendFunction.OneMinusConstantColor => BlendingFactor.OneMinusConstantColor,
|
|
BlendFunction.ConstantAlpha => BlendingFactor.OneMinusConstantAlpha,
|
|
BlendFunction.OneMinusConstantAlpha => BlendingFactor.OneMinusConstantAlpha,
|
|
_ => throw new ArgumentOutOfRangeException(),
|
|
};
|
|
}
|
|
|
|
extension(TestFunction test)
|
|
{
|
|
public All OpenGL => test switch
|
|
{
|
|
TestFunction.Always => All.Always,
|
|
TestFunction.Equal => All.Equal,
|
|
TestFunction.Greater => All.Greater,
|
|
TestFunction.GreaterThanOrEqual => All.Gequal,
|
|
TestFunction.LessThan => All.Less,
|
|
TestFunction.LessThanOrEqual => All.Lequal,
|
|
TestFunction.Never => All.Never,
|
|
TestFunction.NotEqual => All.Notequal,
|
|
_ => throw new ArgumentOutOfRangeException(),
|
|
};
|
|
|
|
public DepthFunction DepthFunction => (DepthFunction)test.OpenGL;
|
|
public StencilFunction StencilFunction => (StencilFunction)test.OpenGL;
|
|
}
|
|
|
|
extension(StencilOperation op)
|
|
{
|
|
public StencilOp OpenGL => op switch
|
|
{
|
|
StencilOperation.Keep => StencilOp.Keep,
|
|
StencilOperation.Zero => StencilOp.Zero,
|
|
StencilOperation.Replace => StencilOp.Replace,
|
|
StencilOperation.Increment => StencilOp.Incr,
|
|
StencilOperation.UncheckedIncrement => StencilOp.IncrWrap,
|
|
StencilOperation.Decrement => StencilOp.Decr,
|
|
StencilOperation.UncheckedDecrement => StencilOp.DecrWrap,
|
|
StencilOperation.Invert => StencilOp.Invert,
|
|
_ => throw new ArgumentOutOfRangeException(),
|
|
};
|
|
}
|
|
|
|
extension(MeshPrimitive primitive)
|
|
{
|
|
public PrimitiveType OpenGL => primitive switch
|
|
{
|
|
MeshPrimitive.Line => PrimitiveType.Lines,
|
|
MeshPrimitive.Point => PrimitiveType.Points,
|
|
MeshPrimitive.Triangle => PrimitiveType.Triangles,
|
|
MeshPrimitive.TriangleFan => PrimitiveType.TriangleFan,
|
|
MeshPrimitive.TriangleStrip => PrimitiveType.TriangleStrip,
|
|
_ => throw new ArgumentOutOfRangeException(),
|
|
};
|
|
}
|
|
|
|
extension(IndexType index)
|
|
{
|
|
public DrawElementsType OpenGL => index switch
|
|
{
|
|
IndexType.Int or IndexType.UnsignedInt => DrawElementsType.UnsignedInt,
|
|
IndexType.Short or IndexType.UnsignedShort => DrawElementsType.UnsignedShort,
|
|
IndexType.UnsignedLong => throw new NotSupportedException(),
|
|
_ => throw new ArgumentOutOfRangeException(),
|
|
};
|
|
}
|
|
|
|
extension(BlendMode mode)
|
|
{
|
|
public void SetEnabled()
|
|
{
|
|
if (mode.Enabled)
|
|
GL.Enable(EnableCap.Blend);
|
|
else
|
|
GL.Disable(EnableCap.Blend);
|
|
}
|
|
|
|
public void SetMode()
|
|
{
|
|
if (mode.IsSeparate)
|
|
{
|
|
GL.BlendEquationSeparate(mode.ColorEquation.OpenGL, mode.AlphaEquation.OpenGL);
|
|
GL.BlendFuncSeparate(
|
|
mode.Color.Source.OpenGL, mode.Color.Destination.OpenGL,
|
|
mode.Alpha.Source.OpenGL, mode.Alpha.Destination.OpenGL);
|
|
}
|
|
else
|
|
{
|
|
GL.BlendEquation(mode.UnifiedEquation.Value.OpenGL);
|
|
GL.BlendFunc(mode.Unified.Value.Source.OpenGL, mode.Unified.Value.Destination.OpenGL);
|
|
}
|
|
}
|
|
|
|
public void SetConstant()
|
|
{
|
|
GL.BlendColor(mode.Constant.R / 255f, mode.Constant.G / 255f,mode.Constant.B / 255f,mode.Constant.A / 255f);
|
|
}
|
|
|
|
public void SetAll()
|
|
{
|
|
mode.SetEnabled();
|
|
mode.SetConstant();
|
|
mode.SetMode();
|
|
}
|
|
}
|
|
|
|
extension(DepthMode mode)
|
|
{
|
|
public void SetEnabled()
|
|
{
|
|
if (mode.Test)
|
|
GL.Enable(EnableCap.DepthTest);
|
|
else
|
|
GL.Disable(EnableCap.DepthTest);
|
|
}
|
|
|
|
public void SetMask() => GL.DepthMask(mode.Mask);
|
|
|
|
public void SetFunction() => GL.DepthFunc(mode.Function.DepthFunction);
|
|
|
|
public void SetAll()
|
|
{
|
|
mode.SetEnabled();
|
|
mode.SetMask();
|
|
mode.SetFunction();
|
|
}
|
|
}
|
|
|
|
extension(StencilMode mode)
|
|
{
|
|
public void SetEnabled()
|
|
{
|
|
if (mode.Enabled)
|
|
GL.Enable(EnableCap.StencilTest);
|
|
else
|
|
GL.Disable(EnableCap.StencilTest);
|
|
}
|
|
|
|
public void SetFunction() => GL.StencilFunc(mode.Function.StencilFunction, mode.Reference, (uint)mode.Mask);
|
|
|
|
public void SetOperations() => GL.StencilOp(mode.Fail.OpenGL, mode.DepthFail.OpenGL, mode.Pass.OpenGL);
|
|
|
|
public void SetAll()
|
|
{
|
|
mode.SetEnabled();
|
|
mode.SetFunction();
|
|
mode.SetOperations();
|
|
}
|
|
}
|
|
|
|
extension(DrawCall call)
|
|
{
|
|
public void UsePipeline() => GL.UseProgram((call.ShaderPipeline as GLShader)?.Handle ?? 0);
|
|
|
|
// TODO: clamp to viewport size
|
|
|
|
public void SetViewport(Vector2 size)
|
|
{
|
|
Vector2 min = Vector2.Max(Vector2.Zero, call.ViewportRegion.Min);
|
|
Vector2 max = Vector2.Min(size, call.ViewportRegion.Max);
|
|
|
|
GL.Viewport(
|
|
(int)Math.Round(min.X),
|
|
(int)Math.Round(size.Y - max.Y),
|
|
(int)Math.Round(max.X - min.X),
|
|
(int)Math.Round(max.Y - min.Y));
|
|
}
|
|
|
|
public void SetScissor(Vector2 size)
|
|
{
|
|
if (call.ScissorRegion == new Box2d(Vector2.PositiveInfinity, Vector2.PositiveInfinity))
|
|
{
|
|
GL.Disable(EnableCap.ScissorTest);
|
|
return;
|
|
}
|
|
|
|
GL.Enable(EnableCap.ScissorTest);
|
|
GL.Scissor(
|
|
(int)Math.Round(call.ViewportRegion.Left),
|
|
(int)Math.Round(size.Y - call.ViewportRegion.Top),
|
|
(int)Math.Round(call.ViewportRegion.Right - call.ViewportRegion.Left),
|
|
(int)Math.Round(call.ViewportRegion.Bottom - call.ViewportRegion.Top));
|
|
}
|
|
|
|
public void SetFrontFace()
|
|
{
|
|
GL.FrontFace(call.FrontFace switch
|
|
{
|
|
WindingOrder.Clockwise => FrontFaceDirection.Cw,
|
|
WindingOrder.Counterclockwise => FrontFaceDirection.Ccw,
|
|
_ => throw new ArgumentOutOfRangeException(),
|
|
});
|
|
}
|
|
|
|
public void SetCullMode()
|
|
{
|
|
if (call.CullMode == FaceCulling.None)
|
|
{
|
|
GL.Disable(EnableCap.CullFace);
|
|
return;
|
|
}
|
|
|
|
GL.Enable(EnableCap.CullFace);
|
|
|
|
GL.CullFace(call.CullMode switch
|
|
{
|
|
FaceCulling.Both => TriangleFace.FrontAndBack,
|
|
FaceCulling.Front => TriangleFace.Front,
|
|
FaceCulling.Back => TriangleFace.Back,
|
|
_ => throw new ArgumentOutOfRangeException(),
|
|
});
|
|
}
|
|
|
|
public void SetBlendMode() => call.BlendMode.SetAll();
|
|
public void SetDepthMode() => call.DepthMode.SetAll();
|
|
public void SetStencilMode() => call.StencilMode.SetAll();
|
|
|
|
public void SetPrimitiveRestart()
|
|
{
|
|
if (!call.EnableRestart)
|
|
{
|
|
GL.Disable(EnableCap.PrimitiveRestart);
|
|
return;
|
|
}
|
|
|
|
GL.Enable(EnableCap.PrimitiveRestart);
|
|
GL.PrimitiveRestartIndex((uint)call.RestartIndex);
|
|
}
|
|
|
|
public void SetColorMask()
|
|
{
|
|
GL.ColorMask(call.RedMask, call.GreenMask, call.BlueMask, call.AlphaMask);
|
|
}
|
|
|
|
public void SetPointSize()
|
|
{
|
|
GL.PointSize(call.PointSize);
|
|
}
|
|
|
|
public void SetLineWidth()
|
|
{
|
|
GL.LineWidth(call.LineWidth);
|
|
}
|
|
|
|
public void UseTextures()
|
|
{
|
|
int i = 0;
|
|
foreach (ITexture? texture in call.Textures)
|
|
{
|
|
GL.ActiveTexture((TextureUnit)((int)TextureUnit.Texture0 + i));
|
|
if (texture is GLTexture glTexture)
|
|
{
|
|
GL.BindTexture(glTexture.Type.OpenGL, glTexture.Handle);
|
|
}
|
|
else
|
|
{
|
|
GL.BindTexture(TextureTarget.Texture2D, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void UpdateVertexArrays(int vao)
|
|
{
|
|
GL.BindVertexArray(vao);
|
|
|
|
if (call.ElementBuffer != null)
|
|
{
|
|
GL.BindBuffer(BufferTarget.ElementArrayBuffer, (call.ElementBuffer as GLBuffer)?.Handle ?? 0);
|
|
}
|
|
|
|
GL.BindBuffer(BufferTarget.ArrayBuffer, (call.VertexBuffer as GLBuffer)?.Handle ?? 0);
|
|
|
|
foreach(VertexAttribute attrib in call.Attributes)
|
|
{
|
|
GL.VertexAttribPointer(
|
|
(uint)attrib.Location,
|
|
attrib.Components,
|
|
attrib.Type.VertexAttribPointerType,
|
|
false,
|
|
(int)attrib.Stride,
|
|
(nint)attrib.Offset);
|
|
GL.EnableVertexAttribArray((uint)attrib.Location);
|
|
}
|
|
}
|
|
|
|
public void UpdateUniforms(bool transpose = false)
|
|
{
|
|
int bufferIndex = 0;
|
|
foreach (UniformDescriptor uniform in call.Uniforms)
|
|
{
|
|
ReadOnlySpan<byte> uniformData = call.UniformData.Span;
|
|
|
|
switch (uniform.Type)
|
|
{
|
|
case UniformType.I1:
|
|
{
|
|
ReadOnlySpan<int> span = CastSpan<int>(uniformData, uniform.Offset, uniform.Size);
|
|
GL.Uniform1i(uniform.Location, span.Length, span);
|
|
break;
|
|
}
|
|
case UniformType.I2:
|
|
{
|
|
ReadOnlySpan<Vector2i> span = CastSpan<Vector2i>(uniformData, uniform.Offset, uniform.Size);
|
|
GL.Uniform2i(uniform.Location, span.Length, span);
|
|
break;
|
|
}
|
|
case UniformType.I3:
|
|
{
|
|
ReadOnlySpan<Vector3i> span = CastSpan<Vector3i>(uniformData, uniform.Offset, uniform.Size);
|
|
GL.Uniform3i(uniform.Location, span.Length, span);
|
|
break;
|
|
}
|
|
case UniformType.I4:
|
|
{
|
|
ReadOnlySpan<Vector4i> span = CastSpan<Vector4i>(uniformData, uniform.Offset, uniform.Size);
|
|
GL.Uniform4i(uniform.Location, span.Length, span);
|
|
break;
|
|
}
|
|
case UniformType.F1:
|
|
{
|
|
ReadOnlySpan<float> span = CastSpan<float>(uniformData, uniform.Offset, uniform.Size);
|
|
GL.Uniform1f(uniform.Location, span.Length, span);
|
|
break;
|
|
}
|
|
case UniformType.F2:
|
|
{
|
|
ReadOnlySpan<Vector2> span = CastSpan<Vector2>(uniformData, uniform.Offset, uniform.Size);
|
|
GL.Uniform2f(uniform.Location, span.Length, span);
|
|
break;
|
|
}
|
|
case UniformType.F3:
|
|
{
|
|
ReadOnlySpan<Vector3> span = CastSpan<Vector3>(uniformData, uniform.Offset, uniform.Size);
|
|
GL.Uniform3f(uniform.Location, span.Length, span);
|
|
break;
|
|
}
|
|
case UniformType.F4:
|
|
{
|
|
ReadOnlySpan<Vector4> span = CastSpan<Vector4>(uniformData, uniform.Offset, uniform.Size);
|
|
GL.Uniform4f(uniform.Location, span.Length, span);
|
|
break;
|
|
}
|
|
case UniformType.Mat2:
|
|
{
|
|
ReadOnlySpan<Matrix2> span = CastSpan<Matrix2>(uniformData, uniform.Offset, uniform.Size);
|
|
GL.UniformMatrix2f(uniform.Location, span.Length, transpose, span);
|
|
break;
|
|
}
|
|
case UniformType.Mat3:
|
|
{
|
|
ReadOnlySpan<Matrix3> span = CastSpan<Matrix3>(uniformData, uniform.Offset, uniform.Size);
|
|
GL.UniformMatrix3f(uniform.Location, span.Length, transpose, span);
|
|
break;
|
|
}
|
|
case UniformType.Mat4:
|
|
{
|
|
ReadOnlySpan<Matrix4> span = CastSpan<Matrix4>(uniformData, uniform.Offset, uniform.Size);
|
|
GL.UniformMatrix4f(uniform.Location, span.Length, transpose, span);
|
|
break;
|
|
}
|
|
case UniformType.Buffer:
|
|
{
|
|
int buffer = (call.UniformBuffer as GLBuffer)?.Handle ?? 0;
|
|
int index = bufferIndex++;
|
|
GL.BindBufferRange(BufferTarget.UniformBuffer, (uint)index, buffer, (nint)uniform.Offset, (int)uniform.Size);
|
|
break;
|
|
}
|
|
default: throw new NotSupportedException();
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
ReadOnlySpan<T> CastSpan<T>(ReadOnlySpan<byte> bytes, long offset, long size)
|
|
where T : unmanaged
|
|
{
|
|
return MemoryMarshal.Cast<byte, T>(bytes.Slice((int)offset, (int)size));
|
|
}
|
|
}
|
|
|
|
public void SetAll(Vector2 size, int vao, bool transpose = false)
|
|
{
|
|
call.UsePipeline();
|
|
call.SetViewport(size); call.SetScissor(size);
|
|
call.SetFrontFace();
|
|
call.SetCullMode();
|
|
call.SetBlendMode();
|
|
call.SetDepthMode();
|
|
call.SetStencilMode();
|
|
call.SetPrimitiveRestart();
|
|
call.SetColorMask();
|
|
call.SetDepthMode();
|
|
call.SetPointSize();
|
|
call.SetLineWidth();
|
|
call.UseTextures();
|
|
call.UpdateUniforms(transpose);
|
|
call.UpdateVertexArrays(vao);
|
|
}
|
|
}
|
|
}
|
|
}
|