Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add planet lighting #32522

Open
wants to merge 31 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
4c4be92
Implements a Dynamic Lighting System on maps.
DoutorWhite Dec 18, 2023
c463d3e
Edit: the night should be a little bit brighter and blue now.
DoutorWhite Dec 18, 2023
ecd336f
Major edit: everything must be done on the client side now, with cert…
DoutorWhite Dec 19, 2023
5928c29
Edit: The offset is now serverside, this makes the time accurate in a…
DoutorWhite Dec 19, 2023
6fd591f
Removing ununsed import
DoutorWhite Dec 19, 2023
6f8c758
Minor tweaks
DoutorWhite Dec 19, 2023
3634f9a
Tweak in time precision
DoutorWhite Dec 19, 2023
34a658b
Minor tweak + Unused import removed
DoutorWhite Dec 19, 2023
f4a7576
Edit: apparently RealTime is better for what I'm looking for
DoutorWhite Dec 19, 2023
e141aad
Fix: Now the time is calculated correctly.
DoutorWhite Dec 19, 2023
89177f3
Minor tweaks
DoutorWhite Dec 19, 2023
a5ac0b3
Adds condition for when the light should be updated
DoutorWhite Dec 19, 2023
17bb922
Add planet lighting
metalgearsloth Sep 29, 2024
317927a
she
metalgearsloth Sep 30, 2024
7ceb3ef
close-ish
metalgearsloth Sep 30, 2024
1068577
c
metalgearsloth Sep 30, 2024
2dfc600
bittersweat
metalgearsloth Oct 2, 2024
19649c0
Fixes
metalgearsloth Oct 4, 2024
9f2785b
Merge branch '22719' into 2024-09-29-planet-lighting
metalgearsloth Oct 4, 2024
2ccce62
Revert "Merge branch '22719' into 2024-09-29-planet-lighting"
metalgearsloth Oct 4, 2024
b4065d7
Europa and day-night
metalgearsloth Oct 5, 2024
216534e
weh
metalgearsloth Oct 6, 2024
914cff1
rooves working
metalgearsloth Oct 6, 2024
ab111c5
Clean
metalgearsloth Oct 11, 2024
b7a9f64
Remove Europa
metalgearsloth Oct 13, 2024
0db161b
Fixes
metalgearsloth Oct 13, 2024
645e851
Merge remote-tracking branch 'upstream/master' into 2024-09-29-planet…
metalgearsloth Oct 13, 2024
65fd1b1
Merge remote-tracking branch 'upstream/master' into 2024-09-29-planet…
metalgearsloth Oct 14, 2024
dcada7b
fix
metalgearsloth Oct 14, 2024
749ab32
Update
metalgearsloth Oct 15, 2024
24e572e
Fix caves
metalgearsloth Oct 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions Content.Client/Light/EntitySystems/PlanetLightSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Robust.Client.Graphics;

namespace Content.Client.Light.EntitySystems;

public sealed class PlanetLightSystem : EntitySystem
{
[Dependency] private readonly IOverlayManager _overlayMan = default!;

private RoofOverlay _overlay = default!;

public override void Initialize()
{
base.Initialize();
_overlay = new(EntityManager);
_overlayMan.AddOverlay(_overlay);
}

public override void Shutdown()
{
base.Shutdown();
_overlayMan.RemoveOverlay(_overlay);
}
}
23 changes: 23 additions & 0 deletions Content.Client/Light/EntitySystems/TileEmissionSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Robust.Client.Graphics;

namespace Content.Client.Light.EntitySystems;

public sealed class TileEmissionSystem : EntitySystem
{
[Dependency] private readonly IOverlayManager _overlayMan = default!;

private TileEmissionOverlay _overlay = default!;

public override void Initialize()
{
base.Initialize();
_overlay = new(EntityManager);
_overlayMan.AddOverlay(_overlay);
}

public override void Shutdown()
{
base.Shutdown();
_overlayMan.RemoveOverlay(_overlay);
}
}
32 changes: 32 additions & 0 deletions Content.Client/Light/LightCycleSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Content.Client.GameTicking.Managers;
using Content.Shared;
using Content.Shared.Light.Components;
using Robust.Shared.Map.Components;
using Robust.Shared.Timing;

namespace Content.Client.Light;

public sealed class LightCycleSystem : SharedLightCycleSystem
{
[Dependency] private readonly ClientGameTicker _ticker = default!;
[Dependency] private readonly IGameTiming _timing = default!;

public override void Update(float frameTime)
{
base.Update(frameTime);
var mapQuery = AllEntityQuery<LightCycleComponent, MapLightComponent>();
while (mapQuery.MoveNext(out var uid, out var cycle, out var map))
{
if (!cycle.Running)
continue;

var time = (float) _timing.CurTime
.Add(cycle.Offset)
.Subtract(_ticker.RoundStartTimeSpan)
.TotalSeconds;

var color = GetColor((uid, cycle), cycle.OriginalColor, time);
map.AmbientLightColor = color;
}
}
}
121 changes: 121 additions & 0 deletions Content.Client/Light/RoofOverlay.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
using System.Numerics;
using Content.Shared.Light.Components;
using Content.Shared.Maps;
using Robust.Client.Graphics;
using Robust.Shared.Enums;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes;

namespace Content.Client.Light;

public sealed class RoofOverlay : Overlay
{
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly IPrototypeManager _protoManager = default!;
private readonly IEntityManager _entManager;

private readonly EntityLookupSystem _lookup;
private readonly SharedMapSystem _mapSystem;
private readonly SharedTransformSystem _xformSystem;

private readonly HashSet<Entity<OccluderComponent>> _occluders = new();

public override OverlaySpace Space => OverlaySpace.BeforeLighting;

private IRenderTexture? _target;

public RoofOverlay(IEntityManager entManager)
{
_entManager = entManager;
IoCManager.InjectDependencies(this);

_lookup = _entManager.System<EntityLookupSystem>();
_mapSystem = _entManager.System<SharedMapSystem>();
_xformSystem = _entManager.System<SharedTransformSystem>();

ZIndex = -1;
}

protected override void Draw(in OverlayDrawArgs args)
{
if (args.Viewport.Eye == null)
return;

var mapEnt = _mapSystem.GetMap(args.MapId);

if (!_entManager.TryGetComponent(mapEnt, out RoofComponent? roofComp) ||
!_entManager.TryGetComponent(mapEnt, out MapGridComponent? grid))
{
return;
}

var viewport = args.Viewport;
var eye = args.Viewport.Eye;

var worldHandle = args.WorldHandle;
var bounds = args.WorldBounds;

if (_target?.Size != viewport.LightRenderTarget.Size)
{
_target = _clyde
.CreateRenderTarget(viewport.LightRenderTarget.Size,
new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "roof-target");
}

worldHandle.RenderInRenderTarget(_target,
() =>
{
var invMatrix = _target.GetWorldToLocalMatrix(eye, viewport.RenderScale / 2f);

var gridMatrix = _xformSystem.GetWorldMatrix(mapEnt);
var matty = Matrix3x2.Multiply(gridMatrix, invMatrix);

worldHandle.SetTransform(matty);

var tileEnumerator = _mapSystem.GetTilesEnumerator(mapEnt, grid, bounds);

// Due to stencilling we essentially draw on unrooved tiles
while (tileEnumerator.MoveNext(out var tileRef))
{
if ((tileRef.Tile.ContentFlag & (ushort) TileFlag.Roof) == 0x0)
{
// Check if the tile is occluded in which case hide it anyway.
// This is to avoid lit walls bleeding over to unlit tiles.
_occluders.Clear();
_lookup.GetLocalEntitiesIntersecting(mapEnt, tileRef.GridIndices, _occluders);
var found = false;

foreach (var occluder in _occluders)
{
if (!occluder.Comp.Enabled)
continue;

found = true;
break;
}

if (!found)
continue;
}

var local = _lookup.GetLocalBounds(tileRef, grid.TileSize);
worldHandle.DrawRect(local, roofComp.Color);
}

}, Color.Transparent);

args.WorldHandle.RenderInRenderTarget(viewport.LightRenderTarget,
() =>
{
var invMatrix =
viewport.LightRenderTarget.GetWorldToLocalMatrix(viewport.Eye, viewport.RenderScale / 2f);
worldHandle.SetTransform(invMatrix);

var maskShader = _protoManager.Index<ShaderPrototype>("Mix").Instance();
worldHandle.UseShader(maskShader);

worldHandle.DrawTextureRect(_target.Texture, bounds);
}, null);
}
}
110 changes: 110 additions & 0 deletions Content.Client/Light/TileEmissionOverlay.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using System.Numerics;
using Content.Shared.Light.Components;
using Robust.Client.Graphics;
using Robust.Shared.Enums;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Prototypes;

namespace Content.Client.Light;

public sealed class TileEmissionOverlay : Overlay
{
public override OverlaySpace Space => OverlaySpace.BeforeLighting;

[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IPrototypeManager _protoManager = default!;

private SharedMapSystem _mapSystem;
private SharedTransformSystem _xformSystem;

private readonly EntityLookupSystem _lookup;

private readonly EntityQuery<TransformComponent> _xformQuery;
private readonly HashSet<Entity<TileEmissionComponent>> _entities = new();

private List<Entity<MapGridComponent>> _grids = new();

private IRenderTexture? _target;

public TileEmissionOverlay(IEntityManager entManager)
{
IoCManager.InjectDependencies(this);

_lookup = entManager.System<EntityLookupSystem>();
_mapSystem = entManager.System<SharedMapSystem>();
_xformSystem = entManager.System<SharedTransformSystem>();

_xformQuery = entManager.GetEntityQuery<TransformComponent>();
}

protected override void Draw(in OverlayDrawArgs args)
{
if (args.Viewport.Eye == null)
return;

var mapId = args.MapId;
var worldHandle = args.WorldHandle;
var bounds = args.WorldBounds;
var expandedBounds = bounds.Enlarged(1.5f);
var viewport = args.Viewport;

if (_target?.Size != viewport.LightRenderTarget.Size)
{
_target = _clyde
.CreateRenderTarget(viewport.LightRenderTarget.Size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "tile-emissions");
}

args.WorldHandle.RenderInRenderTarget(_target,
() =>
{
var invMatrix = _target.GetWorldToLocalMatrix(viewport.Eye, viewport.RenderScale / 2f);
_grids.Clear();
_mapManager.FindGridsIntersecting(mapId, expandedBounds, ref _grids, approx: true);

foreach (var grid in _grids)
{
var gridInvMatrix = _xformSystem.GetInvWorldMatrix(grid);
var localBounds = gridInvMatrix.TransformBox(expandedBounds);
_entities.Clear();
_lookup.GetLocalEntitiesIntersecting(grid.Owner, localBounds, _entities);

if (_entities.Count == 0)
continue;

var gridMatrix = _xformSystem.GetWorldMatrix(grid.Owner);

foreach (var ent in _entities)
{
var xform = _xformQuery.Comp(ent);

var tile = _mapSystem.LocalToTile(grid.Owner, grid, xform.Coordinates);
var matty = Matrix3x2.Multiply(gridMatrix, invMatrix);

worldHandle.SetTransform(matty);

// Yes I am fully aware this leads to overlap. If you really want to have alpha then you'll need
// to turn the squares into polys.
// Additionally no shadows so if you make it too big it's going to go through a 1x wall.
var local = _lookup.GetLocalBounds(tile, grid.Comp.TileSize).Enlarged(ent.Comp.Range);
worldHandle.DrawRect(local, ent.Comp.Color);
}
}
}, Color.Transparent);

args.WorldHandle.RenderInRenderTarget(viewport.LightRenderTarget,
() =>
{
var invMatrix =
viewport.LightRenderTarget.GetWorldToLocalMatrix(viewport.Eye, viewport.RenderScale / 2f);
worldHandle.SetTransform(invMatrix);

var maskShader = _protoManager.Index<ShaderPrototype>("Mix").Instance();
worldHandle.UseShader(maskShader);

worldHandle.DrawTextureRect(_target.Texture, bounds);
}, null);
}
}
8 changes: 8 additions & 0 deletions Content.Server/Light/Components/SetRoofComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Content.Server.Light.Components;

[RegisterComponent]
public sealed partial class SetRoofComponent : Component
{
[DataField(required: true)]
public bool Value;
}
21 changes: 21 additions & 0 deletions Content.Server/Light/EntitySystems/LightCycleSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Content.Shared;
using Content.Shared.Light.Components;
using Robust.Shared.Random;

namespace Content.Server.Light.EntitySystems;

public sealed class LightCycleSystem : SharedLightCycleSystem
{
[Dependency] private readonly IRobustRandom _random = default!;

protected override void OnCycleMapInit(Entity<LightCycleComponent> ent, ref MapInitEvent args)
{
base.OnCycleMapInit(ent, ref args);

if (ent.Comp.InitialOffset)
{
ent.Comp.Offset = _random.Next(ent.Comp.Duration);
Dirty(ent);
}
}
}
32 changes: 32 additions & 0 deletions Content.Server/Light/EntitySystems/RoofSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Content.Server.Light.Components;
using Content.Shared.Light.EntitySystems;
using Robust.Shared.Map.Components;

namespace Content.Server.Light.EntitySystems;

public sealed class RoofSystem : SharedRoofSystem
{
[Dependency] private readonly SharedMapSystem _maps = default!;

private EntityQuery<MapGridComponent> _gridQuery;

public override void Initialize()
{
base.Initialize();
_gridQuery = GetEntityQuery<MapGridComponent>();
SubscribeLocalEvent<SetRoofComponent, ComponentStartup>(OnFlagStartup);
}

private void OnFlagStartup(Entity<SetRoofComponent> ent, ref ComponentStartup args)
{
var xform = Transform(ent.Owner);

if (_gridQuery.TryComp(xform.GridUid, out var grid))
{
var index = _maps.LocalToTile(xform.GridUid.Value, grid, xform.Coordinates);
SetRoof((xform.GridUid.Value, grid, null), index, ent.Comp.Value);
}

QueueDel(ent.Owner);
}
}
Loading
Loading