Map renderer now supports large sprites and rotations. (#6938)

This commit is contained in:
Moony
2022-03-01 18:23:28 -06:00
committed by GitHub
parent c4c238bda9
commit b73621587a
3 changed files with 34 additions and 49 deletions

View File

@@ -1,12 +1,16 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Content.Shared.SubFloor; using Content.Shared.SubFloor;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement; using Robust.Client.ResourceManagement;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Log;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using SixLabors.ImageSharp; using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using TerraFX.Interop.Windows;
using static Robust.Client.Graphics.RSI.State; using static Robust.Client.Graphics.RSI.State;
using static Robust.UnitTesting.RobustIntegrationTest; using static Robust.UnitTesting.RobustIntegrationTest;
@@ -49,11 +53,6 @@ public sealed class EntityPainter
public void Run(Image canvas, EntityData entity) public void Run(Image canvas, EntityData entity)
{ {
if (_sEntityManager.HasComponent<SubFloorHideComponent>(entity.Sprite.Owner))
{
return;
}
if (!entity.Sprite.Visible || entity.Sprite.ContainerOccluded) if (!entity.Sprite.Visible || entity.Sprite.ContainerOccluded)
{ {
return; return;
@@ -94,55 +93,44 @@ public sealed class EntityPainter
image = image.CloneAs<Rgba32>(); image = image.CloneAs<Rgba32>();
var directions = entity.Sprite.GetLayerDirectionCount(layer); (int, int, int, int) GetRsiFrame(RSI? rsi, Image image, EntityData entity, ISpriteLayer layer, int direction)
// TODO add support for 8 directions and animations (delays)
if (directions != 1 && directions != 8)
{ {
double xStart, xEnd, yStart, yEnd; if (rsi is null)
return (0, 0, EyeManager.PixelsPerMeter, EyeManager.PixelsPerMeter);
switch (directions) var statesX = image.Width / rsi.Size.X;
{ var statesY = image.Height / rsi.Size.Y;
case 4: var stateCount = statesX * statesY;
{ var frames = stateCount / entity.Sprite.GetLayerDirectionCount(layer);
var dir = layer.EffectiveDirection(worldRotation); var target = direction * frames;
var targetY = target / statesX;
(xStart, xEnd, yStart, yEnd) = dir switch var targetX = target % statesY;
{ return (targetX * rsi.Size.X, targetY * rsi.Size.Y, rsi.Size.X, rsi.Size.Y);
// Only need the first tuple as doubles for the compiler to recognize it
Direction.South => (0d, 0.5d, 0d, 0.5d),
Direction.East => (0, 0.5, 0.5, 1),
Direction.North => (0.5, 1, 0, 0.5),
Direction.West => (0.5, 1, 0.5, 1),
_ => throw new ArgumentOutOfRangeException(nameof(dir))
};
break;
}
default:
throw new ArgumentOutOfRangeException();
}
var x = (int) (image.Width * xStart);
var width = (int) (image.Width * xEnd) - x;
var y = (int) (image.Height * yStart);
var height = (int) (image.Height * yEnd) - y;
image.Mutate(o => o.Crop(new Rectangle(x, y, width, height)));
} }
var dir = entity.Sprite.GetLayerDirectionCount(layer) switch
{
0 => 0,
_ => (int)layer.EffectiveDirection(worldRotation)
};
var (x, y, width, height) = GetRsiFrame(rsi, image, entity, layer, dir);
image.Mutate(o => o.Crop(new Rectangle(x, y, width, height)));
var colorMix = entity.Sprite.Color * layer.Color; var colorMix = entity.Sprite.Color * layer.Color;
var imageColor = Color.FromRgba(colorMix.RByte, colorMix.GByte, colorMix.BByte, colorMix.AByte); var imageColor = Color.FromRgba(colorMix.RByte, colorMix.GByte, colorMix.BByte, colorMix.AByte);
var coloredImage = new Image<Rgba32>(image.Width, image.Height); var coloredImage = new Image<Rgba32>(image.Width, image.Height);
coloredImage.Mutate(o => o.BackgroundColor(imageColor)); coloredImage.Mutate(o => o.BackgroundColor(imageColor));
var (imgX, imgY) = rsi?.Size ?? (EyeManager.PixelsPerMeter, EyeManager.PixelsPerMeter);
image.Mutate(o => o image.Mutate(o => o
.DrawImage(coloredImage, PixelColorBlendingMode.Multiply, PixelAlphaCompositionMode.SrcAtop, 1) .DrawImage(coloredImage, PixelColorBlendingMode.Multiply, PixelAlphaCompositionMode.SrcAtop, 1)
.Resize(32, 32) .Resize(imgX, imgY)
.Flip(FlipMode.Vertical)); .Flip(FlipMode.Vertical));
var pointX = (int) entity.X; var pointX = (int) entity.X - (imgX / 2) + EyeManager.PixelsPerMeter / 2;
var pointY = (int) entity.Y; var pointY = (int) entity.Y - (imgY / 2) + EyeManager.PixelsPerMeter / 2;
canvas.Mutate(o => o.DrawImage(image, new Point(pointX, pointY), 1)); canvas.Mutate(o => o.DrawImage(image, new Point(pointX, pointY), 1));
} }
} }

View File

@@ -55,14 +55,11 @@ namespace Content.MapRenderer.Painters
return; return;
} }
if (!_decals.TryGetValue(grid.Index, out var decals)) // Decals are always painted before entities, and are also optional.
{ if (_decals.TryGetValue(grid.Index, out var decals))
Console.WriteLine($"No decals found on grid {grid.Index}"); _decalPainter.Run(gridCanvas, decals);
return;
}
// Decals are always painted before entities.
_decalPainter.Run(gridCanvas, decals);
_entityPainter.Run(gridCanvas, entities); _entityPainter.Run(gridCanvas, entities);
Console.WriteLine($"{nameof(GridPainter)} painted grid {grid.Index} in {(int) stopwatch.Elapsed.TotalMilliseconds} ms"); Console.WriteLine($"{nameof(GridPainter)} painted grid {grid.Index} in {(int) stopwatch.Elapsed.TotalMilliseconds} ms");
} }

View File

@@ -18,7 +18,7 @@ namespace Content.MapRenderer
private static readonly MapPainter MapPainter = new(); private static readonly MapPainter MapPainter = new();
#pragma warning disable CA1825 #pragma warning disable CA1825
private static readonly string[] ForceRender = {"saltern"}; private static readonly string[] ForceRender = {"packedstation"};
#pragma warning restore CA1825 #pragma warning restore CA1825
internal static async Task Main() internal static async Task Main()