333 lines
11 KiB
C#
333 lines
11 KiB
C#
using System.Drawing;
|
|
using OpenTK.Graphics.OpenGL;
|
|
using System.Numerics;
|
|
using OTK = OpenTK.Mathematics;
|
|
|
|
namespace Dashboard.Drawing.OpenGL.Executors
|
|
{
|
|
public class BaseCommandExecutor : IInitializer, ICommandExecutor
|
|
{
|
|
private int _program = 0;
|
|
private readonly MappableBumpAllocator<CommandInfo> _commands = new MappableBumpAllocator<CommandInfo>();
|
|
private readonly DrawCallRecorder _calls = new DrawCallRecorder();
|
|
|
|
public bool IsInitialized { get; private set; }
|
|
public IEnumerable<string> Extensions { get; } = new[] { "DB_base" };
|
|
public IContextExecutor Executor { get; private set; } = null!;
|
|
|
|
public void Initialize()
|
|
{
|
|
if (IsInitialized) return;
|
|
|
|
if (Executor == null)
|
|
throw new Exception("Executor has not been set.");
|
|
|
|
IsInitialized = true;
|
|
|
|
LoadShaders();
|
|
}
|
|
|
|
public void SetContextExecutor(IContextExecutor executor)
|
|
{
|
|
Executor = executor;
|
|
}
|
|
|
|
public void BeginFrame()
|
|
{
|
|
}
|
|
|
|
public void BeginDraw()
|
|
{
|
|
_commands.Initialize();
|
|
_calls.Initialize();
|
|
|
|
Size size = Executor.Context.FramebufferSize;
|
|
|
|
Executor.TransformStack.Push(OTK.Matrix4.CreateOrthographicOffCenter(
|
|
0,
|
|
size.Width,
|
|
size.Height,
|
|
0,
|
|
1,
|
|
-1));
|
|
|
|
GL.Viewport(0, 0, size.Width, size.Height);
|
|
}
|
|
|
|
public void EndDraw()
|
|
{
|
|
_commands.Unmap();
|
|
GL.UseProgram(_program);
|
|
_calls.CommandBuffer = _commands.Handle;
|
|
_calls.Execute();
|
|
}
|
|
|
|
public void EndFrame()
|
|
{
|
|
_commands.Clear();
|
|
_calls.Clear();
|
|
}
|
|
|
|
public void ProcessCommand(ICommandFrame frame)
|
|
{
|
|
switch (frame.Command.Name)
|
|
{
|
|
case "Point":
|
|
DrawBasePoint(frame);
|
|
break;
|
|
case "Line":
|
|
DrawBaseLine(frame);
|
|
break;
|
|
case "RectF":
|
|
case "RectS":
|
|
case "RectFS":
|
|
DrawRect(frame);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void DrawBasePoint(ICommandFrame frame)
|
|
{
|
|
ref CommandInfo info = ref _commands.Take(out int index);
|
|
|
|
PointCommandArgs args = frame.GetParameter<PointCommandArgs>();
|
|
|
|
info = new CommandInfo()
|
|
{
|
|
Type = SimpleDrawCommand.Point,
|
|
Arg0 = args.Size,
|
|
};
|
|
|
|
SetCommandCommonBrush(ref info, args.Brush, args.Brush);
|
|
|
|
_calls.Transforms(Executor.TransformStack.Top);
|
|
_calls.Begin(PrimitiveType.Triangles);
|
|
_calls.CommandIndex(index);
|
|
DrawPoint(args.Position, args.Depth, args.Size);
|
|
_calls.End();
|
|
}
|
|
|
|
private void DrawPoint(Vector2 position, float depth, float diameter)
|
|
{
|
|
// Draw a point as a isocles triangle.
|
|
const float adjust = 1.1f;
|
|
const float cos30 = 0.8660254038f;
|
|
Vector2 top = adjust * new Vector2(0, -cos30);
|
|
Vector2 left = adjust * new Vector2(-cos30, 0.5f);
|
|
Vector2 right = adjust * new Vector2(cos30, 0.5f);
|
|
|
|
_calls.TexCoords2(top);
|
|
_calls.Vertex3(new Vector3(position + top * diameter, depth));
|
|
_calls.TexCoords2(left);
|
|
_calls.Vertex3(new Vector3(position + left * diameter, depth));
|
|
_calls.TexCoords2(right);
|
|
_calls.Vertex3(new Vector3(position + right * diameter, depth));
|
|
}
|
|
|
|
private void DrawBaseLine(ICommandFrame frame)
|
|
{
|
|
ref CommandInfo info = ref _commands.Take(out int index);
|
|
|
|
LineCommandArgs args = frame.GetParameter<LineCommandArgs>();
|
|
|
|
info = new CommandInfo()
|
|
{
|
|
Type = SimpleDrawCommand.Line,
|
|
Arg0 = 0.5f * args.Size / (args.End - args.Start).Length(),
|
|
};
|
|
|
|
SetCommandCommonBrush(ref info, args.Brush, args.Brush);
|
|
|
|
_calls.Transforms(Executor.TransformStack.Top);
|
|
_calls.Begin(PrimitiveType.Triangles);
|
|
|
|
_calls.CommandIndex(index);
|
|
|
|
DrawLine(args.Start, args.End, args.Depth, args.Size);
|
|
|
|
_calls.End();
|
|
}
|
|
|
|
private void DrawLine(Vector2 start, Vector2 end, float depth, float width)
|
|
{
|
|
float radius = 0.5f * width;
|
|
Vector2 segment = end - start;
|
|
float length = segment.Length();
|
|
float ratio = radius / length;
|
|
Vector2 n = ratio * segment;
|
|
Vector2 t = new Vector2(-n.Y, n.X);
|
|
|
|
Vector2 t00 = new Vector2(-ratio, -ratio);
|
|
Vector2 t10 = new Vector2(1+ratio, -ratio);
|
|
Vector2 t01 = new Vector2(-ratio, +ratio);
|
|
Vector2 t11 = new Vector2(1+ratio, +ratio);
|
|
|
|
Vector3 x00 = new Vector3(start - n - t, depth);
|
|
Vector3 x10 = new Vector3(end + n - t, depth);
|
|
Vector3 x01 = new Vector3(start - n + t, depth);
|
|
Vector3 x11 = new Vector3(end + n + t, depth);
|
|
|
|
_calls.TexCoords2(t00);
|
|
_calls.Vertex3(x00);
|
|
_calls.TexCoords2(t01);
|
|
_calls.Vertex3(x01);
|
|
_calls.TexCoords2(t11);
|
|
_calls.Vertex3(x11);
|
|
|
|
_calls.TexCoords2(t00);
|
|
_calls.Vertex3(x00);
|
|
_calls.TexCoords2(t11);
|
|
_calls.Vertex3(x11);
|
|
_calls.TexCoords2(t10);
|
|
_calls.Vertex3(x10);
|
|
}
|
|
|
|
private void DrawRect(ICommandFrame frame)
|
|
{
|
|
ref CommandInfo info = ref _commands.Take(out int index);
|
|
|
|
RectCommandArgs args = frame.GetParameter<RectCommandArgs>();
|
|
|
|
Vector2 size = Vector2.Abs(args.End - args.Start);
|
|
float aspect = size.X / size.Y;
|
|
float border = args.StrikeSize;
|
|
float normRad = args.StrikeSize / size.Y;
|
|
float wideRad = aspect * normRad;
|
|
|
|
int flags = 0;
|
|
|
|
switch (frame.Command.Name)
|
|
{
|
|
case "RectF":
|
|
flags |= 1;
|
|
break;
|
|
case "RectS":
|
|
flags |= 2;
|
|
break;
|
|
case "RectFS":
|
|
flags |= 3;
|
|
break;
|
|
}
|
|
|
|
switch (args.BorderKind)
|
|
{
|
|
case BorderKind.Inset:
|
|
flags |= 2 << 2;
|
|
break;
|
|
case BorderKind.Outset:
|
|
flags |= 1 << 2;
|
|
break;
|
|
}
|
|
|
|
info = new CommandInfo()
|
|
{
|
|
Type = SimpleDrawCommand.Rect,
|
|
Flags = flags,
|
|
Arg0 = aspect,
|
|
Arg1 = normRad,
|
|
};
|
|
|
|
SetCommandCommonBrush(ref info, args.FillBrush, args.StrikeBrush);
|
|
|
|
_calls.Transforms(Executor.TransformStack.Top);
|
|
_calls.Begin(PrimitiveType.Triangles);
|
|
|
|
_calls.CommandIndex(index);
|
|
|
|
Vector2 t00 = new Vector2(-wideRad, -normRad);
|
|
Vector2 t10 = new Vector2(1+wideRad, -normRad);
|
|
Vector2 t01 = new Vector2(-wideRad, 1+normRad);
|
|
Vector2 t11 = new Vector2(1+wideRad, 1+normRad);
|
|
|
|
Vector3 x00 = new Vector3(args.Start.X - border, args.Start.Y - border, args.Depth);
|
|
Vector3 x10 = new Vector3(args.End.X + border, args.Start.Y - border, args.Depth);
|
|
Vector3 x01 = new Vector3(args.Start.X - border, args.End.Y + border, args.Depth);
|
|
Vector3 x11 = new Vector3(args.End.X + border, args.End.Y + border, args.Depth);
|
|
|
|
_calls.TexCoords2(t00);
|
|
_calls.Vertex3(x00);
|
|
_calls.TexCoords2(t01);
|
|
_calls.Vertex3(x01);
|
|
_calls.TexCoords2(t11);
|
|
_calls.Vertex3(x11);
|
|
|
|
_calls.TexCoords2(t00);
|
|
_calls.Vertex3(x00);
|
|
_calls.TexCoords2(t11);
|
|
_calls.Vertex3(x11);
|
|
_calls.TexCoords2(t10);
|
|
_calls.Vertex3(x10);
|
|
|
|
_calls.End();
|
|
}
|
|
|
|
protected void SetCommandCommonBrush(ref CommandInfo info, IBrush? fill, IBrush? border)
|
|
{
|
|
switch (fill?.Kind.Name)
|
|
{
|
|
case "DB_Brush_solid":
|
|
SolidBrush solid = (SolidBrush)fill;
|
|
Vector4 color = new Vector4(solid.Color.R/255f, solid.Color.G/255f, solid.Color.B/255f, solid.Color.A/255f);
|
|
info.FgColor = color;
|
|
break;
|
|
case "DB_Brush_gradient":
|
|
GradientBrush gradient = (GradientBrush)fill;
|
|
GradientUniformBuffer gradients = Executor.ResourcePool.GetResourceManager<GradientUniformBuffer>();
|
|
gradients.Initialize();
|
|
GradientUniformBuffer.Entry entry = gradients.InternGradient(gradient.Gradient);
|
|
info.FgGradientIndex = entry.Offset;
|
|
info.FgGradientCount = entry.Count;
|
|
break;
|
|
case null:
|
|
// Craete a magenta brush for this.
|
|
info.FgColor = new Vector4(1, 0, 1, 1);
|
|
break;
|
|
}
|
|
|
|
switch (border?.Kind.Name)
|
|
{
|
|
case "DB_Brush_solid":
|
|
SolidBrush solid = (SolidBrush)border;
|
|
Vector4 color = new Vector4(solid.Color.R/255f, solid.Color.G/255f, solid.Color.B/255f, solid.Color.A/255f);
|
|
info.BgColor = color;
|
|
break;
|
|
case "DB_Brush_gradient":
|
|
GradientBrush gradient = (GradientBrush)border;
|
|
GradientUniformBuffer gradients = Executor.ResourcePool.GetResourceManager<GradientUniformBuffer>();
|
|
gradients.Initialize();
|
|
GradientUniformBuffer.Entry entry = gradients.InternGradient(gradient.Gradient);
|
|
info.BgGradientIndex = entry.Offset;
|
|
info.BgGradientCount = entry.Count;
|
|
break;
|
|
case null:
|
|
// Craete a magenta brush for this.
|
|
info.BgColor = new Vector4(1, 0, 1, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void LoadShaders()
|
|
{
|
|
using Stream vsource = FetchEmbeddedResource("Dashboard.Drawing.OpenGL.Executors.simple.vert");
|
|
using Stream fsource = FetchEmbeddedResource("Dashboard.Drawing.OpenGL.Executors.simple.frag");
|
|
int vs = ShaderUtil.CompileShader(ShaderType.VertexShader, vsource);
|
|
int fs = ShaderUtil.CompileShader(ShaderType.FragmentShader, fsource);
|
|
_program = ShaderUtil.LinkProgram(vs, fs, new []
|
|
{
|
|
"a_v3Position",
|
|
"a_v2TexCoords",
|
|
"a_iCmdIndex",
|
|
});
|
|
GL.DeleteShader(vs);
|
|
GL.DeleteShader(fs);
|
|
|
|
GL.UniformBlockBinding(_program, GL.GetUniformBlockIndex(_program, "CommandBlock"), 0);
|
|
}
|
|
|
|
private static Stream FetchEmbeddedResource(string name)
|
|
{
|
|
return typeof(BaseCommandExecutor).Assembly.GetManifestResourceStream(name)!;
|
|
}
|
|
}
|
|
}
|