mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Colour batch reduction (#2809)
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
This commit is contained in:
@@ -5,20 +5,19 @@ namespace Robust.Client.Graphics.Clyde
|
||||
private static readonly (string, uint)[] BaseShaderAttribLocations =
|
||||
{
|
||||
("aPos", 0),
|
||||
("tCoord", 1)
|
||||
("tCoord", 1),
|
||||
("modulate", 2)
|
||||
};
|
||||
|
||||
private const int UniIModUV = 0;
|
||||
private const int UniIModelMatrix = 1;
|
||||
private const int UniIModulate = 2;
|
||||
private const int UniITexturePixelSize = 3;
|
||||
private const int UniIMainTexture = 4;
|
||||
private const int UniILightTexture = 5;
|
||||
private const int UniCount = 6;
|
||||
private const int UniITexturePixelSize = 2;
|
||||
private const int UniIMainTexture = 3;
|
||||
private const int UniILightTexture = 4;
|
||||
private const int UniCount = 5;
|
||||
|
||||
private const string UniModUV = "modifyUV";
|
||||
private const string UniModelMatrix = "modelMatrix";
|
||||
private const string UniModulate = "modulate";
|
||||
private const string UniTexturePixelSize = "TEXTURE_PIXEL_SIZE";
|
||||
private const string UniMainTexture = "TEXTURE";
|
||||
private const string UniLightTexture = "lightMap";
|
||||
|
||||
@@ -37,7 +37,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
gridProgram.SetUniformTextureMaybe(UniIMainTexture, TextureUnit.Texture0);
|
||||
gridProgram.SetUniformTextureMaybe(UniILightTexture, TextureUnit.Texture1);
|
||||
gridProgram.SetUniform(UniIModUV, new Vector4(0, 0, 1, 1));
|
||||
gridProgram.SetUniform(UniIModulate, Color.White);
|
||||
|
||||
foreach (var mapGrid in _mapManager.FindGridsIntersecting(mapId, worldBounds))
|
||||
{
|
||||
@@ -115,10 +114,10 @@ namespace Robust.Client.Graphics.Clyde
|
||||
var gy = y + cScaled.Y;
|
||||
|
||||
var vIdx = i * 4;
|
||||
vertexBuffer[vIdx + 0] = new Vertex2D(gx, gy, region.Left, region.Bottom);
|
||||
vertexBuffer[vIdx + 1] = new Vertex2D(gx + 1, gy, region.Right, region.Bottom);
|
||||
vertexBuffer[vIdx + 2] = new Vertex2D(gx + 1, gy + 1, region.Right, region.Top);
|
||||
vertexBuffer[vIdx + 3] = new Vertex2D(gx, gy + 1, region.Left, region.Top);
|
||||
vertexBuffer[vIdx + 0] = new Vertex2D(gx, gy, region.Left, region.Bottom, Color.White);
|
||||
vertexBuffer[vIdx + 1] = new Vertex2D(gx + 1, gy, region.Right, region.Bottom, Color.White);
|
||||
vertexBuffer[vIdx + 2] = new Vertex2D(gx + 1, gy + 1, region.Right, region.Top, Color.White);
|
||||
vertexBuffer[vIdx + 3] = new Vertex2D(gx, gy + 1, region.Left, region.Top, Color.White);
|
||||
var nIdx = i * GetQuadBatchIndexCount();
|
||||
var tIdx = (ushort)(i * 4);
|
||||
QuadBatchIndexWrite(indexBuffer, ref nIdx, tIdx);
|
||||
@@ -151,12 +150,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
eboSize, $"Grid {grid.Index} chunk {chunk.Indices} EBO");
|
||||
|
||||
ObjectLabelMaybe(ObjectLabelIdentifier.VertexArray, vao, $"Grid {grid.Index} chunk {chunk.Indices} VAO");
|
||||
// Vertex Coords
|
||||
GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 0);
|
||||
GL.EnableVertexAttribArray(0);
|
||||
// Texture Coords.
|
||||
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 2 * sizeof(float));
|
||||
GL.EnableVertexAttribArray(1);
|
||||
SetupVAOLayout();
|
||||
CheckGlError();
|
||||
|
||||
// Assign VBO and EBO to VAO.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using JetBrains.Annotations;
|
||||
@@ -8,6 +9,26 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Contains various layout/rendering structs used inside Clyde.
|
||||
internal partial class Clyde
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets up VAO layout for Vertex2D for base and raw shader types.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void SetupVAOLayout()
|
||||
{
|
||||
// Vertex Coords
|
||||
GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 0);
|
||||
GL.EnableVertexAttribArray(0);
|
||||
// Texture Coords.
|
||||
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 2 * sizeof(float));
|
||||
GL.EnableVertexAttribArray(1);
|
||||
// Colour Modulation.
|
||||
GL.VertexAttribPointer(2, 4, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 4 * sizeof(float));
|
||||
GL.EnableVertexAttribArray(2);
|
||||
}
|
||||
|
||||
// NOTE: This is:
|
||||
// + Directly cast from DrawVertexUV2DColor!!!
|
||||
// + GLContextWindow does it's own thing with this for winblit, be careful!
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
[PublicAPI]
|
||||
private readonly struct Vertex2D
|
||||
@@ -16,6 +37,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
public readonly Vector2 Position;
|
||||
public readonly Vector2 TextureCoordinates;
|
||||
// Note that this color is in linear space.
|
||||
public readonly Color Modulate;
|
||||
|
||||
static Vertex2D()
|
||||
{
|
||||
@@ -26,27 +49,34 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Vertex2D(Vector2 position, Vector2 textureCoordinates)
|
||||
public Vertex2D(Vector2 position, Vector2 textureCoordinates, Color modulate)
|
||||
{
|
||||
Position = position;
|
||||
TextureCoordinates = textureCoordinates;
|
||||
Modulate = modulate;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Vertex2D(float x, float y, float u, float v)
|
||||
: this(new Vector2(x, y), new Vector2(u, v))
|
||||
public Vertex2D(float x, float y, float u, float v, float r, float g, float b, float a)
|
||||
: this(new Vector2(x, y), new Vector2(u, v), new Color(r, g, b, a))
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Vertex2D(Vector2 position, float u, float v)
|
||||
: this(position, new Vector2(u, v))
|
||||
public Vertex2D(float x, float y, float u, float v, Color modulate)
|
||||
: this(new Vector2(x, y), new Vector2(u, v), modulate)
|
||||
{
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Vertex2D(Vector2 position, float u, float v, Color modulate)
|
||||
: this(position, new Vector2(u, v), modulate)
|
||||
{
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Vertex2D: {Position}, {TextureCoordinates}";
|
||||
return $"Vertex2D: {Position}, {TextureCoordinates}, {Modulate}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -208,60 +208,34 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_clyde.DrawClear(color);
|
||||
}
|
||||
|
||||
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ReadOnlySpan<Vector2> vertices,
|
||||
Color color)
|
||||
{
|
||||
// TODO: Maybe don't stackalloc if the data is too large.
|
||||
Span<DrawVertexUV2D> drawVertices = stackalloc DrawVertexUV2D[vertices.Length];
|
||||
PadVertices(vertices, drawVertices);
|
||||
|
||||
DrawPrimitives(primitiveTopology, Texture.White, drawVertices, color);
|
||||
}
|
||||
|
||||
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ReadOnlySpan<ushort> indices,
|
||||
ReadOnlySpan<Vector2> vertices, Color color)
|
||||
{
|
||||
// TODO: Maybe don't stackalloc if the data is too large.
|
||||
Span<DrawVertexUV2D> drawVertices = stackalloc DrawVertexUV2D[vertices.Length];
|
||||
PadVertices(vertices, drawVertices);
|
||||
|
||||
DrawPrimitives(primitiveTopology, Texture.White, indices, drawVertices, color);
|
||||
}
|
||||
|
||||
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture,
|
||||
ReadOnlySpan<DrawVertexUV2D> vertices, Color color)
|
||||
ReadOnlySpan<DrawVertexUV2DColor> vertices)
|
||||
{
|
||||
if (!(texture is ClydeTexture clydeTexture))
|
||||
{
|
||||
throw new ArgumentException("Texture must be a basic texture.");
|
||||
}
|
||||
|
||||
var castSpan = MemoryMarshal.Cast<DrawVertexUV2D, Vertex2D>(vertices);
|
||||
var castSpan = MemoryMarshal.Cast<DrawVertexUV2DColor, Vertex2D>(vertices);
|
||||
|
||||
_clyde.DrawPrimitives(primitiveTopology, clydeTexture.TextureId, castSpan, color);
|
||||
_clyde.DrawPrimitives(primitiveTopology, clydeTexture.TextureId, castSpan);
|
||||
}
|
||||
|
||||
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture,
|
||||
ReadOnlySpan<ushort> indices,
|
||||
ReadOnlySpan<DrawVertexUV2D> vertices, Color color)
|
||||
ReadOnlySpan<DrawVertexUV2DColor> vertices)
|
||||
{
|
||||
if (!(texture is ClydeTexture clydeTexture))
|
||||
{
|
||||
throw new ArgumentException("Texture must be a basic texture.");
|
||||
}
|
||||
|
||||
var castSpan = MemoryMarshal.Cast<DrawVertexUV2D, Vertex2D>(vertices);
|
||||
var castSpan = MemoryMarshal.Cast<DrawVertexUV2DColor, Vertex2D>(vertices);
|
||||
|
||||
_clyde.DrawPrimitives(primitiveTopology, clydeTexture.TextureId, indices, castSpan, color);
|
||||
_clyde.DrawPrimitives(primitiveTopology, clydeTexture.TextureId, indices, castSpan);
|
||||
}
|
||||
|
||||
private void PadVertices(ReadOnlySpan<Vector2> input, Span<DrawVertexUV2D> output)
|
||||
{
|
||||
for (var i = 0; i < output.Length; i++)
|
||||
{
|
||||
output[i] = new DrawVertexUV2D(input[i], (0.5f, 0.5f));
|
||||
}
|
||||
}
|
||||
// ---- (end) ----
|
||||
|
||||
private sealed class DrawingHandleScreenImpl : DrawingHandleScreen
|
||||
{
|
||||
@@ -282,38 +256,16 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_renderHandle.UseShader(shader);
|
||||
}
|
||||
|
||||
public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology,
|
||||
ReadOnlySpan<Vector2> vertices,
|
||||
Color color)
|
||||
public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture,
|
||||
ReadOnlySpan<DrawVertexUV2DColor> vertices)
|
||||
{
|
||||
var realColor = color * Modulate;
|
||||
|
||||
_renderHandle.DrawPrimitives(primitiveTopology, vertices, realColor);
|
||||
}
|
||||
|
||||
public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology,
|
||||
ReadOnlySpan<ushort> indices,
|
||||
ReadOnlySpan<Vector2> vertices, Color color)
|
||||
{
|
||||
var realColor = color * Modulate;
|
||||
|
||||
_renderHandle.DrawPrimitives(primitiveTopology, indices, vertices, realColor);
|
||||
_renderHandle.DrawPrimitives(primitiveTopology, texture, vertices);
|
||||
}
|
||||
|
||||
public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture,
|
||||
ReadOnlySpan<DrawVertexUV2D> vertices, Color? color = null)
|
||||
ReadOnlySpan<ushort> indices, ReadOnlySpan<DrawVertexUV2DColor> vertices)
|
||||
{
|
||||
var realColor = (color ?? Color.White) * Modulate;
|
||||
|
||||
_renderHandle.DrawPrimitives(primitiveTopology, texture, vertices, realColor);
|
||||
}
|
||||
|
||||
public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture,
|
||||
ReadOnlySpan<ushort> indices, ReadOnlySpan<DrawVertexUV2D> vertices, Color? color = null)
|
||||
{
|
||||
var realColor = (color ?? Color.White) * Modulate;
|
||||
|
||||
_renderHandle.DrawPrimitives(primitiveTopology, texture, indices, vertices, realColor);
|
||||
_renderHandle.DrawPrimitives(primitiveTopology, texture, indices, vertices);
|
||||
}
|
||||
|
||||
public override void DrawLine(Vector2 from, Vector2 to, Color color)
|
||||
@@ -379,7 +331,16 @@ namespace Robust.Client.Graphics.Clyde
|
||||
int divisions = Math.Max(16,(int)(radius * 16));
|
||||
float arcLength = MathF.PI * 2 / divisions;
|
||||
|
||||
Span<Vector2> filledTriangle = stackalloc Vector2[3];
|
||||
var colorReal = color * Modulate;
|
||||
|
||||
if (filled)
|
||||
{
|
||||
// Unfilled (using _renderHandle.DrawLine) does the linear conversion internally.
|
||||
// Filled meanwhile uses DrawPrimitives, so has to do it here.
|
||||
colorReal = Color.FromSrgb(color);
|
||||
}
|
||||
|
||||
Span<DrawVertexUV2DColor> filledTriangle = stackalloc DrawVertexUV2DColor[3];
|
||||
|
||||
// Draws a "circle", but its just a polygon with a bunch of sides
|
||||
// this is the GL_LINES version, not GL_LINE_STRIP
|
||||
@@ -389,14 +350,14 @@ namespace Robust.Client.Graphics.Clyde
|
||||
var endPos = new Vector2(MathF.Cos(arcLength * (i+1)) * radius, MathF.Sin(arcLength * (i + 1)) * radius);
|
||||
|
||||
if(!filled)
|
||||
_renderHandle.DrawLine(startPos, endPos, color);
|
||||
_renderHandle.DrawLine(startPos, endPos, colorReal);
|
||||
else
|
||||
{
|
||||
filledTriangle[0] = startPos + position;
|
||||
filledTriangle[1] = endPos + position;
|
||||
filledTriangle[2] = Vector2.Zero + position;
|
||||
filledTriangle[0] = new DrawVertexUV2DColor(startPos + position, colorReal);
|
||||
filledTriangle[1] = new DrawVertexUV2DColor(endPos + position, colorReal);
|
||||
filledTriangle[2] = new DrawVertexUV2DColor(position, colorReal);
|
||||
|
||||
_renderHandle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, filledTriangle, color);
|
||||
_renderHandle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, Texture.White, filledTriangle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -477,38 +438,16 @@ namespace Robust.Client.Graphics.Clyde
|
||||
quad.TopLeft, quad.TopRight, color, in subRegion);
|
||||
}
|
||||
|
||||
public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology,
|
||||
ReadOnlySpan<Vector2> vertices,
|
||||
Color color)
|
||||
public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture,
|
||||
ReadOnlySpan<DrawVertexUV2DColor> vertices)
|
||||
{
|
||||
var realColor = color * Modulate;
|
||||
|
||||
_renderHandle.DrawPrimitives(primitiveTopology, vertices, realColor);
|
||||
}
|
||||
|
||||
public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology,
|
||||
ReadOnlySpan<ushort> indices,
|
||||
ReadOnlySpan<Vector2> vertices, Color color)
|
||||
{
|
||||
var realColor = color * Modulate;
|
||||
|
||||
_renderHandle.DrawPrimitives(primitiveTopology, indices, vertices, realColor);
|
||||
_renderHandle.DrawPrimitives(primitiveTopology, texture, vertices);
|
||||
}
|
||||
|
||||
public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture,
|
||||
ReadOnlySpan<DrawVertexUV2D> vertices, Color? color = null)
|
||||
ReadOnlySpan<ushort> indices, ReadOnlySpan<DrawVertexUV2DColor> vertices)
|
||||
{
|
||||
var realColor = (color ?? Color.White) * Modulate;
|
||||
|
||||
_renderHandle.DrawPrimitives(primitiveTopology, texture, vertices, realColor);
|
||||
}
|
||||
|
||||
public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture,
|
||||
ReadOnlySpan<ushort> indices, ReadOnlySpan<DrawVertexUV2D> vertices, Color? color = null)
|
||||
{
|
||||
var realColor = (color ?? Color.White) * Modulate;
|
||||
|
||||
_renderHandle.DrawPrimitives(primitiveTopology, texture, indices, vertices, realColor);
|
||||
_renderHandle.DrawPrimitives(primitiveTopology, texture, indices, vertices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,7 +239,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Reset ModUV to ensure it's identity and doesn't touch anything.
|
||||
program.SetUniformMaybe(UniIModUV, new Vector4(0, 0, 1, 1));
|
||||
|
||||
program.SetUniformMaybe(UniIModulate, command.Modulate);
|
||||
program.SetUniformMaybe(UniITexturePixelSize, Vector2.One / loadedTexture.Size);
|
||||
|
||||
var primitiveType = MapPrimitiveType(command.PrimitiveType);
|
||||
@@ -529,25 +528,27 @@ namespace Robust.Client.Graphics.Clyde
|
||||
/// <param name="br">Bottom right vertex of the quad in object space.</param>
|
||||
/// <param name="tl">Top left vertex of the quad in object space.</param>
|
||||
/// <param name="tr">Top right vertex of the quad in object space.</param>
|
||||
/// <param name="modulate">A color to multiply the texture by when shading.</param>
|
||||
/// <param name="modulate">A color to multiply the texture by when shading. Non-linear.</param>
|
||||
/// <param name="texCoords">The four corners of the texture coordinates, matching the four vertices.</param>
|
||||
private void DrawTexture(ClydeHandle texture, Vector2 bl, Vector2 br, Vector2 tl, Vector2 tr, in Color modulate,
|
||||
in Box2 texCoords)
|
||||
{
|
||||
EnsureBatchSpaceAvailable(4, GetQuadBatchIndexCount());
|
||||
EnsureBatchState(texture, in modulate, true, GetQuadBatchPrimitiveType(), _queuedShader);
|
||||
EnsureBatchState(texture, true, GetQuadBatchPrimitiveType(), _queuedShader);
|
||||
|
||||
bl = _currentMatrixModel.Transform(bl);
|
||||
br = _currentMatrixModel.Transform(br);
|
||||
tr = _currentMatrixModel.Transform(tr);
|
||||
tl = _currentMatrixModel.Transform(tl);
|
||||
|
||||
var modulateLinear = Color.FromSrgb(modulate);
|
||||
|
||||
// TODO: split batch if necessary.
|
||||
var vIdx = BatchVertexIndex;
|
||||
BatchVertexData[vIdx + 0] = new Vertex2D(bl, texCoords.BottomLeft);
|
||||
BatchVertexData[vIdx + 1] = new Vertex2D(br, texCoords.BottomRight);
|
||||
BatchVertexData[vIdx + 2] = new Vertex2D(tr, texCoords.TopRight);
|
||||
BatchVertexData[vIdx + 3] = new Vertex2D(tl, texCoords.TopLeft);
|
||||
BatchVertexData[vIdx + 0] = new Vertex2D(bl, texCoords.BottomLeft, modulateLinear);
|
||||
BatchVertexData[vIdx + 1] = new Vertex2D(br, texCoords.BottomRight, modulateLinear);
|
||||
BatchVertexData[vIdx + 2] = new Vertex2D(tr, texCoords.TopRight, modulateLinear);
|
||||
BatchVertexData[vIdx + 3] = new Vertex2D(tl, texCoords.TopLeft, modulateLinear);
|
||||
BatchVertexIndex += 4;
|
||||
QuadBatchIndexWrite(BatchIndexData, ref BatchIndexIndex, (ushort) vIdx);
|
||||
|
||||
@@ -555,7 +556,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
|
||||
private void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ClydeHandle textureId,
|
||||
ReadOnlySpan<ushort> indices, ReadOnlySpan<Vertex2D> vertices, in Color color)
|
||||
ReadOnlySpan<ushort> indices, ReadOnlySpan<Vertex2D> vertices)
|
||||
{
|
||||
FinishBatch();
|
||||
_batchMetaData = null;
|
||||
@@ -585,7 +586,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
command.DrawBatch.Indexed = true;
|
||||
command.DrawBatch.StartIndex = BatchIndexIndex;
|
||||
command.DrawBatch.PrimitiveType = MapDrawToBatchPrimitiveType(primitiveTopology);
|
||||
command.DrawBatch.Modulate = color;
|
||||
command.DrawBatch.TextureId = textureId;
|
||||
command.DrawBatch.ShaderInstance = _queuedShader;
|
||||
|
||||
@@ -598,7 +598,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
|
||||
private void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ClydeHandle textureId,
|
||||
in ReadOnlySpan<Vertex2D> vertices, in Color color)
|
||||
in ReadOnlySpan<Vertex2D> vertices)
|
||||
{
|
||||
FinishBatch();
|
||||
_batchMetaData = null;
|
||||
@@ -612,7 +612,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
command.DrawBatch.Indexed = false;
|
||||
command.DrawBatch.StartIndex = BatchVertexIndex;
|
||||
command.DrawBatch.PrimitiveType = MapDrawToBatchPrimitiveType(primitiveTopology);
|
||||
command.DrawBatch.Modulate = color;
|
||||
command.DrawBatch.TextureId = textureId;
|
||||
command.DrawBatch.ShaderInstance = _queuedShader;
|
||||
|
||||
@@ -641,15 +640,17 @@ namespace Robust.Client.Graphics.Clyde
|
||||
private void DrawLine(Vector2 a, Vector2 b, Color color)
|
||||
{
|
||||
EnsureBatchSpaceAvailable(2, 0);
|
||||
EnsureBatchState(_stockTextureWhite.TextureId, color, false, BatchPrimitiveType.LineList, _queuedShader);
|
||||
EnsureBatchState(_stockTextureWhite.TextureId, false, BatchPrimitiveType.LineList, _queuedShader);
|
||||
|
||||
a = _currentMatrixModel.Transform(a);
|
||||
b = _currentMatrixModel.Transform(b);
|
||||
|
||||
var colorLinear = Color.FromSrgb(color);
|
||||
|
||||
// TODO: split batch if necessary.
|
||||
var vIdx = BatchVertexIndex;
|
||||
BatchVertexData[vIdx + 0] = new Vertex2D(a, Vector2.Zero);
|
||||
BatchVertexData[vIdx + 1] = new Vertex2D(b, Vector2.Zero);
|
||||
BatchVertexData[vIdx + 0] = new Vertex2D(a, Vector2.Zero, colorLinear);
|
||||
BatchVertexData[vIdx + 1] = new Vertex2D(b, Vector2.Zero, colorLinear);
|
||||
BatchVertexIndex += 2;
|
||||
|
||||
_debugStats.LastClydeDrawCalls += 1;
|
||||
@@ -716,14 +717,13 @@ namespace Robust.Client.Graphics.Clyde
|
||||
/// Ensures that batching metadata matches the current batch.
|
||||
/// If not, the current batch is finished and a new one is started.
|
||||
/// </summary>
|
||||
private void EnsureBatchState(ClydeHandle textureId, in Color color, bool indexed,
|
||||
private void EnsureBatchState(ClydeHandle textureId, bool indexed,
|
||||
BatchPrimitiveType primitiveType, ClydeHandle shaderInstance)
|
||||
{
|
||||
if (_batchMetaData.HasValue)
|
||||
{
|
||||
var metaData = _batchMetaData.Value;
|
||||
if (metaData.TextureId == textureId &&
|
||||
StrictColorEquality(metaData.Color, color) &&
|
||||
indexed == metaData.Indexed &&
|
||||
metaData.PrimitiveType == primitiveType &&
|
||||
metaData.ShaderInstance == shaderInstance)
|
||||
@@ -737,7 +737,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
|
||||
// ... and start another.
|
||||
_batchMetaData = new BatchMetaData(textureId, color, indexed, primitiveType,
|
||||
_batchMetaData = new BatchMetaData(textureId, indexed, primitiveType,
|
||||
indexed ? BatchIndexIndex : BatchVertexIndex, shaderInstance);
|
||||
|
||||
/*
|
||||
@@ -769,7 +769,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
command.DrawBatch.Indexed = indexed;
|
||||
command.DrawBatch.StartIndex = metaData.StartIndex;
|
||||
command.DrawBatch.PrimitiveType = metaData.PrimitiveType;
|
||||
command.DrawBatch.Modulate = metaData.Color;
|
||||
command.DrawBatch.TextureId = metaData.TextureId;
|
||||
command.DrawBatch.ShaderInstance = metaData.ShaderInstance;
|
||||
|
||||
@@ -898,7 +897,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
public ClydeHandle TextureId;
|
||||
public ClydeHandle ShaderInstance;
|
||||
public Color Modulate;
|
||||
|
||||
public int StartIndex;
|
||||
public int Count;
|
||||
@@ -970,17 +968,15 @@ namespace Robust.Client.Graphics.Clyde
|
||||
private readonly struct BatchMetaData
|
||||
{
|
||||
public readonly ClydeHandle TextureId;
|
||||
public readonly Color Color;
|
||||
public readonly bool Indexed;
|
||||
public readonly BatchPrimitiveType PrimitiveType;
|
||||
public readonly int StartIndex;
|
||||
public readonly ClydeHandle ShaderInstance;
|
||||
|
||||
public BatchMetaData(ClydeHandle textureId, in Color color, bool indexed, BatchPrimitiveType primitiveType,
|
||||
public BatchMetaData(ClydeHandle textureId, bool indexed, BatchPrimitiveType primitiveType,
|
||||
int startIndex, ClydeHandle shaderInstance)
|
||||
{
|
||||
TextureId = textureId;
|
||||
Color = color;
|
||||
Indexed = indexed;
|
||||
PrimitiveType = primitiveType;
|
||||
StartIndex = startIndex;
|
||||
|
||||
@@ -14,6 +14,7 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Profiling;
|
||||
using Robust.Shared.Timing;
|
||||
using DependencyAttribute = Robust.Shared.IoC.DependencyAttribute;
|
||||
@@ -275,10 +276,10 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
Span<Vertex2D> quadVertices = stackalloc[]
|
||||
{
|
||||
new Vertex2D(1, 0, 1, 1),
|
||||
new Vertex2D(0, 0, 0, 1),
|
||||
new Vertex2D(1, 1, 1, 0),
|
||||
new Vertex2D(0, 1, 0, 0)
|
||||
new Vertex2D(1, 0, 1, 1, Color.White),
|
||||
new Vertex2D(0, 0, 0, 1, Color.White),
|
||||
new Vertex2D(1, 1, 1, 0, Color.White),
|
||||
new Vertex2D(0, 1, 0, 0, Color.White)
|
||||
};
|
||||
|
||||
QuadVBO = new GLBuffer<Vertex2D>(this, BufferTarget.ArrayBuffer, BufferUsageHint.StaticDraw,
|
||||
@@ -294,10 +295,10 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
Span<Vertex2D> winVertices = stackalloc[]
|
||||
{
|
||||
new Vertex2D(-1, 1, 0, 1),
|
||||
new Vertex2D(-1, -1, 0, 0),
|
||||
new Vertex2D(1, 1, 1, 1),
|
||||
new Vertex2D(1, -1, 1, 0),
|
||||
new Vertex2D(-1, 1, 0, 1, Color.White),
|
||||
new Vertex2D(-1, -1, 0, 0, Color.White),
|
||||
new Vertex2D(1, 1, 1, 1, Color.White),
|
||||
new Vertex2D(1, -1, 1, 0, Color.White),
|
||||
};
|
||||
|
||||
WindowVBO = new GLBuffer<Vertex2D>(
|
||||
@@ -318,12 +319,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
BatchVAO = new GLHandle(GenVertexArray());
|
||||
BindVertexArray(BatchVAO.Handle);
|
||||
ObjectLabelMaybe(ObjectLabelIdentifier.VertexArray, BatchVAO, nameof(BatchVAO));
|
||||
// Vertex Coords
|
||||
GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 0);
|
||||
GL.EnableVertexAttribArray(0);
|
||||
// Texture Coords.
|
||||
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 2 * sizeof(float));
|
||||
GL.EnableVertexAttribArray(1);
|
||||
SetupVAOLayout();
|
||||
|
||||
CheckGlError();
|
||||
|
||||
@@ -347,12 +343,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
BindVertexArray(vao.Handle);
|
||||
ObjectLabelMaybe(ObjectLabelIdentifier.VertexArray, vao, nameof(QuadVAO));
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, QuadVBO.ObjectHandle);
|
||||
// Vertex Coords
|
||||
GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 0);
|
||||
GL.EnableVertexAttribArray(0);
|
||||
// Texture Coords.
|
||||
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 2 * sizeof(float));
|
||||
GL.EnableVertexAttribArray(1);
|
||||
SetupVAOLayout();
|
||||
|
||||
return vao;
|
||||
}
|
||||
|
||||
@@ -178,9 +178,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
case UniIModUV:
|
||||
name = UniModUV;
|
||||
break;
|
||||
case UniIModulate:
|
||||
name = UniModulate;
|
||||
break;
|
||||
case UniILightTexture:
|
||||
name = UniLightTexture;
|
||||
break;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
varying highp vec2 UV;
|
||||
varying highp vec2 Pos;
|
||||
varying highp vec4 VtxModulate;
|
||||
|
||||
uniform sampler2D lightMap;
|
||||
uniform highp vec4 modulate;
|
||||
|
||||
// [SHADER_HEADER_CODE]
|
||||
|
||||
@@ -16,5 +16,5 @@ void main()
|
||||
|
||||
lowp vec3 lightSample = texture2D(lightMap, Pos).rgb;
|
||||
|
||||
gl_FragColor = zAdjustResult(COLOR * modulate * vec4(lightSample, 1.0));
|
||||
gl_FragColor = zAdjustResult(COLOR * VtxModulate * vec4(lightSample, 1.0));
|
||||
}
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
/*layout (location = 0)*/ attribute vec2 aPos;
|
||||
// Texture coordinates.
|
||||
/*layout (location = 1)*/ attribute vec2 tCoord;
|
||||
// Colour modulation.
|
||||
/*layout (location = 2)*/ attribute vec4 modulate;
|
||||
|
||||
varying vec2 UV;
|
||||
varying vec2 Pos;
|
||||
varying vec4 VtxModulate;
|
||||
|
||||
// Maybe we should merge these CPU side.
|
||||
// idk yet.
|
||||
@@ -33,4 +36,5 @@ void main()
|
||||
gl_Position = vec4(VERTEX, 0.0, 1.0);
|
||||
Pos = (VERTEX + 1.0) / 2.0;
|
||||
UV = mix(modifyUV.xy, modifyUV.zw, tCoord);
|
||||
VtxModulate = modulate;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
varying highp vec2 UV;
|
||||
|
||||
uniform sampler2D lightMap;
|
||||
uniform highp vec4 modulate;
|
||||
|
||||
// [SHADER_HEADER_CODE]
|
||||
|
||||
@@ -13,5 +12,7 @@ void main()
|
||||
|
||||
// [SHADER_CODE]
|
||||
|
||||
// NOTE: You may want to add modulation here. Problem: Game doesn't like that.
|
||||
// In particular, walls disappear.
|
||||
gl_FragColor = zAdjustResult(COLOR);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
/*layout (location = 0)*/ attribute vec2 aPos;
|
||||
// Texture coordinates.
|
||||
/*layout (location = 1)*/ attribute vec2 tCoord;
|
||||
// Colour modulation.
|
||||
/*layout (location = 2)*/ attribute vec4 modulate;
|
||||
|
||||
varying vec2 UV;
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
@@ -12,6 +14,15 @@ namespace Robust.Client.Graphics
|
||||
//private protected IRenderHandle _renderHandle;
|
||||
private protected readonly int _handleId;
|
||||
public bool Disposed { get; private set; }
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public Color Modulate { get; set; } = Color.White;
|
||||
|
||||
public void Dispose()
|
||||
@@ -37,14 +48,25 @@ namespace Robust.Client.Graphics
|
||||
|
||||
public abstract void UseShader(ShaderInstance? shader);
|
||||
|
||||
// ---- DrawPrimitives: Vector2 API ----
|
||||
|
||||
/// <summary>
|
||||
/// Draws arbitrary geometry primitives with a flat color.
|
||||
/// </summary>
|
||||
/// <param name="primitiveTopology">The topology of the primitives to draw.</param>
|
||||
/// <param name="vertices">The set of vertices to render.</param>
|
||||
/// <param name="color">The color to draw with.</param>
|
||||
public abstract void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ReadOnlySpan<Vector2> vertices,
|
||||
Color color);
|
||||
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ReadOnlySpan<Vector2> vertices,
|
||||
Color color)
|
||||
{
|
||||
var realColor = color * Modulate;
|
||||
|
||||
// TODO: Maybe don't stackalloc if the data is too large.
|
||||
Span<DrawVertexUV2DColor> drawVertices = stackalloc DrawVertexUV2DColor[vertices.Length];
|
||||
PadVerticesV2(vertices, drawVertices, realColor);
|
||||
|
||||
DrawPrimitives(primitiveTopology, Texture.White, drawVertices);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws arbitrary indexed geometry primitives with a flat color.
|
||||
@@ -53,8 +75,28 @@ namespace Robust.Client.Graphics
|
||||
/// <param name="indices">The indices into <paramref name="vertices"/> to render.</param>
|
||||
/// <param name="vertices">The set of vertices to render.</param>
|
||||
/// <param name="color">The color to draw with.</param>
|
||||
public abstract void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ReadOnlySpan<ushort> indices,
|
||||
ReadOnlySpan<Vector2> vertices, Color color);
|
||||
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ReadOnlySpan<ushort> indices,
|
||||
ReadOnlySpan<Vector2> vertices, Color color)
|
||||
{
|
||||
var realColor = color * Modulate;
|
||||
|
||||
// TODO: Maybe don't stackalloc if the data is too large.
|
||||
Span<DrawVertexUV2DColor> drawVertices = stackalloc DrawVertexUV2DColor[vertices.Length];
|
||||
PadVerticesV2(vertices, drawVertices, realColor);
|
||||
|
||||
DrawPrimitives(primitiveTopology, Texture.White, indices, drawVertices);
|
||||
}
|
||||
|
||||
private void PadVerticesV2(ReadOnlySpan<Vector2> input, Span<DrawVertexUV2DColor> output, Color color)
|
||||
{
|
||||
Color colorLinear = Color.FromSrgb(color);
|
||||
for (var i = 0; i < output.Length; i++)
|
||||
{
|
||||
output[i] = new DrawVertexUV2DColor(input[i], (0.5f, 0.5f), colorLinear);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- DrawPrimitives: DrawVertexUV2D API ----
|
||||
|
||||
/// <summary>
|
||||
/// Draws arbitrary geometry primitives with a texture.
|
||||
@@ -63,20 +105,70 @@ namespace Robust.Client.Graphics
|
||||
/// <param name="texture">The texture to render with.</param>
|
||||
/// <param name="vertices">The set of vertices to render.</param>
|
||||
/// <param name="color">The color to draw with.</param>
|
||||
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture, ReadOnlySpan<DrawVertexUV2D> vertices,
|
||||
Color? color = null)
|
||||
{
|
||||
var realColor = (color ?? Color.White) * Modulate;
|
||||
|
||||
// TODO: Maybe don't stackalloc if the data is too large.
|
||||
Span<DrawVertexUV2DColor> drawVertices = stackalloc DrawVertexUV2DColor[vertices.Length];
|
||||
PadVerticesUV(vertices, drawVertices, realColor);
|
||||
|
||||
DrawPrimitives(primitiveTopology, texture, drawVertices);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws arbitrary geometry primitives with a texture.
|
||||
/// </summary>
|
||||
/// <param name="primitiveTopology">The topology of the primitives to draw.</param>
|
||||
/// <param name="texture">The texture to render with.</param>
|
||||
/// <param name="vertices">The set of vertices to render.</param>
|
||||
/// <param name="indices">The indices into <paramref name="vertices"/> to render.</param>
|
||||
/// <param name="color">The color to draw with.</param>
|
||||
public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture, ReadOnlySpan<ushort> indices,
|
||||
ReadOnlySpan<DrawVertexUV2D> vertices, Color? color = null)
|
||||
{
|
||||
var realColor = (color ?? Color.White) * Modulate;
|
||||
|
||||
// TODO: Maybe don't stackalloc if the data is too large.
|
||||
Span<DrawVertexUV2DColor> drawVertices = stackalloc DrawVertexUV2DColor[vertices.Length];
|
||||
PadVerticesUV(vertices, drawVertices, realColor);
|
||||
|
||||
DrawPrimitives(primitiveTopology, texture, indices, drawVertices);
|
||||
}
|
||||
|
||||
private void PadVerticesUV(ReadOnlySpan<DrawVertexUV2D> input, Span<DrawVertexUV2DColor> 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 ----
|
||||
|
||||
/// <summary>
|
||||
/// Draws arbitrary geometry primitives with a texture.
|
||||
/// Be aware that this ignores the Modulate property! Apply it yourself if necessary.
|
||||
/// </summary>
|
||||
/// <param name="primitiveTopology">The topology of the primitives to draw.</param>
|
||||
/// <param name="texture">The texture to render with.</param>
|
||||
/// <param name="vertices">The set of vertices to render.</param>
|
||||
public abstract void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture,
|
||||
ReadOnlySpan<DrawVertexUV2D> vertices, Color? color = null);
|
||||
ReadOnlySpan<DrawVertexUV2DColor> vertices);
|
||||
|
||||
/// <summary>
|
||||
/// Draws arbitrary geometry primitives with a flat color.
|
||||
/// Be aware that this ignores the Modulate property! Apply it yourself if necessary.
|
||||
/// </summary>
|
||||
/// <param name="primitiveTopology">The topology of the primitives to draw.</param>
|
||||
/// <param name="texture">The texture to render with.</param>
|
||||
/// <param name="indices">The indices into <paramref name="vertices"/> to render.</param>
|
||||
/// <param name="vertices">The set of vertices to render.</param>
|
||||
/// <param name="color">The color to draw with.</param>
|
||||
public abstract void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture,
|
||||
ReadOnlySpan<ushort> indices,
|
||||
ReadOnlySpan<DrawVertexUV2D> vertices, Color? color = null);
|
||||
ReadOnlySpan<DrawVertexUV2DColor> vertices);
|
||||
|
||||
[DebuggerStepThrough]
|
||||
protected void CheckDisposed()
|
||||
@@ -108,4 +200,51 @@ namespace Robust.Client.Graphics
|
||||
UV = uv;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 2D Vertex that contains position and UV coordinates, and a modulation colour (Linear!!!)
|
||||
/// NOTE: This is directly cast into Clyde Vertex2D!!!!
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct DrawVertexUV2DColor
|
||||
{
|
||||
public Vector2 Position;
|
||||
public Vector2 UV;
|
||||
/// <summary>
|
||||
/// Modulation colour for this vertex.
|
||||
/// Note that this color is in linear space.
|
||||
/// </summary>
|
||||
public Color Color;
|
||||
|
||||
/// <param name="position">The location.</param>
|
||||
/// <param name="uv">The texture coordinate.</param>
|
||||
/// <param name="col">Modulation colour (In linear space, use Color.FromSrgb if needed)</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public DrawVertexUV2DColor(Vector2 position, Vector2 uv, Color col)
|
||||
{
|
||||
Position = position;
|
||||
UV = uv;
|
||||
Color = col;
|
||||
}
|
||||
|
||||
/// <param name="position">The location.</param>
|
||||
/// <param name="col">Modulation colour (In linear space, use Color.FromSrgb if needed)</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public DrawVertexUV2DColor(Vector2 position, Color col)
|
||||
{
|
||||
Position = position;
|
||||
UV = new Vector2(0.5f, 0.5f);
|
||||
Color = col;
|
||||
}
|
||||
|
||||
/// <param name="b">The existing position/UV pair.</param>
|
||||
/// <param name="col">Modulation colour (In linear space, use Color.FromSrgb if needed)</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public DrawVertexUV2DColor(DrawVertexUV2D b, Color col)
|
||||
{
|
||||
Position = b.Position;
|
||||
UV = b.UV;
|
||||
Color = col;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -787,7 +787,6 @@ namespace Robust.Client.UserInterface
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var clip = control.RectClipContent;
|
||||
var scissorRegion = scissorBox;
|
||||
if (clip)
|
||||
@@ -815,11 +814,14 @@ namespace Robust.Client.UserInterface
|
||||
var handle = renderHandle.DrawingHandleScreen;
|
||||
handle.SetTransform(position, Angle.Zero, Vector2.One);
|
||||
modulate *= control.Modulate;
|
||||
handle.Modulate = modulate * control.ActualModulateSelf;
|
||||
|
||||
if (_rendering || control.AlwaysRender)
|
||||
{
|
||||
// Handle modulation with care.
|
||||
var oldMod = handle.Modulate;
|
||||
handle.Modulate = modulate * control.ActualModulateSelf;
|
||||
control.DrawInternal(renderHandle);
|
||||
handle.Modulate = oldMod;
|
||||
handle.UseShader(null);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Xml.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using SysVector3 = System.Numerics.Vector3;
|
||||
@@ -44,6 +45,7 @@ namespace Robust.Shared.Maths
|
||||
/// Represents a color with 4 floating-point components (R, G, B, A).
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Color : IEquatable<Color>
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user