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