using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using Robust.Shared.Maths;
namespace Robust.Client.Graphics
{
///
/// Used for doing direct drawing without sprite components, existing GUI controls, etc...
///
public abstract class DrawingHandleBase : IDisposable
{
private protected readonly int _handleId;
public bool Disposed { get; private set; }
///
/// Drawing commands that do NOT receive per-vertex modulation get modulated by this.
/// Specifically, *DrawPrimitives w/ DrawVertexUV2DColor IS NOT AFFECTED BY THIS*.
/// The only code that should ever be setting this is UserInterfaceManager.
/// It's absolutely evil statefulness.
/// I understand it's existence and operation.
/// I understand that removing it would require rewriting all the UI controls everywhere.
/// I still wish it a prolonged death - it's a performance nightmare. - 20kdc
///
public Color Modulate { get; set; } = Color.White;
protected Texture White;
public DrawingHandleBase(Texture white)
{
White = white;
}
public void Dispose()
{
Disposed = true;
}
public void SetTransform(in Vector2 position, in Angle rotation, in Vector2 scale)
{
CheckDisposed();
var matrix = Matrix3Helpers.CreateTransform(in position, in rotation, in scale);
SetTransform(in matrix);
}
public void SetTransform(in Vector2 position, in Angle rotation)
{
var matrix = Matrix3Helpers.CreateTransform(in position, in rotation);
SetTransform(in matrix);
}
public abstract void SetTransform(in Matrix3x2 matrix);
public abstract Matrix3x2 GetTransform();
public abstract void UseShader(ShaderInstance? shader);
public abstract ShaderInstance? GetShader();
// ---- DrawPrimitives: Vector2 API ----
///
/// Draws arbitrary geometry primitives with a flat color.
///
/// The topology of the primitives to draw.
/// The list of vertices to render.
/// The color to draw with.
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, List vertices,
Color color)
{
var span = CollectionsMarshal.AsSpan(vertices);
DrawPrimitives(primitiveTopology, span, color);
}
///
/// Draws arbitrary geometry primitives with a flat color.
///
/// The topology of the primitives to draw.
/// The set of vertices to render.
/// The color to draw with.
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ReadOnlySpan vertices,
Color color)
{
var realColor = color * Modulate;
// TODO: Maybe don't stackalloc if the data is too large.
Span drawVertices = stackalloc DrawVertexUV2DColor[vertices.Length];
PadVerticesV2(vertices, drawVertices, realColor);
DrawPrimitives(primitiveTopology, White, drawVertices);
}
///
/// Draws arbitrary indexed geometry primitives with a flat color.
///
/// The topology of the primitives to draw.
/// The indices into to render.
/// The set of vertices to render.
/// The color to draw with.
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ReadOnlySpan indices,
ReadOnlySpan vertices, Color color)
{
var realColor = color * Modulate;
// TODO: Maybe don't stackalloc if the data is too large.
Span drawVertices = stackalloc DrawVertexUV2DColor[vertices.Length];
PadVerticesV2(vertices, drawVertices, realColor);
DrawPrimitives(primitiveTopology, White, indices, drawVertices);
}
private void PadVerticesV2(ReadOnlySpan input, Span output, Color color)
{
Color colorLinear = Color.FromSrgb(color);
for (var i = 0; i < output.Length; i++)
{
output[i] = new DrawVertexUV2DColor(input[i], new Vector2(0.5f, 0.5f), colorLinear);
}
}
// ---- DrawPrimitives: DrawVertexUV2D API ----
///
/// Draws arbitrary geometry primitives with a texture.
///
/// The topology of the primitives to draw.
/// The texture to render with.
/// The set of vertices to render.
/// The color to draw with.
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture, ReadOnlySpan vertices,
Color? color = null)
{
var realColor = (color ?? Color.White) * Modulate;
// TODO: Maybe don't stackalloc if the data is too large.
Span drawVertices = stackalloc DrawVertexUV2DColor[vertices.Length];
PadVerticesUV(vertices, drawVertices, realColor);
DrawPrimitives(primitiveTopology, texture, drawVertices);
}
///
/// Draws arbitrary geometry primitives with a texture.
///
/// The topology of the primitives to draw.
/// The texture to render with.
/// The set of vertices to render.
/// The indices into to render.
/// The color to draw with.
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture, ReadOnlySpan indices,
ReadOnlySpan vertices, Color? color = null)
{
var realColor = (color ?? Color.White) * Modulate;
// TODO: Maybe don't stackalloc if the data is too large.
Span drawVertices = stackalloc DrawVertexUV2DColor[vertices.Length];
PadVerticesUV(vertices, drawVertices, realColor);
DrawPrimitives(primitiveTopology, texture, indices, drawVertices);
}
private void PadVerticesUV(ReadOnlySpan input, Span output, Color color)
{
Color colorLinear = Color.FromSrgb(color);
for (var i = 0; i < output.Length; i++)
{
output[i] = new DrawVertexUV2DColor(input[i], colorLinear);
}
}
// ---- End wrappers ----
///
/// Draws arbitrary geometry primitives with a texture.
/// Be aware that this ignores the Modulate property! Apply it yourself if necessary.
///
/// The topology of the primitives to draw.
/// The texture to render with.
/// The set of vertices to render.
public abstract void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture,
ReadOnlySpan vertices);
///
/// Draws arbitrary geometry primitives with a flat color.
/// Be aware that this ignores the Modulate property! Apply it yourself if necessary.
///
/// The topology of the primitives to draw.
/// The texture to render with.
/// The indices into to render.
/// The set of vertices to render.
public abstract void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture,
ReadOnlySpan indices,
ReadOnlySpan vertices);
[DebuggerStepThrough]
protected void CheckDisposed()
{
if (Disposed)
{
throw new ObjectDisposedException(nameof(DrawingHandleBase));
}
}
public abstract void DrawCircle(Vector2 position, float radius, Color color, bool filled = true);
public abstract void DrawLine(Vector2 from, Vector2 to, Color color);
public abstract void RenderInRenderTarget(IRenderTarget target, Action a, Color? clearColor);
public abstract void DrawTexture(Texture texture, Vector2 position, Color? modulate = null);
}
///
/// 2D Vertex that contains both position and UV coordinates.
///
public struct DrawVertexUV2D
{
public Vector2 Position;
public Vector2 UV;
public DrawVertexUV2D(Vector2 position, Vector2 uv)
{
Position = position;
UV = uv;
}
}
///
/// 2D Vertex that contains position and UV coordinates, and a modulation colour (Linear!!!)
/// NOTE: This is directly cast into Clyde Vertex2D!!!!
///
[StructLayout(LayoutKind.Sequential)]
public struct DrawVertexUV2DColor
{
public Vector2 Position;
public Vector2 UV;
public Vector2 UV2;
///
/// Modulation colour for this vertex.
/// Note that this color is in linear space.
///
public Color Color;
/// The location.
/// The texture coordinate.
/// Modulation colour (In linear space, use Color.FromSrgb if needed)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public DrawVertexUV2DColor(Vector2 position, Vector2 uv, Color col)
{
Position = position;
UV = uv;
Color = col;
}
/// The location.
/// Modulation colour (In linear space, use Color.FromSrgb if needed)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public DrawVertexUV2DColor(Vector2 position, Color col)
{
Position = position;
UV = new Vector2(0.5f, 0.5f);
Color = col;
}
/// The existing position/UV pair.
/// Modulation colour (In linear space, use Color.FromSrgb if needed)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public DrawVertexUV2DColor(DrawVertexUV2D b, Color col)
{
Position = b.Position;
UV = b.UV;
Color = col;
}
}
}