Compare commits

..

8 Commits

Author SHA1 Message Date
PJB3005
ee330d0ae9 Make DOOM work
I think I lost this work originally
2025-01-15 23:08:44 +01:00
metalgearsloth
6c44dd9665 RenderingTreeSystem cleanup (#1759)
* RenderingTreeSystem cleanup

10% less bad but 100% still boilerplate

* Slight changies

* Even better

* New shit just got made

* Apply revews
2021-05-20 18:57:29 +10:00
Acruid
c81413b0b4 Fixes bug where the net_entityreport red PVS range square was drawn at half the actual range. 2021-05-19 15:52:44 -07:00
Vera Aguilera Puerto
88b3a557da Fixes bug with PrototypeIdListSerializer where lists wouldn't be copied at all. 2021-05-19 12:12:13 +02:00
Vera Aguilera Puerto
572eb01290 Opens SCSI window centered
- Kinda fixes it getting NaN'd on resize... This isn't a proper fix, however.
2021-05-19 11:11:35 +02:00
Vera Aguilera Puerto
9dab74c9d5 Fixes SS14Window going off-screen. 2021-05-19 10:55:41 +02:00
Paul
e1cb1e1b9c fixes xamlui nuking itself when one (1) (singular) partial declaration is missing 2021-05-18 20:19:50 +02:00
Vera Aguilera Puerto
a23da702b1 MidiManager now has a Volume property which changes the general midi volume.
- Adds VV attributes to MidiManager and MidiRenderer.
2021-05-18 20:03:20 +02:00
20 changed files with 480 additions and 254 deletions

View File

@@ -247,7 +247,6 @@ namespace {nameSpace}
DiagnosticSeverity.Error,
true),
Location.None));
return null;
}
}

View File

@@ -11,9 +11,11 @@ using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Broadphase;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
using Logger = Robust.Shared.Log.Logger;
namespace Robust.Client.Audio.Midi
@@ -32,24 +34,6 @@ namespace Robust.Client.Audio.Midi
/// </returns>
IMidiRenderer? GetNewRenderer();
/*
/// <summary>
/// Checks whether the file at the given path is a valid midi file or not.
/// </summary>
/// <remarks>
/// We add this here so content doesn't need to reference NFluidsynth.
/// </remarks>
bool IsMidiFile(string filename);
/// <summary>
/// Checks whether the file at the given path is a valid midi file or not.
/// </summary>
/// <remarks>
/// We add this here so content doesn't need to reference NFluidsynth.
/// </remarks>
bool IsSoundfontFile(string filename);
*/
/// <summary>
/// Method called every frame.
/// Should be used to update positional audio.
@@ -57,6 +41,11 @@ namespace Robust.Client.Audio.Midi
/// <param name="frameTime"></param>
void FrameUpdate(float frameTime);
/// <summary>
/// Volume, in db.
/// </summary>
float Volume { get; set; }
/// <summary>
/// If true, MIDI support is available.
/// </summary>
@@ -75,6 +64,7 @@ namespace Robust.Client.Audio.Midi
private SharedBroadPhaseSystem _broadPhaseSystem = default!;
[ViewVariables]
public bool IsAvailable
{
get
@@ -85,12 +75,29 @@ namespace Robust.Client.Audio.Midi
}
}
[ViewVariables]
private readonly List<IMidiRenderer> _renderers = new();
private bool _alive = true;
private Settings? _settings;
private Thread? _midiThread;
private ISawmill _midiSawmill = default!;
private float _volume = 0f;
private bool _volumeDirty = true;
[ViewVariables(VVAccess.ReadWrite)]
public float Volume
{
get => _volume;
set
{
if (MathHelper.CloseTo(_volume, value))
return;
_volume = value;
_volumeDirty = true;
}
}
private static readonly string[] LinuxSoundfonts =
{
@@ -117,6 +124,7 @@ namespace Robust.Client.Audio.Midi
private NFluidsynth.Logger.LoggerDelegate _loggerDelegate = default!;
private ISawmill _sawmill = default!;
[ViewVariables(VVAccess.ReadWrite)]
public int OcclusionCollisionMask { get; set; }
private void InitializeFluidsynth()
@@ -258,76 +266,79 @@ namespace Robust.Client.Audio.Midi
}
// Update positions of streams every frame.
lock (_renderers)
foreach (var renderer in _renderers)
{
foreach (var renderer in _renderers)
if (renderer.Disposed)
continue;
if(_volumeDirty)
renderer.Source.SetVolume(Volume);
if (!renderer.Mono)
{
if (renderer.Disposed)
continue;
renderer.Source.SetGlobal();
continue;
}
if (!renderer.Mono)
MapCoordinates? mapPos = null;
if (renderer.TrackingCoordinates != null)
{
mapPos = renderer.TrackingCoordinates.Value.ToMap(_entityManager);
}
else if (renderer.TrackingEntity != null)
{
mapPos = renderer.TrackingEntity.Transform.MapPosition;
}
if (mapPos != null)
{
var pos = mapPos.Value;
if (pos.MapId != _eyeManager.CurrentMap)
{
renderer.Source.SetVolume(-10000000);
}
else
{
var sourceRelative = _eyeManager.CurrentEye.Position.Position - pos.Position;
var occlusion = 0f;
if (sourceRelative.Length > 0)
{
occlusion = _broadPhaseSystem.IntersectRayPenetration(
pos.MapId,
new CollisionRay(
pos.Position,
sourceRelative.Normalized,
OcclusionCollisionMask),
sourceRelative.Length,
renderer.TrackingEntity);
}
renderer.Source.SetOcclusion(occlusion);
}
if (renderer.Source.SetPosition(pos.Position))
{
renderer.Source.SetGlobal();
continue;
}
MapCoordinates? mapPos = null;
if (renderer.TrackingCoordinates != null)
if (renderer.TrackingEntity != null)
{
mapPos = renderer.TrackingCoordinates.Value.ToMap(_entityManager);
}
else if (renderer.TrackingEntity != null)
{
mapPos = renderer.TrackingEntity.Transform.MapPosition;
renderer.Source.SetVelocity(renderer.TrackingEntity.GlobalLinearVelocity());
}
if (mapPos != null)
if (float.IsNaN(pos.Position.X) || float.IsNaN(pos.Position.Y))
{
var pos = mapPos.Value;
if (pos.MapId != _eyeManager.CurrentMap)
{
renderer.Source.SetVolume(-10000000);
}
else
{
var sourceRelative = _eyeManager.CurrentEye.Position.Position - pos.Position;
var occlusion = 0f;
if (sourceRelative.Length > 0)
{
occlusion = _broadPhaseSystem.IntersectRayPenetration(
pos.MapId,
new CollisionRay(
pos.Position,
sourceRelative.Normalized,
OcclusionCollisionMask),
sourceRelative.Length,
renderer.TrackingEntity);
}
renderer.Source.SetOcclusion(occlusion);
}
if (renderer.Source.SetPosition(pos.Position))
{
continue;
}
if (renderer.TrackingEntity != null)
{
renderer.Source.SetVelocity(renderer.TrackingEntity.GlobalLinearVelocity());
}
if (float.IsNaN(pos.Position.X) || float.IsNaN(pos.Position.Y))
{
// just duck out instead of move to NaN
renderer.Source.SetOcclusion(float.MaxValue);
continue;
}
_midiSawmill?.Warning("Interrupting positional audio, can't set position.");
renderer.Source.StopPlaying();
// just duck out instead of move to NaN
renderer.Source.SetOcclusion(float.MaxValue);
continue;
}
_midiSawmill?.Warning("Interrupting positional audio, can't set position.");
renderer.Source.StopPlaying();
}
}
_volumeDirty = false;
}
/// <summary>

View File

@@ -8,6 +8,7 @@ using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
using MidiEvent = NFluidsynth.MidiEvent;
namespace Robust.Client.Audio.Midi
@@ -205,8 +206,11 @@ namespace Robust.Client.Audio.Midi
private SequencerClientId _synthRegister;
public IClydeBufferedAudioSource Source { get; set; }
IClydeBufferedAudioSource IMidiRenderer.Source => Source;
[ViewVariables]
public bool Disposed { get; private set; } = false;
[ViewVariables(VVAccess.ReadWrite)]
public byte MidiProgram
{
get => _midiProgram;
@@ -220,6 +224,7 @@ namespace Robust.Client.Audio.Midi
}
}
[ViewVariables(VVAccess.ReadWrite)]
public byte MidiBank
{
get => _midiBank;
@@ -233,6 +238,7 @@ namespace Robust.Client.Audio.Midi
}
}
[ViewVariables(VVAccess.ReadWrite)]
public uint MidiSoundfont
{
get => _midiSoundfont;
@@ -246,10 +252,16 @@ namespace Robust.Client.Audio.Midi
}
}
[ViewVariables(VVAccess.ReadWrite)]
public bool DisablePercussionChannel { get; set; } = true;
[ViewVariables(VVAccess.ReadWrite)]
public bool DisableProgramChangeEvent { get; set; } = true;
[ViewVariables(VVAccess.ReadWrite)]
public int PlayerTotalTick => _player?.GetTotalTicks ?? 0;
[ViewVariables(VVAccess.ReadWrite)]
public int PlayerTick
{
get => _player?.CurrentTick ?? 0;
@@ -260,12 +272,19 @@ namespace Robust.Client.Audio.Midi
}
}
[ViewVariables(VVAccess.ReadWrite)]
public uint SequencerTick => _sequencer?.Tick ?? 0;
[ViewVariables(VVAccess.ReadWrite)]
public double SequencerTimeScale => _sequencer?.TimeScale ?? 0;
[ViewVariables(VVAccess.ReadWrite)]
public bool Mono { get; set; }
[ViewVariables]
public MidiRendererStatus Status { get; private set; } = MidiRendererStatus.None;
[ViewVariables(VVAccess.ReadWrite)]
public bool LoopMidi
{
get => _loopMidi;
@@ -277,10 +296,11 @@ namespace Robust.Client.Audio.Midi
}
}
[ViewVariables(VVAccess.ReadWrite)]
public IEntity? TrackingEntity { get; set; } = null;
public EntityCoordinates? TrackingCoordinates { get; set; } = null;
internal bool Free { get; set; } = false;
[ViewVariables(VVAccess.ReadWrite)]
public EntityCoordinates? TrackingCoordinates { get; set; } = null;
internal MidiRenderer(Settings settings, SoundFontLoader soundFontLoader, bool mono = true)
{

View File

@@ -30,7 +30,8 @@ namespace Robust.Client.Console
var console = new ScriptConsoleServer(this, session);
_activeConsoles.Add(session, console);
console.Open();
// FIXME: When this is Open(), resizing the window will cause its position to get NaN'd.
console.OpenCentered();
}
private void ReceiveScriptResponse(MsgScriptResponse message)

View File

@@ -93,7 +93,7 @@ namespace Robust.Client
// Disable load context usage on content start.
// This prevents Content.Client being loaded twice and things like csi blowing up because of it.
_modLoader.SetUseLoadContext(!ContentStart);
_modLoader.SetEnableSandboxing(Options.Sandboxing);
_modLoader.SetEnableSandboxing(false && Options.Sandboxing);
if (!_modLoader.TryLoadModulesFrom(new ResourcePath("/Assemblies/"), Options.ContentModulePrefix))
{

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.Animations;
@@ -167,7 +168,7 @@ namespace Robust.Client.GameObjects
set
{
_radius = MathF.Max(value, 0.01f); // setting radius to 0 causes exceptions, so just use a value close enough to zero that it's unnoticeable.
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PointLightRadiusChangedMessage(this));
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PointLightRadiusChangedEvent(this));
}
}
@@ -179,6 +180,18 @@ namespace Robust.Client.GameObjects
Mask = null;
}
/// <summary>
/// What MapId we are intersecting for RenderingTreeSystem.
/// </summary>
[ViewVariables]
internal MapId IntersectingMapId { get; set; } = MapId.Nullspace;
/// <summary>
/// What grids we're on for RenderingTreeSystem.
/// </summary>
[ViewVariables]
internal List<GridId> IntersectingGrids = new();
void ISerializationHooks.AfterDeserialization()
{
if (_maskPath != null)
@@ -230,7 +243,7 @@ namespace Robust.Client.GameObjects
if (map != MapId.Nullspace)
{
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local,
new RenderTreeRemoveLightMessage(this, map));
new RenderTreeRemoveLightEvent(this, map));
}
}
@@ -248,11 +261,11 @@ namespace Robust.Client.GameObjects
}
}
public struct PointLightRadiusChangedMessage
public class PointLightRadiusChangedEvent : EntityEventArgs
{
public PointLightComponent PointLightComponent { get; }
public PointLightRadiusChangedMessage(PointLightComponent pointLightComponent)
public PointLightRadiusChangedEvent(PointLightComponent pointLightComponent)
{
PointLightComponent = pointLightComponent;
}

View File

@@ -125,6 +125,18 @@ namespace Robust.Client.GameObjects
[DataField("directional")]
private bool _directional = true;
/// <summary>
/// What MapId we are intersecting for RenderingTreeSystem.
/// </summary>
[ViewVariables]
internal MapId IntersectingMapId { get; set; } = MapId.Nullspace;
/// <summary>
/// What grids we're on for RenderingTreeSystem.
/// </summary>
[ViewVariables]
internal List<GridId> IntersectingGrids { get; } = new();
[DataField("layerDatums")]
private List<PrototypeLayerData> LayerDatums
{
@@ -1355,7 +1367,7 @@ namespace Robust.Client.GameObjects
if (map != MapId.Nullspace)
{
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local,
new RenderTreeRemoveSpriteMessage(this, map));
new RenderTreeRemoveSpriteEvent(this, map));
}
}

View File

@@ -1,4 +1,6 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using JetBrains.Annotations;
using Robust.Client.Physics;
using Robust.Shared.GameObjects;
@@ -6,6 +8,7 @@ using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Utility;
namespace Robust.Client.GameObjects
{
@@ -47,145 +50,176 @@ namespace Robust.Client.GameObjects
_mapManager.OnGridCreated += MapManagerOnGridCreated;
_mapManager.OnGridRemoved += MapManagerOnGridRemoved;
SubscribeLocalEvent<EntMapIdChangedMessage>(EntMapIdChanged);
SubscribeLocalEvent<MoveEvent>(EntMoved);
SubscribeLocalEvent<EntParentChangedMessage>(EntParentChanged);
SubscribeLocalEvent<PointLightRadiusChangedMessage>(PointLightRadiusChanged);
SubscribeLocalEvent<RenderTreeRemoveSpriteMessage>(RemoveSprite);
SubscribeLocalEvent<RenderTreeRemoveLightMessage>(RemoveLight);
SubscribeLocalEvent<SpriteComponent, EntMapIdChangedMessage>(SpriteMapChanged);
SubscribeLocalEvent<SpriteComponent, MoveEvent>(SpriteMoved);
SubscribeLocalEvent<SpriteComponent, EntParentChangedMessage>(SpriteParentChanged);
SubscribeLocalEvent<SpriteComponent, RenderTreeRemoveSpriteEvent>(RemoveSprite);
SubscribeLocalEvent<PointLightComponent, EntMapIdChangedMessage>(LightMapChanged);
SubscribeLocalEvent<PointLightComponent, MoveEvent>(LightMoved);
SubscribeLocalEvent<PointLightComponent, EntParentChangedMessage>(LightParentChanged);
SubscribeLocalEvent<PointLightComponent, PointLightRadiusChangedEvent>(PointLightRadiusChanged);
SubscribeLocalEvent<PointLightComponent, RenderTreeRemoveLightEvent>(RemoveLight);
}
// For the RemoveX methods
// If the Transform is removed BEFORE the Sprite/Light,
// then the MapIdChanged code will handle and remove it (because MapId gets set to nullspace).
// Otherwise these will still have their past MapId and that's all we need..
#region SpriteHandlers
private void SpriteMapChanged(EntityUid uid, SpriteComponent component, EntMapIdChangedMessage args)
{
QueueSpriteUpdate(component);
}
private void SpriteMoved(EntityUid uid, SpriteComponent component, MoveEvent args)
{
QueueSpriteUpdate(component);
}
private void SpriteParentChanged(EntityUid uid, SpriteComponent component, EntParentChangedMessage args)
{
QueueSpriteUpdate(component);
}
private void RemoveSprite(EntityUid uid, SpriteComponent component, RenderTreeRemoveSpriteEvent args)
{
ClearSprite(component);
}
private void ClearSprite(SpriteComponent component)
{
if (_gridTrees.TryGetValue(component.IntersectingMapId, out var gridTrees))
{
foreach (var gridId in component.IntersectingGrids)
{
if (!gridTrees.TryGetValue(gridId, out var tree)) continue;
tree.SpriteTree.Remove(component);
}
}
component.IntersectingGrids.Clear();
}
private void QueueSpriteUpdate(SpriteComponent component)
{
if (component.TreeUpdateQueued) return;
component.TreeUpdateQueued = true;
_spriteQueue.Add(component);
foreach (var child in component.Owner.Transform.Children)
{
QueueSpriteUpdate(child.Owner);
}
}
private void QueueSpriteUpdate(IEntity entity)
{
if (!entity.TryGetComponent(out SpriteComponent? spriteComponent)) return;
QueueSpriteUpdate(spriteComponent);
foreach (var child in entity.Transform.Children)
{
QueueSpriteUpdate(child.Owner);
}
}
#endregion
#region LightHandlers
private void LightMapChanged(EntityUid uid, PointLightComponent component, EntMapIdChangedMessage args)
{
QueueLightUpdate(component);
}
private void LightMoved(EntityUid uid, PointLightComponent component, MoveEvent args)
{
QueueLightUpdate(component);
}
private void LightParentChanged(EntityUid uid, PointLightComponent component, EntParentChangedMessage args)
{
QueueLightUpdate(component);
}
private void PointLightRadiusChanged(EntityUid uid, PointLightComponent component, PointLightRadiusChangedEvent args)
{
QueueLightUpdate(component);
}
private void RemoveLight(EntityUid uid, PointLightComponent component, RenderTreeRemoveLightEvent args)
{
ClearLight(component);
}
private void ClearLight(PointLightComponent component)
{
if (_gridTrees.TryGetValue(component.IntersectingMapId, out var gridTrees))
{
foreach (var gridId in component.IntersectingGrids)
{
if (!gridTrees.TryGetValue(gridId, out var tree)) continue;
tree.LightTree.Remove(component);
}
}
component.IntersectingGrids.Clear();
}
private void QueueLightUpdate(PointLightComponent component)
{
if (component.TreeUpdateQueued) return;
component.TreeUpdateQueued = true;
_lightQueue.Add(component);
foreach (var child in component.Owner.Transform.Children)
{
QueueLightUpdate(child.Owner);
}
}
private void QueueLightUpdate(IEntity entity)
{
if (!entity.TryGetComponent(out PointLightComponent? lightComponent)) return;
QueueLightUpdate(lightComponent);
foreach (var child in entity.Transform.Children)
{
QueueLightUpdate(child.Owner);
}
}
#endregion
public override void Shutdown()
{
base.Shutdown();
_mapManager.MapCreated -= MapManagerOnMapCreated;
_mapManager.MapDestroyed -= MapManagerOnMapDestroyed;
_mapManager.OnGridCreated -= MapManagerOnGridCreated;
_mapManager.OnGridRemoved -= MapManagerOnGridRemoved;
}
// For these next 2 methods (the Remove* ones):
// If the Transform is removed BEFORE the Sprite/Light,
// then the MapIdChanged code will handle and remove it (because MapId gets set to nullspace).
// Otherwise these will still have their past MapId and that's all we need..
private void RemoveLight(RenderTreeRemoveLightMessage ev)
{
foreach (var gridId in _mapManager.FindGridIdsIntersecting(ev.Map, MapTrees.LightAabbFunc(ev.Light), true))
{
_gridTrees[ev.Map][gridId].LightTree.Remove(ev.Light);
}
}
private void RemoveSprite(RenderTreeRemoveSpriteMessage ev)
{
foreach (var gridId in _mapManager.FindGridIdsIntersecting(ev.Map, MapTrees.SpriteAabbFunc(ev.Sprite), true))
{
_gridTrees[ev.Map][gridId].SpriteTree.Remove(ev.Sprite);
}
}
private void PointLightRadiusChanged(PointLightRadiusChangedMessage ev)
{
QueueUpdateLight(ev.PointLightComponent);
}
private void EntParentChanged(EntParentChangedMessage ev)
{
UpdateEntity(ev.Entity);
}
private void EntMoved(MoveEvent ev)
{
UpdateEntity(ev.Sender);
}
private void UpdateEntity(IEntity entity)
{
if (entity.TryGetComponent(out SpriteComponent? spriteComponent))
{
if (!spriteComponent.TreeUpdateQueued)
{
spriteComponent.TreeUpdateQueued = true;
_spriteQueue.Add(spriteComponent);
}
}
if (entity.TryGetComponent(out PointLightComponent? light))
{
QueueUpdateLight(light);
}
foreach (var child in entity.Transform.ChildEntityUids)
{
UpdateEntity(EntityManager.GetEntity(child));
}
}
private void QueueUpdateLight(PointLightComponent light)
{
if (!light.TreeUpdateQueued)
{
light.TreeUpdateQueued = true;
_lightQueue.Add(light);
}
}
private void EntMapIdChanged(EntMapIdChangedMessage ev)
{
// Nullspace is a valid map ID for stuff to have but we also aren't gonna bother indexing it.
// So that's why there's a GetValueOrDefault.
var oldMapTrees = _gridTrees.GetValueOrDefault(ev.OldMapId);
var newMapTrees = _gridTrees.GetValueOrDefault(ev.Entity.Transform.MapID);
// TODO: MMMM probably a better way to do this.
if (ev.Entity.TryGetComponent(out SpriteComponent? sprite))
{
if (oldMapTrees != null)
{
foreach (var (_, gridTree) in oldMapTrees)
{
gridTree.SpriteTree.Remove(sprite);
}
}
var bounds = MapTrees.SpriteAabbFunc(sprite);
foreach (var gridId in _mapManager.FindGridIdsIntersecting(ev.Entity.Transform.MapID, bounds, true))
{
var gridBounds = gridId == GridId.Invalid
? bounds : bounds.Translated(-_mapManager.GetGrid(gridId).WorldPosition);
newMapTrees?[gridId].SpriteTree.AddOrUpdate(sprite, gridBounds);
}
}
if (ev.Entity.TryGetComponent(out PointLightComponent? light))
{
if (oldMapTrees != null)
{
foreach (var (_, gridTree) in oldMapTrees)
{
gridTree.LightTree.Remove(light);
}
}
var bounds = MapTrees.LightAabbFunc(light);
foreach (var gridId in _mapManager.FindGridIdsIntersecting(ev.Entity.Transform.MapID, bounds, true))
{
var gridBounds = gridId == GridId.Invalid
? bounds : bounds.Translated(-_mapManager.GetGrid(gridId).WorldPosition);
newMapTrees?[gridId].LightTree.AddOrUpdate(light, gridBounds);
}
}
}
private void MapManagerOnMapDestroyed(object? sender, MapEventArgs e)
{
foreach (var (_, gridTree) in _gridTrees[e.Map])
{
foreach (var comp in gridTree.LightTree)
{
comp.IntersectingGrids.Clear();
}
foreach (var comp in gridTree.SpriteTree)
{
comp.IntersectingGrids.Clear();
}
// Just in case?
gridTree.LightTree.Clear();
gridTree.SpriteTree.Clear();
}
_gridTrees.Remove(e.Map);
}
@@ -209,47 +243,109 @@ namespace Robust.Client.GameObjects
private void MapManagerOnGridRemoved(MapId mapId, GridId gridId)
{
var gridTree = _gridTrees[mapId][gridId];
foreach (var sprite in gridTree.SpriteTree)
{
sprite.IntersectingGrids.Remove(gridId);
}
foreach (var light in gridTree.LightTree)
{
light.IntersectingGrids.Remove(gridId);
}
// Clear in case
gridTree.LightTree.Clear();
gridTree.SpriteTree.Clear();
_gridTrees[mapId].Remove(gridId);
}
public override void FrameUpdate(float frameTime)
{
foreach (var queuedUpdateSprite in _spriteQueue)
foreach (var sprite in _spriteQueue)
{
var map = queuedUpdateSprite.Owner.Transform.MapID;
if (map == MapId.Nullspace)
var mapId = sprite.Owner.Transform.MapID;
// If we're on a new map then clear the old one.
if (sprite.IntersectingMapId != mapId)
{
continue;
ClearSprite(sprite);
}
var mapTree = _gridTrees[map];
sprite.IntersectingMapId = mapId;
foreach (var gridId in _mapManager.FindGridIdsIntersecting(map,
MapTrees.SpriteAabbFunc(queuedUpdateSprite), true))
if (mapId == MapId.Nullspace) continue;
var mapTree = _gridTrees[mapId];
var aabb = MapTrees.SpriteAabbFunc(sprite);
var intersectingGrids = _mapManager.FindGridIdsIntersecting(mapId, aabb, true).ToList();
// Remove from old
foreach (var gridId in sprite.IntersectingGrids)
{
mapTree[gridId].SpriteTree.AddOrUpdate(queuedUpdateSprite);
if (intersectingGrids.Contains(gridId)) continue;
mapTree[gridId].SpriteTree.Remove(sprite);
}
queuedUpdateSprite.TreeUpdateQueued = false;
// Rebuild in the update below
sprite.IntersectingGrids.Clear();
// Update / add to new
foreach (var gridId in intersectingGrids)
{
var translated = aabb.Translated(gridId == GridId.Invalid
? Vector2.Zero
: -_mapManager.GetGrid(gridId).WorldPosition);
mapTree[gridId].SpriteTree.AddOrUpdate(sprite, translated);
sprite.IntersectingGrids.Add(gridId);
}
sprite.TreeUpdateQueued = false;
}
foreach (var queuedUpdateLight in _lightQueue)
foreach (var light in _lightQueue)
{
var map = queuedUpdateLight.Owner.Transform.MapID;
if (map == MapId.Nullspace)
var mapId = light.Owner.Transform.MapID;
// If we're on a new map then clear the old one.
if (light.IntersectingMapId != mapId)
{
continue;
ClearLight(light);
}
var mapTree = _gridTrees[map];
light.IntersectingMapId = mapId;
foreach (var gridId in _mapManager.FindGridIdsIntersecting(map,
MapTrees.LightAabbFunc(queuedUpdateLight), true))
if (mapId == MapId.Nullspace) continue;
var mapTree = _gridTrees[mapId];
var aabb = MapTrees.LightAabbFunc(light);
var intersectingGrids = _mapManager.FindGridIdsIntersecting(mapId, aabb, true).ToList();
// Remove from old
foreach (var gridId in intersectingGrids)
{
mapTree[gridId].LightTree.AddOrUpdate(queuedUpdateLight);
if (intersectingGrids.Contains(gridId)) continue;
mapTree[gridId].LightTree.Remove(light);
}
queuedUpdateLight.TreeUpdateQueued = false;
// Rebuild in the update below
light.IntersectingGrids.Clear();
// Update / add to new
foreach (var gridId in intersectingGrids)
{
var translated = aabb.Translated(gridId == GridId.Invalid
? Vector2.Zero
: -_mapManager.GetGrid(gridId).WorldPosition);
mapTree[gridId].LightTree.AddOrUpdate(light, translated);
light.IntersectingGrids.Add(gridId);
}
light.TreeUpdateQueued = false;
}
_spriteQueue.Clear();
@@ -284,9 +380,9 @@ namespace Robust.Client.GameObjects
}
}
internal struct RenderTreeRemoveSpriteMessage
internal class RenderTreeRemoveSpriteEvent : EntityEventArgs
{
public RenderTreeRemoveSpriteMessage(SpriteComponent sprite, MapId map)
public RenderTreeRemoveSpriteEvent(SpriteComponent sprite, MapId map)
{
Sprite = sprite;
Map = map;
@@ -296,9 +392,9 @@ namespace Robust.Client.GameObjects
public MapId Map { get; }
}
internal struct RenderTreeRemoveLightMessage
internal class RenderTreeRemoveLightEvent : EntityEventArgs
{
public RenderTreeRemoveLightMessage(PointLightComponent light, MapId map)
public RenderTreeRemoveLightEvent(PointLightComponent light, MapId map)
{
Light = light;
Map = map;

View File

@@ -16,11 +16,17 @@ namespace Robust.Client.GameObjects
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
private RenderingTreeSystem _treeSystem = default!;
public override void Initialize()
{
base.Initialize();
_treeSystem = Get<RenderingTreeSystem>();
}
/// <inheritdoc />
public override void FrameUpdate(float frameTime)
{
var renderTreeSystem = EntitySystemManager.GetEntitySystem<RenderingTreeSystem>();
// So we could calculate the correct size of the entities based on the contents of their sprite...
// Or we can just assume that no entity is larger than 10x10 and get a stupid easy check.
var pvsBounds = _eyeManager.GetWorldViewport().Enlarged(5);
@@ -33,18 +39,9 @@ namespace Robust.Client.GameObjects
foreach (var gridId in _mapManager.FindGridIdsIntersecting(currentMap, pvsBounds, true))
{
Box2 gridBounds;
var gridBounds = gridId == GridId.Invalid ? pvsBounds : pvsBounds.Translated(-_mapManager.GetGrid(gridId).WorldPosition);
if (gridId == GridId.Invalid)
{
gridBounds = pvsBounds;
}
else
{
gridBounds = pvsBounds.Translated(-_mapManager.GetGrid(gridId).WorldPosition);
}
var mapTree = renderTreeSystem.GetSpriteTreeForMap(currentMap, gridId);
var mapTree = _treeSystem.GetSpriteTreeForMap(currentMap, gridId);
mapTree.QueryAabb(ref frameTime, (ref float state, in SpriteComponent value) =>
{

View File

@@ -146,13 +146,12 @@ namespace Robust.Client.GameStates
private void DrawWorld(in OverlayDrawArgs args)
{
bool pvsEnabled = _configurationManager.GetCVar<bool>("net.pvs");
if(!pvsEnabled)
return;
float pvsSize = _configurationManager.GetCVar<float>("net.maxupdaterange");
float pvsRange = _configurationManager.GetCVar<float>("net.maxupdaterange");
var pvsCenter = _eyeManager.CurrentEye.Position;
Box2 pvsBox = Box2.CenteredAround(pvsCenter.Position, new Vector2(pvsSize, pvsSize));
Box2 pvsBox = Box2.CenteredAround(pvsCenter.Position, new Vector2(pvsRange * 2, pvsRange * 2));
var worldHandle = args.WorldHandle;

View File

@@ -183,6 +183,32 @@ namespace Robust.Client.Graphics.Clyde
if (handles.filterHandle != 0) EFX.DeleteFilter(handles.filterHandle);
}
public AudioStream LoadAudioRaw(short[] samples, int channels, int sampleRate)
{
var buffer = AL.GenBuffer();
unsafe
{
fixed (short* ptr = samples)
{
AL.BufferData(
buffer,
channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16,
(IntPtr) ptr,
samples.Length * 2,
sampleRate);
}
}
_checkAlError();
var handle = new ClydeHandle(_audioSampleBuffers.Count);
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
// ReSharper disable once PossibleLossOfFraction
var length = TimeSpan.FromSeconds(samples.Length / channels / (double) sampleRate);
return new AudioStream(handle, length, channels);
}
public void SetMasterVolume(float newVolume)
{
AL.Listener(ALListenerf.Gain, _baseGain * newVolume);
@@ -439,6 +465,21 @@ namespace Robust.Client.Graphics.Clyde
_checkAlError();
}
public void SetVolumeDirect(float decibels)
{
_checkDisposed();
var priorOcclusion = 1f;
if (!IsEfxSupported)
{
AL.GetSource(SourceHandle, ALSourcef.Gain, out var priorGain);
priorOcclusion = priorGain / _gain;
}
_gain = decibels;
AL.Source(SourceHandle, ALSourcef.Gain, _gain * priorOcclusion);
_checkAlError();
}
public void SetOcclusion(float blocks)
{
_checkDisposed();
@@ -747,6 +788,21 @@ namespace Robust.Client.Graphics.Clyde
_checkAlError();
}
public void SetVolumeDirect(float masterVolumeDecay)
{
_checkDisposed();
var priorOcclusion = 1f;
if (!IsEfxSupported)
{
AL.GetSource(SourceHandle!.Value, ALSourcef.Gain, out var priorGain);
priorOcclusion = priorGain / _gain;
}
_gain = masterVolumeDecay;
AL.Source(SourceHandle!.Value, ALSourcef.Gain, _gain * priorOcclusion);
_checkAlError();
}
public void SetPitch(float pitch)
{
_checkDisposed();

View File

@@ -248,6 +248,11 @@ namespace Robust.Client.Graphics.Clyde
return new(default, default, 1, name);
}
public AudioStream LoadAudioRaw(short[] samples, int channels, int sampleRate)
{
throw new NotImplementedException();
}
public IClydeAudioSource CreateAudioSource(AudioStream stream)
{
return DummyAudioSource.Instance;
@@ -337,6 +342,11 @@ namespace Robust.Client.Graphics.Clyde
{
// Nada.
}
public void SetVolumeDirect(float masterVolumeDecay)
{
// Nada.
}
}
private sealed class DummyBufferedAudioSource : DummyAudioSource, IClydeBufferedAudioSource

View File

@@ -8,6 +8,7 @@ namespace Robust.Client.Graphics
// AUDIO SYSTEM DOWN BELOW.
AudioStream LoadAudioOggVorbis(Stream stream, string? name = null);
AudioStream LoadAudioWav(Stream stream, string? name = null);
AudioStream LoadAudioRaw(short[] samples, int channels, int sampleRate);
void SetMasterVolume(float newVolume);

View File

@@ -21,5 +21,6 @@ namespace Robust.Client.Graphics
void SetOcclusion(float blocks);
void SetPlaybackPosition(float seconds);
void SetVelocity(Vector2 velocity);
void SetVolumeDirect(float masterVolumeDecay);
}
}

View File

@@ -4,7 +4,7 @@ using Robust.Shared.Maths;
namespace Robust.Client.Graphics
{
internal interface IRenderHandle
public interface IRenderHandle
{
DrawingHandleScreen DrawingHandleScreen { get; }
DrawingHandleWorld DrawingHandleWorld { get; }

View File

@@ -463,7 +463,7 @@ namespace Robust.Client.UserInterface
{
}
internal virtual void DrawInternal(IRenderHandle renderHandle)
public virtual void DrawInternal(IRenderHandle renderHandle)
{
Draw(renderHandle.DrawingHandleScreen);
}

View File

@@ -41,7 +41,7 @@ namespace Robust.Client.UserInterface.Controls
return (32, 32) * Scale;
}
internal override void DrawInternal(IRenderHandle renderHandle)
public override void DrawInternal(IRenderHandle renderHandle)
{
if (Sprite == null || Sprite.Deleted)
{

View File

@@ -96,7 +96,7 @@ namespace Robust.Client.UserInterface.CustomControls
var (spaceX, spaceY) = Parent!.Size;
if (Position.Y > spaceY)
{
LayoutContainer.SetPosition(this, (Position.X, spaceY - HEADER_SIZE_Y));
LayoutContainer.SetPosition(this, (Position.X, spaceY + HEADER_SIZE_Y));
}
if (Position.X > spaceX)
@@ -104,6 +104,16 @@ namespace Robust.Client.UserInterface.CustomControls
// 50 is arbitrary here. As long as it's bumped back into view.
LayoutContainer.SetPosition(this, (spaceX - 50, Position.Y));
}
if (Position.Y < 0)
{
LayoutContainer.SetPosition(this, (Position.X, 0));
}
if (Position.X < 0)
{
LayoutContainer.SetPosition(this, (0, Position.Y));
}
}
protected override DragMode GetDragModeFor(Vector2 relativeMousePos)

View File

@@ -2,7 +2,7 @@ using Robust.Shared.Map;
namespace Robust.Shared.GameObjects
{
public readonly struct EntMapIdChangedMessage
public class EntMapIdChangedMessage : EntityEventArgs
{
public EntMapIdChangedMessage(IEntity entity, MapId oldMapId)
{

View File

@@ -105,7 +105,7 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Pro
bool skipHook,
ISerializationContext? context)
{
return new();
return new(source);
}
}
}