-
Notifications
You must be signed in to change notification settings - Fork 149
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #344 from Rxup/upstream-sync
Upstream sync
- Loading branch information
Showing
89 changed files
with
763 additions
and
268 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
using System.Linq; | ||
using System.Numerics; | ||
using Content.Client.Parallax; | ||
using Content.Shared.Weather; | ||
using Robust.Client.GameObjects; | ||
using Robust.Client.Graphics; | ||
using Robust.Client.ResourceManagement; | ||
using Robust.Client.Utility; | ||
using Robust.Shared.Enums; | ||
using Robust.Shared.Graphics.RSI; | ||
using Robust.Shared.Map; | ||
using Robust.Shared.Map.Components; | ||
using Robust.Shared.Physics.Components; | ||
using Robust.Shared.Prototypes; | ||
using Robust.Shared.Timing; | ||
using Robust.Shared.Utility; | ||
|
||
namespace Content.Client.Weather; | ||
|
||
public sealed class WeatherOverlay : Overlay | ||
{ | ||
[Dependency] private readonly IClyde _clyde = default!; | ||
[Dependency] private readonly IEntityManager _entManager = default!; | ||
[Dependency] private readonly IGameTiming _timing = default!; | ||
[Dependency] private readonly IMapManager _mapManager = default!; | ||
[Dependency] private readonly IPrototypeManager _protoManager = default!; | ||
[Dependency] private readonly IResourceCache _cache = default!; | ||
private readonly SharedTransformSystem _transform; | ||
private readonly SpriteSystem _sprite; | ||
private readonly WeatherSystem _weather; | ||
|
||
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV; | ||
|
||
private IRenderTexture? _blep; | ||
|
||
public WeatherOverlay(SharedTransformSystem transform, SpriteSystem sprite, WeatherSystem weather) | ||
{ | ||
ZIndex = ParallaxSystem.ParallaxZIndex + 1; | ||
_transform = transform; | ||
_weather = weather; | ||
_sprite = sprite; | ||
IoCManager.InjectDependencies(this); | ||
} | ||
|
||
protected override bool BeforeDraw(in OverlayDrawArgs args) | ||
{ | ||
if (args.MapId == MapId.Nullspace) | ||
return false; | ||
|
||
if (!_entManager.TryGetComponent<WeatherComponent>(_mapManager.GetMapEntityId(args.MapId), out var weather) || | ||
weather.Weather.Count == 0) | ||
{ | ||
return false; | ||
} | ||
|
||
return base.BeforeDraw(in args); | ||
} | ||
|
||
protected override void Draw(in OverlayDrawArgs args) | ||
{ | ||
var mapUid = _mapManager.GetMapEntityId(args.MapId); | ||
|
||
if (!_entManager.TryGetComponent<WeatherComponent>(mapUid, out var comp)) | ||
{ | ||
return; | ||
} | ||
|
||
foreach (var (proto, weather) in comp.Weather) | ||
{ | ||
if (!_protoManager.TryIndex<WeatherPrototype>(proto, out var weatherProto)) | ||
continue; | ||
|
||
var alpha = _weather.GetPercent(weather, mapUid); | ||
DrawWorld(args, weatherProto, alpha); | ||
} | ||
} | ||
|
||
private void DrawWorld(in OverlayDrawArgs args, WeatherPrototype weatherProto, float alpha) | ||
{ | ||
var worldHandle = args.WorldHandle; | ||
var mapId = args.MapId; | ||
var worldAABB = args.WorldAABB; | ||
var worldBounds = args.WorldBounds; | ||
var invMatrix = args.Viewport.GetWorldToLocalMatrix(); | ||
var position = args.Viewport.Eye?.Position.Position ?? Vector2.Zero; | ||
|
||
if (_blep?.Texture.Size != args.Viewport.Size) | ||
{ | ||
_blep?.Dispose(); | ||
_blep = _clyde.CreateRenderTarget(args.Viewport.Size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "weather-stencil"); | ||
} | ||
|
||
// Cut out the irrelevant bits via stencil | ||
// This is why we don't just use parallax; we might want specific tiles to get drawn over | ||
// particularly for planet maps or stations. | ||
worldHandle.RenderInRenderTarget(_blep, () => | ||
{ | ||
var bodyQuery = _entManager.GetEntityQuery<PhysicsComponent>(); | ||
var xformQuery = _entManager.GetEntityQuery<TransformComponent>(); | ||
var weatherIgnoreQuery = _entManager.GetEntityQuery<IgnoreWeatherComponent>(); | ||
|
||
// idk if this is safe to cache in a field and clear sloth help | ||
var grids = new List<Entity<MapGridComponent>>(); | ||
_mapManager.FindGridsIntersecting(mapId, worldAABB, ref grids); | ||
|
||
foreach (var grid in grids) | ||
{ | ||
var matrix = _transform.GetWorldMatrix(grid, xformQuery); | ||
Matrix3.Multiply(in matrix, in invMatrix, out var matty); | ||
worldHandle.SetTransform(matty); | ||
|
||
foreach (var tile in grid.Comp.GetTilesIntersecting(worldAABB)) | ||
{ | ||
// Ignored tiles for stencil | ||
if (_weather.CanWeatherAffect(grid, tile, weatherIgnoreQuery, bodyQuery)) | ||
{ | ||
continue; | ||
} | ||
|
||
var gridTile = new Box2(tile.GridIndices * grid.Comp.TileSize, | ||
(tile.GridIndices + Vector2i.One) * grid.Comp.TileSize); | ||
|
||
worldHandle.DrawRect(gridTile, Color.White); | ||
} | ||
} | ||
|
||
}, Color.Transparent); | ||
|
||
worldHandle.SetTransform(Matrix3.Identity); | ||
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilMask").Instance()); | ||
worldHandle.DrawTextureRect(_blep.Texture, worldBounds); | ||
Texture? sprite = null; | ||
var curTime = _timing.RealTime; | ||
|
||
switch (weatherProto.Sprite) | ||
{ | ||
case SpriteSpecifier.Rsi rsi: | ||
var rsiActual = _cache.GetResource<RSIResource>(rsi.RsiPath).RSI; | ||
rsiActual.TryGetState(rsi.RsiState, out var state); | ||
var frames = state!.GetFrames(RsiDirection.South); | ||
var delays = state.GetDelays(); | ||
var totalDelay = delays.Sum(); | ||
var time = curTime.TotalSeconds % totalDelay; | ||
var delaySum = 0f; | ||
|
||
for (var i = 0; i < delays.Length; i++) | ||
{ | ||
var delay = delays[i]; | ||
delaySum += delay; | ||
|
||
if (time > delaySum) | ||
continue; | ||
|
||
sprite = frames[i]; | ||
break; | ||
} | ||
|
||
sprite ??= _sprite.Frame0(weatherProto.Sprite); | ||
break; | ||
case SpriteSpecifier.Texture texture: | ||
sprite = texture.GetTexture(_cache); | ||
break; | ||
default: | ||
throw new NotImplementedException(); | ||
} | ||
|
||
// Draw the rain | ||
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilDraw").Instance()); | ||
|
||
// TODO: This is very similar to parallax but we need stencil support but we can probably combine these somehow | ||
// and not make it spaghetti, while getting the advantages of not-duped code? | ||
|
||
|
||
// Okay I have spent like 5 hours on this at this point and afaict you have one of the following comprises: | ||
// - No scrolling so the weather is always centered on the player | ||
// - Crappy looking rotation but strafing looks okay and scrolls | ||
// - Crappy looking strafing but rotation looks okay. | ||
// - No rotation | ||
// - Storing state across frames to do scrolling and just having it always do topdown. | ||
|
||
// I have chosen no rotation. | ||
|
||
const float scale = 1f; | ||
const float slowness = 0f; | ||
var scrolling = Vector2.Zero; | ||
|
||
// Size of the texture in world units. | ||
var size = (sprite.Size / (float) EyeManager.PixelsPerMeter) * scale; | ||
var scrolled = scrolling * (float) curTime.TotalSeconds; | ||
|
||
// Origin - start with the parallax shift itself. | ||
var originBL = position * slowness + scrolled; | ||
|
||
// Centre the image. | ||
originBL -= size / 2; | ||
|
||
// Remove offset so we can floor. | ||
var flooredBL = args.WorldAABB.BottomLeft - originBL; | ||
|
||
// Floor to background size. | ||
flooredBL = (flooredBL / size).Floored() * size; | ||
|
||
// Re-offset. | ||
flooredBL += originBL; | ||
|
||
for (var x = flooredBL.X; x < args.WorldAABB.Right; x += size.X) | ||
{ | ||
for (var y = flooredBL.Y; y < args.WorldAABB.Top; y += size.Y) | ||
{ | ||
var box = Box2.FromDimensions(new Vector2(x, y), size); | ||
worldHandle.DrawTextureRect(sprite, box, (weatherProto.Color ?? Color.White).WithAlpha(alpha)); | ||
} | ||
} | ||
|
||
worldHandle.SetTransform(Matrix3.Identity); | ||
worldHandle.UseShader(null); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.