From 69172ac69fab48d0ea93b0270fa7d9b814bfc652 Mon Sep 17 00:00:00 2001 From: notquitehadouken Date: Thu, 23 Jan 2025 16:21:06 -0600 Subject: [PATCH] Yes --- .../Components/Renderable/SpriteComponent.cs | 20 ++++---- .../GameObjects/EntitySystems/SpriteSystem.cs | 4 +- Robust.Client/Graphics/Clyde/Clyde.HLR.cs | 18 +++++-- .../Graphics/Clyde/Clyde.RenderHandle.cs | 20 +++++--- .../Graphics/Drawing/DrawingHandleWorld.cs | 4 +- .../ResourceTypes/RSIResource.cs | 50 +++++++++++++++---- Robust.Shared/Resources/RsiLoading.cs | 9 ++-- 7 files changed, 88 insertions(+), 37 deletions(-) diff --git a/Robust.Client/GameObjects/Components/Renderable/SpriteComponent.cs b/Robust.Client/GameObjects/Components/Renderable/SpriteComponent.cs index a4bdcae91b7..7a8d0a8814d 100644 --- a/Robust.Client/GameObjects/Components/Renderable/SpriteComponent.cs +++ b/Robust.Client/GameObjects/Components/Renderable/SpriteComponent.cs @@ -1289,7 +1289,7 @@ public bool SnapCardinals [ViewVariables(VVAccess.ReadWrite)] public bool NoRotation { get => _screenLock; set => _screenLock = value; } - internal void RenderInternal(DrawingHandleWorld drawingHandle, Angle eyeRotation, Angle worldRotation, Vector2 worldPosition, Direction? overrideDirection) + internal void RenderInternal(DrawingHandleWorld drawingHandle, Angle eyeRotation, Angle worldRotation, Vector2 worldPosition, Direction? overrideDirection, bool normal = false) { var angle = worldRotation + eyeRotation; // angle on-screen. Used to decide the direction of 4/8 directional RSIs angle = angle.Reduced().FlipPositive(); // Reduce the angles to fix math shenanigans @@ -1324,16 +1324,16 @@ internal void RenderInternal(DrawingHandleWorld drawingHandle, Angle eyeRotation switch (layer.RenderingStrategy) { case LayerRenderingStrategy.NoRotation: - layer.Render(drawingHandle, ref transformNoRot, angle, overrideDirection); + layer.Render(drawingHandle, ref transformNoRot, angle, overrideDirection, normal: normal); break; case LayerRenderingStrategy.SnapToCardinals: - layer.Render(drawingHandle, ref transformSnap, angle, overrideDirection); + layer.Render(drawingHandle, ref transformSnap, angle, overrideDirection, normal: normal); break; case LayerRenderingStrategy.Default: - layer.Render(drawingHandle, ref transformDefault, angle, overrideDirection); + layer.Render(drawingHandle, ref transformDefault, angle, overrideDirection, normal: normal); break; case LayerRenderingStrategy.UseSpriteStrategy: - layer.Render(drawingHandle, ref transformSprite, angle, overrideDirection); + layer.Render(drawingHandle, ref transformSprite, angle, overrideDirection, normal: normal); break; default: Logger.Error($"Tried to render a layer with unknown rendering stragegy: {layer.RenderingStrategy}"); @@ -1346,7 +1346,7 @@ internal void RenderInternal(DrawingHandleWorld drawingHandle, Angle eyeRotation { foreach (var layer in Layers) { - layer.Render(drawingHandle, ref transformSprite, angle, overrideDirection); + layer.Render(drawingHandle, ref transformSprite, angle, overrideDirection, normal: normal); } } } @@ -2017,7 +2017,7 @@ public static RsiDirection GetDirection(RsiDirectionType dirType, Angle angle) /// /// Render a layer. This assumes that the input angle is between 0 and 2pi. /// - internal void Render(DrawingHandleWorld drawingHandle, ref Matrix3x2 spriteMatrix, Angle angle, Direction? overrideDirection) + internal void Render(DrawingHandleWorld drawingHandle, ref Matrix3x2 spriteMatrix, Angle angle, Direction? overrideDirection, bool normal = false) { if (!Visible || Blank) return; @@ -2042,7 +2042,7 @@ internal void Render(DrawingHandleWorld drawingHandle, ref Matrix3x2 spriteMatri var transformMatrix = Matrix3x2.Multiply(layerMatrix, spriteMatrix); drawingHandle.SetTransform(in transformMatrix); - RenderTexture(drawingHandle, texture); + RenderTexture(drawingHandle, texture, normal: normal); } else { @@ -2072,7 +2072,7 @@ internal void Render(DrawingHandleWorld drawingHandle, ref Matrix3x2 spriteMatri } } - private void RenderTexture(DrawingHandleWorld drawingHandle, Texture texture) + private void RenderTexture(DrawingHandleWorld drawingHandle, Texture texture, bool normal = false) { if (Shader != null) drawingHandle.UseShader(Shader); @@ -2081,7 +2081,7 @@ private void RenderTexture(DrawingHandleWorld drawingHandle, Texture texture) var textureSize = texture.Size / (float)EyeManager.PixelsPerMeter; var quad = Box2.FromDimensions(textureSize/-2, textureSize); - drawingHandle.DrawTextureRectRegion(texture, quad, layerColor); + drawingHandle.DrawTextureRectRegion(texture, quad, layerColor, normal: normal); if (Shader != null) drawingHandle.UseShader(null); diff --git a/Robust.Client/GameObjects/EntitySystems/SpriteSystem.cs b/Robust.Client/GameObjects/EntitySystems/SpriteSystem.cs index e92f0df258c..5ef7d184811 100644 --- a/Robust.Client/GameObjects/EntitySystems/SpriteSystem.cs +++ b/Robust.Client/GameObjects/EntitySystems/SpriteSystem.cs @@ -46,12 +46,12 @@ public sealed partial class SpriteSystem : EntitySystem private ISawmill _sawmill = default!; - internal void Render(EntityUid uid, SpriteComponent sprite, DrawingHandleWorld drawingHandle, Angle eyeRotation, in Angle worldRotation, in Vector2 worldPosition) + internal void Render(EntityUid uid, SpriteComponent sprite, DrawingHandleWorld drawingHandle, Angle eyeRotation, in Angle worldRotation, in Vector2 worldPosition, bool normal = false) { if (!sprite.IsInert) _queuedFrameUpdate.Add(uid); - sprite.RenderInternal(drawingHandle, eyeRotation, worldRotation, worldPosition, sprite.EnableDirectionOverride ? sprite.DirectionOverride : null); + sprite.RenderInternal(drawingHandle, eyeRotation, worldRotation, worldPosition, sprite.EnableDirectionOverride ? sprite.DirectionOverride : null, normal: normal); } public override void Initialize() diff --git a/Robust.Client/Graphics/Clyde/Clyde.HLR.cs b/Robust.Client/Graphics/Clyde/Clyde.HLR.cs index 11ffc62737f..ce66ce8f4c5 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.HLR.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.HLR.cs @@ -247,7 +247,7 @@ private List GetOverlaysForSpace(OverlaySpace space) return ScreenBufferTexture; } - private void DrawEntities(Viewport viewport, Box2Rotated worldBounds, Box2 worldAABB, IEye eye) + private void DrawEntities(Viewport viewport, Box2Rotated worldBounds, Box2 worldAABB, IEye eye, bool normal = false) { var mapId = eye.Position.MapId; if (mapId == MapId.Nullspace) @@ -288,7 +288,7 @@ private void DrawEntities(Viewport viewport, Box2Rotated worldBounds, Box2 world } Vector2i roundedPos = default; - if (entry.Sprite.PostShader != null) + if (entry.Sprite.PostShader != null && !normal) { // get the size of the sprite on screen, scaled slightly to allow for shaders that increase the final sprite size. var screenSpriteSize = (Vector2i)(entry.SpriteScreenBB.Size * PostShadeScale).Rounded(); @@ -353,7 +353,13 @@ private void DrawEntities(Viewport viewport, Box2Rotated worldBounds, Box2 world } } - spriteSystem.Render(entry.Uid, entry.Sprite, _renderHandle.DrawingHandleWorld, eye.Rotation, in entry.WorldRot, in entry.WorldPos); + spriteSystem.Render(entry.Uid, + entry.Sprite, + _renderHandle.DrawingHandleWorld, + eye.Rotation, + in entry.WorldRot, + in entry.WorldPos, + normal: normal); if (entry.Sprite.PostShader != null && entityPostRenderTarget != null) { @@ -481,6 +487,12 @@ private void RenderViewport(Viewport viewport) if (eye.Position.MapId != MapId.Nullspace) { + using (DebugGroup("EntityNormals")) + using (_prof.Group("EntityNormals")) + { + DrawEntities(viewport, worldBounds, worldAABB, eye, normal: true); + } + using (DebugGroup("Lights")) using (_prof.Group("Lights")) { diff --git a/Robust.Client/Graphics/Clyde/Clyde.RenderHandle.cs b/Robust.Client/Graphics/Clyde/Clyde.RenderHandle.cs index 57b4c038eac..4fe5fc12578 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.RenderHandle.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.RenderHandle.cs @@ -84,9 +84,9 @@ public void DrawTextureScreen(Texture texture, Vector2 bl, Vector2 br, Vector2 t /// A color to multiply the texture by when shading. /// The four corners of the texture sub region in px. public void DrawTextureWorld(Texture texture, Vector2 bl, Vector2 br, Vector2 tl, Vector2 tr, - Color modulate, in UIBox2? subRegion) + Color modulate, in UIBox2? subRegion, bool normal = false) { - var clydeTexture = ExtractTexture(texture, in subRegion, out var csr); + var clydeTexture = ExtractTexture(texture, in subRegion, out var csr, normal: normal); var sr = WorldTextureBoundsToUV(clydeTexture, csr); @@ -102,14 +102,17 @@ internal static Box2 WorldTextureBoundsToUV(ClydeTexture texture, UIBox2 csr) /// /// Converts a subRegion (px) into texture coords (0-1) of a given texture (cells of the textureAtlas). /// - internal static ClydeTexture ExtractTexture(Texture texture, in UIBox2? subRegion, out UIBox2 sr) + internal static ClydeTexture ExtractTexture(Texture texture, in UIBox2? subRegion, out UIBox2 sr, bool normal = false) { + normal = false; if (texture is AtlasTexture atlas) { texture = atlas.SourceTexture; if (subRegion.HasValue) { var offset = atlas.SubRegion.TopLeft; + if (normal) + offset += Vector2.UnitX * (texture.Width >> 1); sr = new UIBox2( subRegion.Value.TopLeft + offset, subRegion.Value.BottomRight + offset); @@ -121,7 +124,8 @@ internal static ClydeTexture ExtractTexture(Texture texture, in UIBox2? subRegio } else { - sr = subRegion ?? new UIBox2(0, 0, texture.Width, texture.Height); + var normOffset = normal ? texture.Width >> 1 : 0; + sr = subRegion ?? new UIBox2(normOffset, 0, texture.Width >> 1 + normOffset, texture.Height); } var clydeTexture = (ClydeTexture) texture; @@ -513,12 +517,12 @@ public override void DrawRect(in Box2Rotated rect, Color color, bool filled = tr /// A color to multiply the texture by when shading. /// The four corners of the texture sub region in px. public override void DrawTextureRectRegion(Texture texture, Box2 quad, - Color? modulate = null, UIBox2? subRegion = null) + Color? modulate = null, UIBox2? subRegion = null, bool normal = false) { var color = (modulate ?? Color.White) * Modulate; _renderHandle.DrawTextureWorld(texture, quad.BottomLeft, quad.BottomRight, - quad.TopLeft, quad.TopRight, color, in subRegion); + quad.TopLeft, quad.TopRight, color, in subRegion, normal: normal); } /// @@ -531,12 +535,12 @@ public override void DrawTextureRectRegion(Texture texture, Box2 quad, /// A color to multiply the texture by when shading. /// The four corners of the texture sub region in px. public override void DrawTextureRectRegion(Texture texture, in Box2Rotated quad, - Color? modulate = null, UIBox2? subRegion = null) + Color? modulate = null, UIBox2? subRegion = null, bool normal = false) { var color = (modulate ?? Color.White) * Modulate; _renderHandle.DrawTextureWorld(texture, quad.BottomLeft, quad.BottomRight, - quad.TopLeft, quad.TopRight, color, in subRegion); + quad.TopLeft, quad.TopRight, color, in subRegion, normal: normal); } public override void DrawPrimitives(DrawPrimitiveTopology primitiveTopology, Texture texture, diff --git a/Robust.Client/Graphics/Drawing/DrawingHandleWorld.cs b/Robust.Client/Graphics/Drawing/DrawingHandleWorld.cs index 9fba50a0506..8e5f0c67acd 100644 --- a/Robust.Client/Graphics/Drawing/DrawingHandleWorld.cs +++ b/Robust.Client/Graphics/Drawing/DrawingHandleWorld.cs @@ -43,7 +43,7 @@ protected DrawingHandleWorld(Texture white) : base(white) /// A color to multiply the texture by when shading. /// The four corners of the texture sub region in px. public abstract void DrawTextureRectRegion(Texture texture, Box2 quad, - Color? modulate = null, UIBox2? subRegion = null); + Color? modulate = null, UIBox2? subRegion = null, bool normal = false); /// /// Draws a sprite to the world. The coordinate system is right handed. @@ -56,7 +56,7 @@ public abstract void DrawTextureRectRegion(Texture texture, Box2 quad, /// A color to multiply the texture by when shading. /// The four corners of the texture sub region in px. public abstract void DrawTextureRectRegion(Texture texture, in Box2Rotated quad, - Color? modulate = null, UIBox2? subRegion = null); + Color? modulate = null, UIBox2? subRegion = null, bool normal = false); private Box2 GetQuad(Texture texture, Vector2 position) { diff --git a/Robust.Client/ResourceManagement/ResourceTypes/RSIResource.cs b/Robust.Client/ResourceManagement/ResourceTypes/RSIResource.cs index f7b9bda7324..96e45e05f3b 100644 --- a/Robust.Client/ResourceManagement/ResourceTypes/RSIResource.cs +++ b/Robust.Client/ResourceManagement/ResourceTypes/RSIResource.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using System.IO.IsolatedStorage; using System.Linq; using Robust.Client.Graphics; using Robust.Client.Utility; @@ -7,6 +9,7 @@ using Robust.Shared.Graphics; using Robust.Shared.Graphics.RSI; using Robust.Shared.IoC; +using Robust.Shared.Log; using Robust.Shared.Maths; using Robust.Shared.Resources; using Robust.Shared.Utility; @@ -92,14 +95,39 @@ internal static void LoadPreTexture(IResourceManager manager, LoadStepData data) var stateObject = metadata.States[index]; // Load image from disk. var texPath = data.Path / (stateObject.StateId + ".png"); + var normalPath = data.Path / (stateObject.NormalId + ".png"); using (var stream = manager.ContentFileRead(texPath)) { - reg.Src = Image.Load(stream); - } + var texture = Image.Load(stream); + Image normalImage; + if (stateObject.NormalId is not null) + { + using (var normalStream = manager.ContentFileRead(normalPath)) + { + normalImage = Image.Load(normalStream); + } + } + else + { + normalImage = new Image(texture.Width, texture.Height); + } + for (int nX = 0; nX < texture.Width; nX++) + { + for (int nY = 0; nY < texture.Height; nY++) + { + var T = normalImage[nX, nY]; + if (stateObject.NormalId is not { }) + T = new Rgba32(0.5f, 0.5f, 1f, 0f); + T.A = texture[nX, nY].A; + normalImage[nX, nY] = T; + } + } - if (reg.Src.Width % frameSize.X != 0 || reg.Src.Height % frameSize.Y != 0) + reg.Src = (texture, normalImage); + } + if (reg.Src.Item1.Width % frameSize.X != 0 || reg.Src.Item1.Height % frameSize.Y != 0) { - var regDims = $"{reg.Src.Width}x{reg.Src.Height}"; + var regDims = $"{reg.Src.Item1.Width}x{reg.Src.Item1.Height}"; var iconDims = $"{frameSize.X}x{frameSize.Y}"; throw new RSILoadException($"State '{stateObject.StateId}' image size ({regDims}) is not a multiple of the icon size ({iconDims})."); } @@ -144,7 +172,8 @@ internal static void LoadPreTexture(IResourceManager manager, LoadStepData data) var dimensionX = (int) MathF.Ceiling(MathF.Sqrt(totalFrameCount)); var dimensionY = (int) MathF.Ceiling((float) totalFrameCount / dimensionX); - var sheet = new Image(dimensionX * frameSize.X, dimensionY * frameSize.Y); + var sheetHalfWidth = dimensionX * frameSize.X; + var sheet = new Image(sheetHalfWidth * 1, dimensionY * frameSize.Y); var sheetIndex = 0; for (var index = 0; index < toAtlas.Length; index++) @@ -153,7 +182,7 @@ internal static void LoadPreTexture(IResourceManager manager, LoadStepData data) // Blit all the frames over. for (var i = 0; i < reg.TotalFrameCount; i++) { - var srcWidth = (reg.Src.Width / frameSize.X); + var srcWidth = (reg.Src.Item1.Width / frameSize.X); var srcColumn = i % srcWidth; var srcRow = i / srcWidth; var srcPos = (srcColumn * frameSize.X, srcRow * frameSize.Y); @@ -161,10 +190,12 @@ internal static void LoadPreTexture(IResourceManager manager, LoadStepData data) var sheetColumn = (sheetIndex + i) % dimensionX; var sheetRow = (sheetIndex + i) / dimensionX; var sheetPos = (sheetColumn * frameSize.X, sheetRow * frameSize.Y); + var sheetNPos = (sheetColumn * frameSize.X + sheetHalfWidth, sheetRow * frameSize.Y); var srcBox = UIBox2i.FromDimensions(srcPos, frameSize); - reg.Src.Blit(srcBox, sheet, sheetPos); + reg.Src.Item1.Blit(srcBox, sheet, sheetPos); + // reg.Src.Item2.Blit(srcBox, sheet, sheetNPos); } sheetIndex += reg.TotalFrameCount; @@ -173,7 +204,8 @@ internal static void LoadPreTexture(IResourceManager manager, LoadStepData data) for (var i = 0; i < toAtlas.Length; i++) { ref var reg = ref toAtlas[i]; - reg.Src.Dispose(); + reg.Src.Item1.Dispose(); + reg.Src.Item2.Dispose(); } data.Rsi = rsi; @@ -392,7 +424,7 @@ internal sealed class LoadStepData internal struct StateReg { - public Image Src; + public (Image, Image) Src; public Texture[][] Output; public int[][] Indices; public Vector2i[][] Offsets; diff --git a/Robust.Shared/Resources/RsiLoading.cs b/Robust.Shared/Resources/RsiLoading.cs index 3854f80ce14..7a3c9a59350 100644 --- a/Robust.Shared/Resources/RsiLoading.cs +++ b/Robust.Shared/Resources/RsiLoading.cs @@ -41,6 +41,7 @@ internal static RsiMetadata LoadRsiMetadata(Stream manifestFile) { var stateObject = manifestJson.States[stateI]; var stateName = stateObject.Name; + var normalName = stateObject.Normal; int dirValue; if (stateObject.Directions is { } dirVal) @@ -91,7 +92,7 @@ internal static RsiMetadata LoadRsiMetadata(Stream manifestFile) } } - states[stateI] = new StateMetadata(stateName, dirValue, delays); + states[stateI] = new StateMetadata(stateName, normalName, dirValue, delays); } var textureParams = TextureLoadParameters.Default; @@ -125,12 +126,14 @@ internal sealed class RsiMetadata(Vector2i size, StateMetadata[] states, Texture internal sealed class StateMetadata { public readonly string StateId; + public readonly string? NormalId; public readonly int DirCount; public readonly float[][] Delays; - public StateMetadata(string stateId, int dirCount, float[][] delays) + public StateMetadata(string stateId, string? normalId, int dirCount, float[][] delays) { StateId = stateId; + NormalId = normalId; DirCount = dirCount; Delays = delays; @@ -148,7 +151,7 @@ private sealed record RsiJsonMetadata( bool MetaAtlas = true); [UsedImplicitly] - private sealed record StateJsonMetadata(string Name, int? Directions, float[][]? Delays); + private sealed record StateJsonMetadata(string Name, string? Normal, int? Directions, float[][]? Delays); [UsedImplicitly] private sealed record RsiJsonLoad(bool Srgb = true);