Skip to content

Commit

Permalink
Engine changes for displacement maps. (#5023)
Browse files Browse the repository at this point in the history
* Add load parameter support to RSIs.

Currently only supports turning sRGB off. RSIs with custom load parameters are not thrown into the meta-atlas.

As part of this, TextureLoadParameters and TextureSampleParameters has been made to support equality.

* Add UV2 channel to vertices.

This is a bad hack to make displacement maps work in Robust. The UV2 channel goes from 0 -> 1 across the draw and can therefore be used by displacement maps to map a separate displacement map layer on top of the regular meta-atlas RSI texture.

This creates float inaccuracy issues but they weren't bad enough to completely void the feature. I'm thinking I learn from this experience and completely re-do how UVs work with the renderer rewrite, so that hopefully won't happen anymore.

This required dumping the optimized PadVerticesV2 because the changed struct size made it impractical. RIP.

I don't like this approach at all but the renderer is slated for a rewrite anyways, and all shaders will need to be rewritten regardless.

* Add CopyToShaderParameters for sprite layers.

This effectively allows copying the parameters of a sprite layer into another layer's shader parameters. The use case is to copy texture coordinates for displacement maps, as the exact map used changes depending on orientation. It also enables animations to be used though I didn't use that personally.
  • Loading branch information
PJB3005 committed Apr 27, 2024
1 parent 6e0205d commit 4033d96
Show file tree
Hide file tree
Showing 18 changed files with 278 additions and 88 deletions.
4 changes: 3 additions & 1 deletion RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ END TEMPLATE-->

### New features

*None yet*
* RSIs can now specify load parameters, mimicking the ones from `.png.yml`. Currently only disabling sRGB is supported.
* Added a second UV channel to Clyde's vertex format. On regular batched sprite draws, this goes 0 -> 1 across the sprite quad.
* Added a new `CopyToShaderParameters` system for `SpriteComponent` layers.

### Bugfixes

Expand Down
85 changes: 73 additions & 12 deletions Robust.Client/GameObjects/Components/Renderable/SpriteComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Numerics;
using System.Text;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Clyde;
using Robust.Client.ResourceManagement;
using Robust.Client.Utility;
using Robust.Shared.Animations;
Expand All @@ -28,6 +29,7 @@
using DrawDepthTag = Robust.Shared.GameObjects.DrawDepth;
using static Robust.Shared.Serialization.TypeSerializers.Implementations.SpriteSpecifierSerializer;
using Direction = Robust.Shared.Maths.Direction;
using Vector4 = Robust.Shared.Maths.Vector4;

namespace Robust.Client.GameObjects
{
Expand Down Expand Up @@ -770,15 +772,7 @@ public void LayerSetData(int index, PrototypeLayerData layerDatum)
{
foreach (var keyString in layerDatum.MapKeys)
{
object key;
if (reflection.TryParseEnumReference(keyString, out var @enum))
{
key = @enum;
}
else
{
key = keyString;
}
var key = ParseKey(keyString);

if (LayerMap.TryGetValue(key, out var mappedIndex))
{
Expand All @@ -804,9 +798,30 @@ public void LayerSetData(int index, PrototypeLayerData layerDatum)
// If neither state: nor texture: were provided we assume that they want a blank invisible layer.
layer.Visible = layerDatum.Visible ?? layer.Visible;

if (layerDatum.CopyToShaderParameters is { } copyParameters)
{
layer.CopyToShaderParameters = new CopyToShaderParameters(ParseKey(copyParameters.LayerKey))
{
ParameterTexture = copyParameters.ParameterTexture,
ParameterUV = copyParameters.ParameterUV
};
}
else
{
layer.CopyToShaderParameters = null;
}

RebuildBounds();
}

private object ParseKey(string keyString)
{
if (reflection.TryParseEnumReference(keyString, out var @enum))
return @enum;

return keyString;
}

public void LayerSetData(object layerKey, PrototypeLayerData data)
{
if (!LayerMapTryGet(layerKey, out var layer, true))
Expand Down Expand Up @@ -1635,6 +1650,9 @@ public Vector2 Offset
[ViewVariables]
public LayerRenderingStrategy RenderingStrategy = LayerRenderingStrategy.UseSpriteStrategy;

[ViewVariables(VVAccess.ReadWrite)]
public CopyToShaderParameters? CopyToShaderParameters;

public Layer(SpriteComponent parent)
{
_parent = parent;
Expand Down Expand Up @@ -2007,8 +2025,6 @@ internal void Render(DrawingHandleWorld drawingHandle, ref Matrix3 spriteMatrix,

// Set the drawing transform for this layer
GetLayerDrawMatrix(dir, out var layerMatrix);
Matrix3.Multiply(in layerMatrix, in spriteMatrix, out var transformMatrix);
drawingHandle.SetTransform(in transformMatrix);

// The direction used to draw the sprite can differ from the one that the angle would naively suggest,
// due to direction overrides or offsets.
Expand All @@ -2018,7 +2034,41 @@ internal void Render(DrawingHandleWorld drawingHandle, ref Matrix3 spriteMatrix,

// Get the correct directional texture from the state, and draw it!
var texture = GetRenderTexture(_actualState, dir);
RenderTexture(drawingHandle, texture);

if (CopyToShaderParameters == null)
{
// Set the drawing transform for this layer
Matrix3.Multiply(in layerMatrix, in spriteMatrix, out var transformMatrix);
drawingHandle.SetTransform(in transformMatrix);

RenderTexture(drawingHandle, texture);
}
else
{
// Multiple atrocities to god being committed right here.
var otherLayerIdx = _parent.LayerMap[CopyToShaderParameters.LayerKey!];
var otherLayer = _parent.Layers[otherLayerIdx];
if (otherLayer.Shader is not { } shader)
{
// No shader set apparently..?
return;
}

if (!shader.Mutable)
otherLayer.Shader = shader = shader.Duplicate();

var clydeTexture = Clyde.RenderHandle.ExtractTexture(texture, null, out var csr);
var sr = Clyde.RenderHandle.WorldTextureBoundsToUV(clydeTexture, csr);

if (CopyToShaderParameters.ParameterTexture is { } paramTexture)
shader.SetParameter(paramTexture, clydeTexture);

if (CopyToShaderParameters.ParameterUV is { } paramUV)
{
var uv = new Vector4(sr.Left, sr.Bottom, sr.Right, sr.Top);
shader.SetParameter(paramUV, uv);
}
}
}

private void RenderTexture(DrawingHandleWorld drawingHandle, Texture texture)
Expand Down Expand Up @@ -2096,6 +2146,17 @@ internal void AdvanceFrameAnimation(RSI.State state)
}
}

/// <summary>
/// Instantiated version of <see cref="PrototypeCopyToShaderParameters"/>.
/// Has <see cref="LayerKey"/> actually resolved to a a real key.
/// </summary>
public sealed class CopyToShaderParameters(object layerKey)
{
public object LayerKey = layerKey;
public string? ParameterTexture;
public string? ParameterUV;
}

void IAnimationProperties.SetAnimatableProperty(string name, object value)
{
if (!name.StartsWith("layer/"))
Expand Down
3 changes: 2 additions & 1 deletion Robust.Client/Graphics/Clyde/Clyde.Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ internal sealed partial class Clyde
{
("aPos", 0),
("tCoord", 1),
("modulate", 2)
("tCoord2", 2),
("modulate", 3)
};

private const int UniIModUV = 0;
Expand Down
17 changes: 15 additions & 2 deletions Robust.Client/Graphics/Clyde/Clyde.Layout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ private static unsafe void SetupVAOLayout()
// Texture Coords.
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, sizeof(Vertex2D), 2 * sizeof(float));
GL.EnableVertexAttribArray(1);
// Colour Modulation.
GL.VertexAttribPointer(2, 4, VertexAttribPointerType.Float, false, sizeof(Vertex2D), 4 * sizeof(float));
// Texture Coords (2).
GL.VertexAttribPointer(2, 2, VertexAttribPointerType.Float, false, sizeof(Vertex2D), 4 * sizeof(float));
GL.EnableVertexAttribArray(2);
// Colour Modulation.
GL.VertexAttribPointer(3, 4, VertexAttribPointerType.Float, false, sizeof(Vertex2D), 6 * sizeof(float));
GL.EnableVertexAttribArray(3);
}

// NOTE: This is:
Expand All @@ -37,6 +40,7 @@ private static unsafe void SetupVAOLayout()
{
public readonly Vector2 Position;
public readonly Vector2 TextureCoordinates;
public readonly Vector2 TextureCoordinates2;
// Note that this color is in linear space.
public readonly Color Modulate;

Expand All @@ -48,6 +52,15 @@ public Vertex2D(Vector2 position, Vector2 textureCoordinates, Color modulate)
Modulate = modulate;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vertex2D(Vector2 position, Vector2 textureCoordinates, Vector2 textureCoordinates2, Color modulate)
{
Position = position;
TextureCoordinates = textureCoordinates;
TextureCoordinates2 = textureCoordinates2;
Modulate = modulate;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
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))
Expand Down
13 changes: 9 additions & 4 deletions Robust.Client/Graphics/Clyde/Clyde.RenderHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal partial class Clyde
{
private RenderHandle _renderHandle = default!;

private sealed class RenderHandle : IRenderHandle
internal sealed class RenderHandle : IRenderHandle
{
private readonly Clyde _clyde;
private readonly IEntityManager _entities;
Expand Down Expand Up @@ -88,16 +88,21 @@ public void SetProjView(in Matrix3 proj, in Matrix3 view)
{
var clydeTexture = ExtractTexture(texture, in subRegion, out var csr);

var (w, h) = clydeTexture.Size;
var sr = new Box2(csr.Left / w, (h - csr.Bottom) / h, csr.Right / w, (h - csr.Top) / h);
var sr = WorldTextureBoundsToUV(clydeTexture, csr);

_clyde.DrawTexture(clydeTexture.TextureId, bl, br, tl, tr, in modulate, in sr);
}

internal static Box2 WorldTextureBoundsToUV(ClydeTexture texture, UIBox2 csr)
{
var (w, h) = texture.Size;
return new Box2(csr.Left / w, (h - csr.Bottom) / h, csr.Right / w, (h - csr.Top) / h);
}

/// <summary>
/// Converts a subRegion (px) into texture coords (0-1) of a given texture (cells of the textureAtlas).
/// </summary>
private static ClydeTexture ExtractTexture(Texture texture, in UIBox2? subRegion, out UIBox2 sr)
internal static ClydeTexture ExtractTexture(Texture texture, in UIBox2? subRegion, out UIBox2 sr)
{
if (texture is AtlasTexture atlas)
{
Expand Down
8 changes: 4 additions & 4 deletions Robust.Client/Graphics/Clyde/Clyde.Rendering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -578,10 +578,10 @@ private void DrawSetProjViewTransform(in Matrix3 proj, in Matrix3 view)

// TODO: split batch if necessary.
var vIdx = BatchVertexIndex;
BatchVertexData[vIdx + 0] = new Vertex2D(bl, texCoords.BottomLeft, modulate);
BatchVertexData[vIdx + 1] = new Vertex2D(br, texCoords.BottomRight, modulate);
BatchVertexData[vIdx + 2] = new Vertex2D(tr, texCoords.TopRight, modulate);
BatchVertexData[vIdx + 3] = new Vertex2D(tl, texCoords.TopLeft, modulate);
BatchVertexData[vIdx + 0] = new Vertex2D(bl, texCoords.BottomLeft, new Vector2(0, 0), modulate);
BatchVertexData[vIdx + 1] = new Vertex2D(br, texCoords.BottomRight, new Vector2(1, 0), modulate);
BatchVertexData[vIdx + 2] = new Vertex2D(tr, texCoords.TopRight, new Vector2(1, 1), modulate);
BatchVertexData[vIdx + 3] = new Vertex2D(tl, texCoords.TopLeft, new Vector2(0, 1), modulate);
BatchVertexIndex += 4;
QuadBatchIndexWrite(BatchIndexData, ref BatchIndexIndex, (ushort) vIdx);

Expand Down
2 changes: 1 addition & 1 deletion Robust.Client/Graphics/Clyde/Clyde.Textures.cs
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@ private void FlushTextureDispose()
}
}

private sealed class ClydeTexture : OwnedTexture
internal sealed class ClydeTexture : OwnedTexture
{
private readonly Clyde _clyde;
public readonly bool IsSrgb;
Expand Down
1 change: 1 addition & 0 deletions Robust.Client/Graphics/Clyde/Shaders/base-default.frag
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
varying highp vec2 UV;
varying highp vec2 UV2;
varying highp vec2 Pos;
varying highp vec4 VtxModulate;

Expand Down
5 changes: 4 additions & 1 deletion Robust.Client/Graphics/Clyde/Shaders/base-default.vert
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
/*layout (location = 0)*/ attribute vec2 aPos;
// Texture coordinates.
/*layout (location = 1)*/ attribute vec2 tCoord;
/*layout (location = 2)*/ attribute vec2 tCoord2;
// Colour modulation.
/*layout (location = 2)*/ attribute vec4 modulate;
/*layout (location = 3)*/ attribute vec4 modulate;

varying vec2 UV;
varying vec2 UV2;
varying vec2 Pos;
varying vec4 VtxModulate;

Expand Down Expand Up @@ -36,5 +38,6 @@ void main()
gl_Position = vec4(VERTEX, 0.0, 1.0);
Pos = (VERTEX + 1.0) / 2.0;
UV = mix(modifyUV.xy, modifyUV.zw, tCoord);
UV2 = tCoord2;
VtxModulate = zFromSrgb(modulate);
}
1 change: 1 addition & 0 deletions Robust.Client/Graphics/Clyde/Shaders/base-raw.frag
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
varying highp vec2 UV;
varying highp vec2 UV2;

uniform sampler2D lightMap;

Expand Down
5 changes: 4 additions & 1 deletion Robust.Client/Graphics/Clyde/Shaders/base-raw.vert
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
/*layout (location = 0)*/ attribute vec2 aPos;
// Texture coordinates.
/*layout (location = 1)*/ attribute vec2 tCoord;
/*layout (location = 2)*/ attribute vec2 tCoord2;
// Colour modulation.
/*layout (location = 2)*/ attribute vec4 modulate;
/*layout (location = 3)*/ attribute vec4 modulate;

varying vec2 UV;
varying vec2 UV2;

// Maybe we should merge these CPU side.
// idk yet.
Expand Down Expand Up @@ -40,6 +42,7 @@ void main()
vec2 VERTEX = aPos;

UV = tCoord;
UV2 = tCoord2;

// [SHADER_CODE]

Expand Down
41 changes: 6 additions & 35 deletions Robust.Client/Graphics/Drawing/DrawingHandleBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,43 +114,12 @@ public void SetTransform(in Vector2 position, in Angle rotation)
DrawPrimitives(primitiveTopology, White, indices, drawVertices);
}

private static void PadVerticesV2(ReadOnlySpan<Vector2> input, Span<DrawVertexUV2DColor> output, Color color)
private void PadVerticesV2(ReadOnlySpan<Vector2> input, Span<DrawVertexUV2DColor> output, Color color)
{
if (input.Length == 0)
return;

if (input.Length != output.Length)
{
throw new InvalidOperationException("Invalid lengths!");
}

var colorLinear = Color.FromSrgb(color);
var colorVec = Unsafe.As<Color, Vector128<float>>(ref colorLinear);
var uvVec = Vector128.Create(0, 0, 0.5f, 0.5f);
var maskVec = Vector128.Create(0xFFFFFFFF, 0xFFFFFFFF, 0, 0).AsSingle();

var simdVectors = (nuint)(input.Length / 2);
ref readonly var srcBase = ref Unsafe.As<Vector2, float>(ref Unsafe.AsRef(in input[0]));
ref var dstBase = ref Unsafe.As<DrawVertexUV2DColor, float>(ref output[0]);

for (nuint i = 0; i < simdVectors; i++)
{
var positions = Vector128.LoadUnsafe(in srcBase, i * 4);

var posColorLower = (positions & maskVec) | uvVec;
var posColorUpper = (Vector128.Shuffle(positions, Vector128.Create(2, 3, 0, 0)) & maskVec) | uvVec;

posColorLower.StoreUnsafe(ref dstBase, i * 16);
colorVec.StoreUnsafe(ref dstBase, i * 16 + 4);
posColorUpper.StoreUnsafe(ref dstBase, i * 16 + 8);
colorVec.StoreUnsafe(ref dstBase, i * 16 + 12);
}

var lastPos = (int)simdVectors * 2;
if (lastPos != output.Length)
Color colorLinear = Color.FromSrgb(color);
for (var i = 0; i < output.Length; i++)
{
// Odd number of vertices. Handle the last manually.
output[lastPos] = new DrawVertexUV2DColor(input[lastPos], new Vector2(0.5f, 0.5f), colorLinear);
output[i] = new DrawVertexUV2DColor(input[i], new Vector2(0.5f, 0.5f), colorLinear);
}
}

Expand Down Expand Up @@ -268,6 +237,8 @@ public struct DrawVertexUV2DColor
{
public Vector2 Position;
public Vector2 UV;
public Vector2 UV2;

/// <summary>
/// Modulation colour for this vertex.
/// Note that this color is in linear space.
Expand Down
Loading

0 comments on commit 4033d96

Please sign in to comment.