Files
Dashboard/Dashboard.OpenGL/Drawing/ImmediateMode.cs
T

214 lines
10 KiB
C#

using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Numerics;
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using Dashboard.Drawing;
using Dashboard.Pal;
using OpenTK.Graphics.OpenGL;
namespace Dashboard.OpenGL.Drawing
{
public class ImmediateMode : IImmediateMode
{
public string DriverName => "Dashboard OpenGL Immediate Mode";
public string DriverVendor => "Dashboard";
public Version DriverVersion { get; } = new Version(1, 0);
public DeviceContext Context { get; private set; } = null!;
private IDirectRendering _dr;
private GLShader _program;
private uint _program_apos;
private uint _program_atexcoord;
private uint _program_acolor;
private int _program_transforms;
private int _program_image;
private GLTexture _white;
public void Dispose()
{
}
public void Require(DeviceContext context)
{
Context = context;
_dr = context.ExtensionRequire<IDirectRendering>();
GlslShaderCreateInfo shader;
using (StreamReader vertex = new StreamReader(GetType().Assembly
.GetManifestResourceStream("Dashboard.OpenGL.Drawing.immediate.vert")!))
using (StreamReader fragment = new StreamReader(GetType().Assembly
.GetManifestResourceStream("Dashboard.OpenGL.Drawing.immediate.frag")!))
{
shader = new GlslShaderCreateInfo()
{
VertexShader = vertex.ReadToEnd(),
FragmentShader = fragment.ReadToEnd(),
};
}
_program = (GLShader)_dr.CreateShader(shader);
_program_apos = (uint)GL.GetAttribLocation(_program.Handle, "aPos");
_program_atexcoord = (uint)GL.GetAttribLocation(_program.Handle, "aTexCoords");
_program_acolor = (uint)GL.GetAttribLocation(_program.Handle, "aColor");
_program_transforms = GL.GetUniformLocation(_program.Handle, "transforms");
_program_image = GL.GetUniformLocation(_program.Handle, "image");
_white = (GLTexture)_dr.CreateTexture(TextureType.Texture2D);
_white.SetStorage(PixelFormat.Rgb8I, 1, 1, 1, 1);
GL.BindTexture(TextureTarget.Texture2D, _white.Handle);
GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, 1, 1, OpenTK.Graphics.OpenGL.PixelFormat.Rgb, PixelType.Byte, stackalloc byte[] { 0xFF, 0xFF, 0xFF, 0xFF });
GL.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleA, (int)All.One);
GL.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleR, (int)All.One);
GL.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleG, (int)All.One);
GL.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleB, (int)All.One);
GL.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
GL.TexParameteri(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
}
public void ClearColor(Color color)
{
GL.ClearColor(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f);
GL.Clear(ClearBufferMask.ColorBufferBit);
}
public void Line(Vector2 a, Vector2 b, float width, float depth, Vector4 color)
{
Vector2 normal = Vector2.Normalize(b - a);
Vector2 tangent = new Vector2(-normal.Y, normal.X) * width;
Span<ImmediateVertex> vertices =
[
new ImmediateVertex(new Vector3(a-tangent, depth), Vector2.Zero, color),
new ImmediateVertex(new Vector3(b-tangent, depth), Vector2.Zero, color),
new ImmediateVertex(new Vector3(b+tangent, depth), Vector2.Zero, color),
new ImmediateVertex(new Vector3(a-tangent, depth), Vector2.Zero, color),
new ImmediateVertex(new Vector3(b+tangent, depth), Vector2.Zero, color),
new ImmediateVertex(new Vector3(a+tangent, depth), Vector2.Zero, color),
];
DrawImmediate(new ImmediateDrawCall(MeshPrimitive.Triangle, vertices.ToArray())
{
Transforms = Context.ExtensionRequire<IDeviceContextBase>().Transforms,
PipelineState = new PipelineState { CullMode = FaceCulling.None },
});
}
public void Rectangle(Box2d rectangle, float depth, Vector4 color)
{
Span<ImmediateVertex> vertices =
[
new ImmediateVertex(new Vector3(rectangle.Min.X, rectangle.Min.Y, depth), Vector2.Zero, color),
new ImmediateVertex(new Vector3(rectangle.Max.X, rectangle.Min.Y, depth), Vector2.Zero, color),
new ImmediateVertex(new Vector3(rectangle.Max.X, rectangle.Max.Y, depth), Vector2.Zero, color),
new ImmediateVertex(new Vector3(rectangle.Min.X, rectangle.Min.Y, depth), Vector2.Zero, color),
new ImmediateVertex(new Vector3(rectangle.Max.X, rectangle.Max.Y, depth), Vector2.Zero, color),
new ImmediateVertex(new Vector3(rectangle.Min.X, rectangle.Max.Y, depth), Vector2.Zero, color),
];
DrawImmediate(new ImmediateDrawCall(MeshPrimitive.Triangle, vertices.ToArray())
{
Transforms = Context.ExtensionRequire<IDeviceContextBase>().Transforms,
PipelineState = new PipelineState { CullMode = FaceCulling.None },
});
}
public void Rectangle(in RectangleDrawInfo rectangle)
{
// TODO: implement this better.
int z = Context.ExtensionRequire<IDeviceContextBase>().IncrementZ();
Color color = (rectangle.Fill as SolidColorBrush)?.Color ?? Color.LightGray;
Vector4 colorV = new Vector4(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f);
Vector4 margin = rectangle.Box.Margin;
Vector4 border = rectangle.Box.Border;
Vector2 size = rectangle.Box.Size + new Vector2(border.X + border.Z, border.Y = border.W);
Box2d box = Box2d.FromPositionAndSize(rectangle.Position + new Vector2(margin.X + border.X, margin.Y + border.Y), size);
Rectangle(box, z, colorV);
}
public void Image(Box2d rectangle, Box2d uv, float depth, ITexture texture)
{
Span<ImmediateVertex> vertices =
[
new ImmediateVertex(new Vector3(rectangle.Min.X, rectangle.Min.Y, depth), new Vector2(uv.Min.X, uv.Min.Y), Vector4.One),
new ImmediateVertex(new Vector3(rectangle.Max.X, rectangle.Min.Y, depth), new Vector2(uv.Max.X, uv.Min.Y), Vector4.One),
new ImmediateVertex(new Vector3(rectangle.Max.X, rectangle.Max.Y, depth), new Vector2(uv.Max.X, uv.Max.Y), Vector4.One),
new ImmediateVertex(new Vector3(rectangle.Min.X, rectangle.Min.Y, depth), new Vector2(uv.Min.X, uv.Min.Y), Vector4.One),
new ImmediateVertex(new Vector3(rectangle.Max.X, rectangle.Max.Y, depth), new Vector2(uv.Max.X, uv.Max.Y), Vector4.One),
new ImmediateVertex(new Vector3(rectangle.Min.X, rectangle.Max.Y, depth), new Vector2(uv.Min.X, uv.Max.Y), Vector4.One),
];
DrawImmediate(new ImmediateDrawCall(MeshPrimitive.Triangle, vertices.ToArray())
{
Transforms = Context.ExtensionRequire<IDeviceContextBase>().Transforms,
PipelineState = new PipelineState { CullMode = FaceCulling.None },
Texture = texture,
});
}
IContextBase IContextExtensionBase.Context => Context;
void IContextExtensionBase.Require(IContextBase context)
{
Require((DeviceContext)context);
}
public void DrawImmediate(ImmediateDrawCall call)
{
// This is a terrible implementation as it stands but we can improve this immensely later on.
IDirectRendering dr = Context.ExtensionRequire<IDirectRendering>();
int size = call.Vertices.Length * ImmediateVertex.Size;
IBuffer buffer = dr.CreateBuffer(BufferAccessPattern.Stream, size);
buffer.Write(0, call.Vertices.Span);
VertexSpecification spec = CreateVertexSpec(buffer);
ReadOnlyMemory<byte> uniforms = MemoryMarshal.AsBytes(stackalloc Matrix4x4[] {call.Transforms}).ToArray();
DrawCall drawCall = new DrawCall(call.Primitive, spec, 0, call.Vertices.Length)
{
ShaderPipeline = _program,
PipelineState = call.PipelineState,
UniformData = uniforms,
Uniforms = [
new UniformDescriptor() {
Location = _program_transforms,
Type = Dashboard.Drawing.UniformType.Mat4,
Offset = 0,
Size = Unsafe.SizeOf<Matrix4x4>(),
}
],
Textures = [call.Texture ?? _white]
};
dr.Draw(drawCall);
buffer.Dispose();
}
private VertexSpecification CreateVertexSpec(IBuffer buffer)
{
return new VertexSpecification(
[
new VertexAttribute((int)_program_apos, 3, VertexAttributeType.Float, ImmediateVertex.PosOffset, ImmediateVertex.Size),
new VertexAttribute((int)_program_atexcoord, 2, VertexAttributeType.Float, ImmediateVertex.TexCoordsOffset, ImmediateVertex.Size),
new VertexAttribute((int)_program_acolor, 4, VertexAttributeType.Float, ImmediateVertex.ColorOffset, ImmediateVertex.Size)
],
[
buffer,
buffer,
buffer
]
);
}
}
}