diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index c981f4460e0..da7acdce7b1 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -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
diff --git a/Robust.Client/GameObjects/Components/Renderable/SpriteComponent.cs b/Robust.Client/GameObjects/Components/Renderable/SpriteComponent.cs
index f42d3a8afa6..f6467daa075 100644
--- a/Robust.Client/GameObjects/Components/Renderable/SpriteComponent.cs
+++ b/Robust.Client/GameObjects/Components/Renderable/SpriteComponent.cs
@@ -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;
@@ -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
{
@@ -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))
{
@@ -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))
@@ -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;
@@ -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.
@@ -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)
@@ -2096,6 +2146,17 @@ internal void AdvanceFrameAnimation(RSI.State state)
}
}
+ ///
+ /// Instantiated version of .
+ /// Has actually resolved to a a real key.
+ ///
+ 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/"))
diff --git a/Robust.Client/Graphics/Clyde/Clyde.Constants.cs b/Robust.Client/Graphics/Clyde/Clyde.Constants.cs
index 94b1fe3acf6..2ae93219693 100644
--- a/Robust.Client/Graphics/Clyde/Clyde.Constants.cs
+++ b/Robust.Client/Graphics/Clyde/Clyde.Constants.cs
@@ -6,7 +6,8 @@ private static readonly (string, uint)[] BaseShaderAttribLocations =
{
("aPos", 0),
("tCoord", 1),
- ("modulate", 2)
+ ("tCoord2", 2),
+ ("modulate", 3)
};
private const int UniIModUV = 0;
diff --git a/Robust.Client/Graphics/Clyde/Clyde.Layout.cs b/Robust.Client/Graphics/Clyde/Clyde.Layout.cs
index 07a0f145f76..7f6ab086b71 100644
--- a/Robust.Client/Graphics/Clyde/Clyde.Layout.cs
+++ b/Robust.Client/Graphics/Clyde/Clyde.Layout.cs
@@ -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:
@@ -37,6 +40,7 @@ private readonly struct Vertex2D
{
public readonly Vector2 Position;
public readonly Vector2 TextureCoordinates;
+ public readonly Vector2 TextureCoordinates2;
// Note that this color is in linear space.
public readonly Color Modulate;
@@ -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))
diff --git a/Robust.Client/Graphics/Clyde/Clyde.RenderHandle.cs b/Robust.Client/Graphics/Clyde/Clyde.RenderHandle.cs
index b3453a7574c..b51d70c0165 100644
--- a/Robust.Client/Graphics/Clyde/Clyde.RenderHandle.cs
+++ b/Robust.Client/Graphics/Clyde/Clyde.RenderHandle.cs
@@ -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;
@@ -88,16 +88,21 @@ public void DrawTextureWorld(Texture texture, Vector2 bl, Vector2 br, Vector2 tl
{
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);
+ }
+
///
/// Converts a subRegion (px) into texture coords (0-1) of a given texture (cells of the textureAtlas).
///
- 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)
{
diff --git a/Robust.Client/Graphics/Clyde/Clyde.Rendering.cs b/Robust.Client/Graphics/Clyde/Clyde.Rendering.cs
index 8a58541e575..da77262b139 100644
--- a/Robust.Client/Graphics/Clyde/Clyde.Rendering.cs
+++ b/Robust.Client/Graphics/Clyde/Clyde.Rendering.cs
@@ -578,10 +578,10 @@ private void DrawTexture(ClydeHandle texture, Vector2 bl, Vector2 br, Vector2 tl
// 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);
diff --git a/Robust.Client/Graphics/Clyde/Clyde.Textures.cs b/Robust.Client/Graphics/Clyde/Clyde.Textures.cs
index 6b8180917cb..4dc90a723e0 100644
--- a/Robust.Client/Graphics/Clyde/Clyde.Textures.cs
+++ b/Robust.Client/Graphics/Clyde/Clyde.Textures.cs
@@ -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;
diff --git a/Robust.Client/Graphics/Clyde/Shaders/base-default.frag b/Robust.Client/Graphics/Clyde/Shaders/base-default.frag
index 15bdb5b1252..a0830f17f4b 100644
--- a/Robust.Client/Graphics/Clyde/Shaders/base-default.frag
+++ b/Robust.Client/Graphics/Clyde/Shaders/base-default.frag
@@ -1,4 +1,5 @@
varying highp vec2 UV;
+varying highp vec2 UV2;
varying highp vec2 Pos;
varying highp vec4 VtxModulate;
diff --git a/Robust.Client/Graphics/Clyde/Shaders/base-default.vert b/Robust.Client/Graphics/Clyde/Shaders/base-default.vert
index 28d21c4f137..51ba6649c94 100644
--- a/Robust.Client/Graphics/Clyde/Shaders/base-default.vert
+++ b/Robust.Client/Graphics/Clyde/Shaders/base-default.vert
@@ -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;
@@ -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);
}
diff --git a/Robust.Client/Graphics/Clyde/Shaders/base-raw.frag b/Robust.Client/Graphics/Clyde/Shaders/base-raw.frag
index e72eaeebbf1..b62afbb8d56 100644
--- a/Robust.Client/Graphics/Clyde/Shaders/base-raw.frag
+++ b/Robust.Client/Graphics/Clyde/Shaders/base-raw.frag
@@ -1,4 +1,5 @@
varying highp vec2 UV;
+varying highp vec2 UV2;
uniform sampler2D lightMap;
diff --git a/Robust.Client/Graphics/Clyde/Shaders/base-raw.vert b/Robust.Client/Graphics/Clyde/Shaders/base-raw.vert
index e5b8cf27fbc..11fee7a4f6c 100644
--- a/Robust.Client/Graphics/Clyde/Shaders/base-raw.vert
+++ b/Robust.Client/Graphics/Clyde/Shaders/base-raw.vert
@@ -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.
@@ -40,6 +42,7 @@ void main()
vec2 VERTEX = aPos;
UV = tCoord;
+ UV2 = tCoord2;
// [SHADER_CODE]
diff --git a/Robust.Client/Graphics/Drawing/DrawingHandleBase.cs b/Robust.Client/Graphics/Drawing/DrawingHandleBase.cs
index 525fa741f43..56259861687 100644
--- a/Robust.Client/Graphics/Drawing/DrawingHandleBase.cs
+++ b/Robust.Client/Graphics/Drawing/DrawingHandleBase.cs
@@ -114,43 +114,12 @@ public void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, ReadOnlySpan
DrawPrimitives(primitiveTopology, White, indices, drawVertices);
}
- private static void PadVerticesV2(ReadOnlySpan input, Span output, Color color)
+ private void PadVerticesV2(ReadOnlySpan input, Span 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>(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(ref Unsafe.AsRef(in input[0]));
- ref var dstBase = ref Unsafe.As(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);
}
}
@@ -268,6 +237,8 @@ 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.
diff --git a/Robust.Client/ResourceManagement/ResourceCache.Preload.cs b/Robust.Client/ResourceManagement/ResourceCache.Preload.cs
index f698171adbf..cc567565ef5 100644
--- a/Robust.Client/ResourceManagement/ResourceCache.Preload.cs
+++ b/Robust.Client/ResourceManagement/ResourceCache.Preload.cs
@@ -10,6 +10,7 @@
using Robust.Shared.Audio;
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
+using Robust.Shared.Graphics;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Maths;
@@ -142,6 +143,26 @@ private void PreloadRsis(ISawmill sawmill)
}
});
+ // Do not meta-atlas RSIs with custom load parameters.
+ var atlasList = rsiList.Where(x => x.LoadParameters == TextureLoadParameters.Default).ToArray();
+ var nonAtlasList = rsiList.Where(x => x.LoadParameters != TextureLoadParameters.Default).ToArray();
+
+ foreach (var data in nonAtlasList)
+ {
+ if (data.Bad)
+ continue;
+
+ try
+ {
+ RSIResource.LoadTexture(Clyde, data);
+ }
+ catch (Exception e)
+ {
+ sawmill.Error($"Exception while loading RSI {data.Path}:\n{e}");
+ data.Bad = true;
+ }
+ }
+
// This combines individual RSI atlases into larger atlases to reduce draw batches. currently this is a VERY
// lazy bundling and is not at all compact, its basically an atlas of RSI atlases. Really what this should
// try to do is to have each RSI write directly to the atlas, rather than having each RSI write to its own
@@ -155,7 +176,7 @@ private void PreloadRsis(ISawmill sawmill)
// TODO allow RSIs to opt out (useful for very big & rare RSIs)
// TODO combine with (non-rsi) texture atlas?
- Array.Sort(rsiList, (b, a) => (b.AtlasSheet?.Height ?? 0).CompareTo(a.AtlasSheet?.Height ?? 0));
+ Array.Sort(atlasList, (b, a) => (b.AtlasSheet?.Height ?? 0).CompareTo(a.AtlasSheet?.Height ?? 0));
// Each RSI sub atlas has a different size.
// Even if we iterate through them once to estimate total area, I have NFI how to sanely estimate an optimal square-texture size.
@@ -167,9 +188,9 @@ private void PreloadRsis(ISawmill sawmill)
Vector2i offset = default;
int finalized = -1;
int atlasCount = 0;
- for (int i = 0; i < rsiList.Length; i++)
+ for (int i = 0; i < atlasList.Length; i++)
{
- var rsi = rsiList[i];
+ var rsi = atlasList[i];
if (rsi.Bad)
continue;
@@ -200,14 +221,14 @@ private void PreloadRsis(ISawmill sawmill)
var height = offset.Y + deltaY;
var croppedSheet = new Image(maxSize, height);
sheet.Blit(new UIBox2i(0, 0, maxSize, height), croppedSheet, default);
- FinalizeMetaAtlas(rsiList.Length - 1, croppedSheet);
+ FinalizeMetaAtlas(atlasList.Length - 1, croppedSheet);
void FinalizeMetaAtlas(int toIndex, Image sheet)
{
var atlas = Clyde.LoadTextureFromImage(sheet);
for (int i = finalized + 1; i <= toIndex; i++)
{
- var rsi = rsiList[i];
+ var rsi = atlasList[i];
rsi.AtlasTexture = atlas;
}
@@ -255,9 +276,10 @@ void FinalizeMetaAtlas(int toIndex, Image sheet)
}
sawmill.Debug(
- "Preloaded {CountLoaded} RSIs into {CountAtlas} Atlas(es?) ({CountErrored} errored) in {LoadTime}",
+ "Preloaded {CountLoaded} RSIs into {CountAtlas} Atlas(es?) ({CountNotAtlas} not atlassed, {CountErrored} errored) in {LoadTime}",
rsiList.Length,
atlasCount,
+ nonAtlasList.Length,
errors,
sw.Elapsed);
diff --git a/Robust.Client/ResourceManagement/ResourceTypes/RSIResource.cs b/Robust.Client/ResourceManagement/ResourceTypes/RSIResource.cs
index f207b583288..7f7b92525ca 100644
--- a/Robust.Client/ResourceManagement/ResourceTypes/RSIResource.cs
+++ b/Robust.Client/ResourceManagement/ResourceTypes/RSIResource.cs
@@ -40,17 +40,21 @@ public override void Load(IDependencyCollection dependencies, ResPath path)
var loadStepData = new LoadStepData {Path = path};
var manager = dependencies.Resolve();
LoadPreTexture(manager, loadStepData);
-
- loadStepData.AtlasTexture = dependencies.Resolve().LoadTextureFromImage(
- loadStepData.AtlasSheet,
- loadStepData.Path.ToString());
-
+ LoadTexture(dependencies.Resolve(), loadStepData);
LoadPostTexture(loadStepData);
LoadFinish(dependencies.Resolve(), loadStepData);
loadStepData.AtlasSheet.Dispose();
}
+ internal static void LoadTexture(IClyde clyde, LoadStepData loadStepData)
+ {
+ loadStepData.AtlasTexture = clyde.LoadTextureFromImage(
+ loadStepData.AtlasSheet,
+ loadStepData.Path.ToString(),
+ loadStepData.LoadParameters);
+ }
+
internal static void LoadPreTexture(IResourceManager manager, LoadStepData data)
{
var manifestPath = data.Path / "meta.json";
@@ -178,6 +182,7 @@ internal static void LoadPreTexture(IResourceManager manager, LoadStepData data)
data.FrameSize = frameSize;
data.DimX = dimensionX;
data.CallbackOffsets = callbackOffsets;
+ data.LoadParameters = metadata.LoadParameters;
}
internal static void LoadPostTexture(LoadStepData data)
@@ -380,6 +385,7 @@ internal sealed class LoadStepData
public Texture AtlasTexture = default!;
public Vector2i AtlasOffset;
public RSI Rsi = default!;
+ public TextureLoadParameters LoadParameters;
}
internal struct StateReg
diff --git a/Robust.Shared/GameObjects/Components/Renderable/SpriteLayerData.cs b/Robust.Shared/GameObjects/Components/Renderable/SpriteLayerData.cs
index 68920261139..531a28a19c3 100644
--- a/Robust.Shared/GameObjects/Components/Renderable/SpriteLayerData.cs
+++ b/Robust.Shared/GameObjects/Components/Renderable/SpriteLayerData.cs
@@ -29,9 +29,50 @@ public sealed partial class PrototypeLayerData
[DataField("map")] public HashSet? MapKeys;
[DataField("renderingStrategy")] public LayerRenderingStrategy? RenderingStrategy;
+ ///
+ /// If set, indicates that this sprite layer should instead be used to copy into shader parameters on another layer.
+ ///
+ ///
+ ///
+ /// If set, this sprite layer is not rendered. Instead, the "result" of rendering it (exact sprite layer and such)
+ /// are copied into the shader parameters of another object,
+ /// specified by the .
+ ///
+ ///
+ /// The specified layer must have a shader set. When it does, the shader's
+ ///
+ ///
+ /// Note that sprite layers are processed in-order, so to avoid 1-frame delays,
+ /// the layer doing the copying should occur BEFORE the layer being copied into.
+ ///
+ ///
+ [DataField] public PrototypeCopyToShaderParameters? CopyToShaderParameters;
+
[DataField] public bool Cycle;
}
+///
+/// Stores parameters for .
+///
+[Serializable, NetSerializable, DataDefinition]
+public sealed partial class PrototypeCopyToShaderParameters
+{
+ ///
+ /// The map key of the layer that will have its shader modified.
+ ///
+ [DataField(required: true)] public string LayerKey;
+
+ ///
+ /// The name of the shader parameter that will receive the actual selected texture.
+ ///
+ [DataField] public string? ParameterTexture;
+
+ ///
+ /// The name of the shader parameter that will receive UVs to select the sprite in .
+ ///
+ [DataField] public string? ParameterUV;
+}
+
[Serializable, NetSerializable]
public enum LayerRenderingStrategy
{
diff --git a/Robust.Shared/Graphics/TextureLoadParameters.cs b/Robust.Shared/Graphics/TextureLoadParameters.cs
index 2ec1449c73a..b1764dad6c0 100644
--- a/Robust.Shared/Graphics/TextureLoadParameters.cs
+++ b/Robust.Shared/Graphics/TextureLoadParameters.cs
@@ -1,3 +1,4 @@
+using System;
using JetBrains.Annotations;
using Robust.Shared.Utility;
using YamlDotNet.RepresentationModel;
@@ -8,7 +9,7 @@ namespace Robust.Shared.Graphics;
/// Flags for loading of textures.
///
[PublicAPI]
-public struct TextureLoadParameters
+public struct TextureLoadParameters : IEquatable
{
///
/// The default sampling parameters for the texture.
@@ -41,4 +42,29 @@ public static TextureLoadParameters FromYaml(YamlMappingNode yaml)
SampleParameters = TextureSampleParameters.Default,
Srgb = true
};
-}
\ No newline at end of file
+
+ public bool Equals(TextureLoadParameters other)
+ {
+ return SampleParameters.Equals(other.SampleParameters) && Srgb == other.Srgb;
+ }
+
+ public override bool Equals(object? obj)
+ {
+ return obj is TextureLoadParameters other && Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(SampleParameters, Srgb);
+ }
+
+ public static bool operator ==(TextureLoadParameters left, TextureLoadParameters right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(TextureLoadParameters left, TextureLoadParameters right)
+ {
+ return !left.Equals(right);
+ }
+}
diff --git a/Robust.Shared/Graphics/TextureSampleParameters.cs b/Robust.Shared/Graphics/TextureSampleParameters.cs
index 2e3eb3a5e0f..3ae52db8632 100644
--- a/Robust.Shared/Graphics/TextureSampleParameters.cs
+++ b/Robust.Shared/Graphics/TextureSampleParameters.cs
@@ -12,7 +12,7 @@ namespace Robust.Shared.Graphics;
/// with different sampling parameters than the base texture.
///
[PublicAPI]
-public struct TextureSampleParameters
+public struct TextureSampleParameters : IEquatable
{
// NOTE: If somebody is gonna add support for 3D/1D textures, change this doc comment.
// See the note on this page for why: https://www.khronos.org/opengl/wiki/Sampler_Object#Filtering
@@ -62,4 +62,29 @@ public static TextureSampleParameters FromYaml(YamlMappingNode node)
Filter = false,
WrapMode = TextureWrapMode.None
};
-}
\ No newline at end of file
+
+ public bool Equals(TextureSampleParameters other)
+ {
+ return Filter == other.Filter && WrapMode == other.WrapMode;
+ }
+
+ public override bool Equals(object? obj)
+ {
+ return obj is TextureSampleParameters other && Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(Filter, (int) WrapMode);
+ }
+
+ public static bool operator ==(TextureSampleParameters left, TextureSampleParameters right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(TextureSampleParameters left, TextureSampleParameters right)
+ {
+ return !left.Equals(right);
+ }
+}
diff --git a/Robust.Shared/Resources/RsiLoading.cs b/Robust.Shared/Resources/RsiLoading.cs
index 37779d66a3c..66a45396407 100644
--- a/Robust.Shared/Resources/RsiLoading.cs
+++ b/Robust.Shared/Resources/RsiLoading.cs
@@ -2,6 +2,7 @@
using System.IO;
using System.Text.Json;
using JetBrains.Annotations;
+using Robust.Shared.Graphics;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
@@ -93,7 +94,17 @@ internal static RsiMetadata LoadRsiMetadata(Stream manifestFile)
states[stateI] = new StateMetadata(stateName, dirValue, delays);
}
- return new RsiMetadata(size, states);
+ var textureParams = TextureLoadParameters.Default;
+ if (manifestJson.Load is { } load)
+ {
+ textureParams = new TextureLoadParameters
+ {
+ SampleParameters = TextureSampleParameters.Default,
+ Srgb = load.Srgb
+ };
+ }
+
+ return new RsiMetadata(size, states, textureParams);
}
public static void Warmup()
@@ -103,16 +114,11 @@ public static void Warmup()
JsonSerializer.Deserialize(warmupJson, SerializerOptions);
}
- internal sealed class RsiMetadata
+ internal sealed class RsiMetadata(Vector2i size, StateMetadata[] states, TextureLoadParameters loadParameters)
{
- public readonly Vector2i Size;
- public readonly StateMetadata[] States;
-
- public RsiMetadata(Vector2i size, StateMetadata[] states)
- {
- Size = size;
- States = states;
- }
+ public readonly Vector2i Size = size;
+ public readonly StateMetadata[] States = states;
+ public readonly TextureLoadParameters LoadParameters = loadParameters;
}
internal sealed class StateMetadata
@@ -134,10 +140,13 @@ public StateMetadata(string stateId, int dirCount, float[][] delays)
// To be directly deserialized.
[UsedImplicitly]
- private sealed record RsiJsonMetadata(Vector2i Size, StateJsonMetadata[] States);
+ private sealed record RsiJsonMetadata(Vector2i Size, StateJsonMetadata[] States, RsiJsonLoad? Load);
[UsedImplicitly]
private sealed record StateJsonMetadata(string Name, int? Directions, float[][]? Delays);
+
+ [UsedImplicitly]
+ private sealed record RsiJsonLoad(bool Srgb = true);
}
[Serializable]