mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
* 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.
168 lines
5.1 KiB
C#
168 lines
5.1 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Text.Json;
|
|
using JetBrains.Annotations;
|
|
using Robust.Shared.Graphics;
|
|
using Robust.Shared.Maths;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Robust.Shared.Resources;
|
|
|
|
/// <summary>
|
|
/// RSI manipulation and loading behavior. Server (Packaging/ACZ) and client (loading).
|
|
/// </summary>
|
|
internal static class RsiLoading
|
|
{
|
|
private static readonly float[] OneArray = {1};
|
|
|
|
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web);
|
|
|
|
/// <summary>
|
|
/// The minimum version of RSI we can load.
|
|
/// </summary>
|
|
public const uint MINIMUM_RSI_VERSION = 1;
|
|
|
|
/// <summary>
|
|
/// The maximum version of RSI we can load.
|
|
/// </summary>
|
|
public const uint MAXIMUM_RSI_VERSION = 1;
|
|
|
|
internal static RsiMetadata LoadRsiMetadata(Stream manifestFile)
|
|
{
|
|
var manifestJson = JsonSerializer.Deserialize<RsiJsonMetadata>(manifestFile, SerializerOptions);
|
|
|
|
if (manifestJson == null)
|
|
throw new RSILoadException($"Manifest JSON failed to deserialize!");
|
|
|
|
var size = manifestJson.Size;
|
|
var states = new StateMetadata[manifestJson.States.Length];
|
|
|
|
for (var stateI = 0; stateI < manifestJson.States.Length; stateI++)
|
|
{
|
|
var stateObject = manifestJson.States[stateI];
|
|
var stateName = stateObject.Name;
|
|
int dirValue;
|
|
|
|
if (stateObject.Directions is { } dirVal)
|
|
{
|
|
dirValue = dirVal;
|
|
if (dirVal is not (1 or 4 or 8))
|
|
{
|
|
throw new RSILoadException(
|
|
$"Invalid direction for state '{stateName}': {dirValue}. Expected 1, 4 or 8");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dirValue = 1;
|
|
}
|
|
|
|
// We can ignore selectors and flags for now,
|
|
// because they're not used yet!
|
|
|
|
// Get the lists of delays.
|
|
float[][] delays;
|
|
if (stateObject.Delays != null)
|
|
{
|
|
delays = stateObject.Delays;
|
|
|
|
if (delays.Length != dirValue)
|
|
{
|
|
throw new RSILoadException(
|
|
$"Direction frames list count ({dirValue}) does not match amount of delays specified ({delays.Length}) for state '{stateName}'.");
|
|
}
|
|
|
|
for (var i = 0; i < delays.Length; i++)
|
|
{
|
|
var delayList = delays[i];
|
|
if (delayList.Length == 0)
|
|
{
|
|
delays[i] = OneArray;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
delays = new float[dirValue][];
|
|
// No delays specified, default to 1 frame per dir.
|
|
for (var i = 0; i < dirValue; i++)
|
|
{
|
|
delays[i] = OneArray;
|
|
}
|
|
}
|
|
|
|
states[stateI] = new StateMetadata(stateName, dirValue, delays);
|
|
}
|
|
|
|
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()
|
|
{
|
|
// Just a random RSI I pulled from SS14.
|
|
const string warmupJson = @"{""version"":1,""license"":""CC-BY-SA-3.0"",""copyright"":""Space Wizards Federation"",""size"":{""x"":32,""y"":32},""states"":[{""name"":""mono""}]}";
|
|
JsonSerializer.Deserialize<RsiJsonMetadata>(warmupJson, SerializerOptions);
|
|
}
|
|
|
|
internal sealed class RsiMetadata(Vector2i size, StateMetadata[] states, TextureLoadParameters loadParameters)
|
|
{
|
|
public readonly Vector2i Size = size;
|
|
public readonly StateMetadata[] States = states;
|
|
public readonly TextureLoadParameters LoadParameters = loadParameters;
|
|
}
|
|
|
|
internal sealed class StateMetadata
|
|
{
|
|
public readonly string StateId;
|
|
public readonly int DirCount;
|
|
public readonly float[][] Delays;
|
|
|
|
public StateMetadata(string stateId, int dirCount, float[][] delays)
|
|
{
|
|
StateId = stateId;
|
|
DirCount = dirCount;
|
|
|
|
Delays = delays;
|
|
|
|
DebugTools.Assert(delays.Length == DirCount);
|
|
}
|
|
}
|
|
|
|
// To be directly deserialized.
|
|
[UsedImplicitly]
|
|
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]
|
|
[Virtual]
|
|
public class RSILoadException : Exception
|
|
{
|
|
public RSILoadException()
|
|
{
|
|
}
|
|
|
|
public RSILoadException(string message) : base(message)
|
|
{
|
|
}
|
|
|
|
public RSILoadException(string message, Exception inner) : base(message, inner)
|
|
{
|
|
}
|
|
}
|