Compare commits

..

2 Commits

Author SHA1 Message Date
DrSmugleaf
3ae65f9e3d Version: 248.0.2-source-gen-debug 2025-03-20 21:13:47 -07:00
DrSmugleaf
af6fc51def Source gen changes 2025-03-20 18:06:10 -07:00
36 changed files with 587 additions and 1000 deletions

View File

@@ -1,4 +1,4 @@
<Project>
<!-- This file automatically reset by Tools/version.py -->
<!-- This file automatically reset by Tools/version.py -->

View File

@@ -54,28 +54,7 @@ END TEMPLATE-->
*None yet*
## 249.0.0
### Breaking changes
* Layer is now read-only on VisibilityComponent and isn't serialized.
### New features
* Added a debug overlay for the linear and angular velocity of all entities on the screen. Use the `showvel` and `showangvel` commands to toggle it.
* Add a GetWorldManifold overload that doesn't require a span of points.
* Added a GetVisMaskEvent. Calling `RefreshVisibilityMask` will raise it and subscribers can update the vismask via the event rather than subscribers having to each manually try and handle the vismask directly.
### Bugfixes
* `BoxContainer` no longer causes stretching children to go below their minimum size.
* Fix lights on other grids getting clipped due to ignoring the light range cvar.
* Fix the `showvelocities` command.
* Fix the DirtyFields overload not being sandbox safe for content.
### Internal
* Polygon vertices are now inlined with FixedArray8 and a separate SlimPolygon using FixedArray4 for hot paths rather than using pooled arrays.
## 248.0.2-source-gen-debug
## 248.0.2

View File

@@ -428,20 +428,11 @@ cmd-entfo-help = Usage: entfo <entityuid>
The entity UID can be prefixed with 'c' to convert it to a client entity UID.
cmd-fuck-desc = Throws an exception
cmd-fuck-help = Usage: fuck
cmd-fuck-help = Throws an exception
cmd-showpos-desc = Show the position of all entities on the screen.
cmd-showpos-desc = Enables debug drawing over all entity positions in the game.
cmd-showpos-help = Usage: showpos
cmd-showrot-desc = Show the rotation of all entities on the screen.
cmd-showrot-help = Usage: showrot
cmd-showvel-desc = Show the local velocity of all entites on the screen.
cmd-showvel-help = Usage: showvel
cmd-showangvel-desc = Show the angular velocity of all entities on the screen.
cmd-showangvel-help = Usage: showangvel
cmd-sggcell-desc = Lists entities on a snap grid cell.
cmd-sggcell-help = Usage: sggcell <gridID> <vector2i>\nThat vector2i param is in the form x<int>,y<int>.

View File

@@ -173,51 +173,29 @@ namespace Robust.Client.Console.Commands
}
}
internal sealed class ShowPositionsCommand : LocalizedEntityCommands
internal sealed class ShowPositionsCommand : LocalizedCommands
{
[Dependency] private readonly DebugDrawingSystem _debugDrawing = default!;
[Dependency] private readonly IEntitySystemManager _entitySystems = default!;
public override string Command => "showpos";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
_debugDrawing.DebugPositions = !_debugDrawing.DebugPositions;
var mgr = _entitySystems.GetEntitySystem<DebugDrawingSystem>();
mgr.DebugPositions = !mgr.DebugPositions;
}
}
internal sealed class ShowRotationsCommand : LocalizedEntityCommands
internal sealed class ShowRotationsCommand : LocalizedCommands
{
[Dependency] private readonly DebugDrawingSystem _debugDrawing = default!;
[Dependency] private readonly IEntitySystemManager _entitySystems = default!;
public override string Command => "showrot";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
_debugDrawing.DebugRotations = !_debugDrawing.DebugRotations;
}
}
internal sealed class ShowVelocitiesCommand : LocalizedEntityCommands
{
[Dependency] private readonly DebugDrawingSystem _debugDrawing = default!;
public override string Command => "showvel";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
_debugDrawing.DebugVelocities = !_debugDrawing.DebugVelocities;
}
}
internal sealed class ShowAngularVelocitiesCommand : LocalizedEntityCommands
{
[Dependency] private readonly DebugDrawingSystem _debugDrawing = default!;
public override string Command => "showangvel";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
_debugDrawing.DebugAngularVelocities = !_debugDrawing.DebugAngularVelocities;
var mgr = _entitySystems.GetEntitySystem<DebugDrawingSystem>();
mgr.DebugRotations = !mgr.DebugRotations;
}
}

View File

@@ -5,15 +5,15 @@ using Robust.Shared.IoC;
namespace Robust.Client.Console.Commands
{
public sealed class ShowPlayerVelocityCommand : LocalizedCommands
public sealed class VelocitiesCommand : LocalizedCommands
{
[Dependency] private readonly IEntitySystemManager _entitySystems = default!;
public override string Command => "showplayervelocity";
public override string Command => "showvelocities";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
_entitySystems.GetEntitySystem<ShowPlayerVelocityDebugSystem>().Enabled ^= true;
_entitySystems.GetEntitySystem<VelocityDebugSystem>().Enabled ^= true;
}
}
}

View File

@@ -1,221 +1,140 @@
using System.Numerics;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Physics.Components;
using System.Numerics;
namespace Robust.Client.Debugging;
/// <summary>
/// A collection of visual debug overlays for the client game.
/// </summary>
public sealed class DebugDrawingSystem : EntitySystem
namespace Robust.Client.Debugging
{
[Dependency] private readonly IOverlayManager _overlayManager = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
private bool _debugPositions;
private bool _debugRotations;
private bool _debugVelocities;
private bool _debugAngularVelocities;
/// <summary>
/// Toggles the visual overlay of the local origin for each entity on screen.
/// A collection of visual debug overlays for the client game.
/// </summary>
public bool DebugPositions
public sealed class DebugDrawingSystem : EntitySystem
{
get => _debugPositions;
set
[Dependency] private readonly IOverlayManager _overlayManager = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly TransformSystem _transform = default!;
private bool _debugPositions;
private bool _debugRotations;
/// <summary>
/// Toggles the visual overlay of the local origin for each entity on screen.
/// </summary>
public bool DebugPositions
{
if (value == DebugPositions)
get => _debugPositions;
set
{
return;
}
if (value == DebugPositions)
{
return;
}
_debugPositions = value;
_debugPositions = value;
if (value && !_overlayManager.HasOverlay<EntityPositionOverlay>())
{
_overlayManager.AddOverlay(new EntityPositionOverlay(_lookup, _transform));
}
else
{
_overlayManager.RemoveOverlay<EntityPositionOverlay>();
if (value && !_overlayManager.HasOverlay<EntityPositionOverlay>())
{
_overlayManager.AddOverlay(new EntityPositionOverlay(_lookup, EntityManager, _transform));
}
else
{
_overlayManager.RemoveOverlay<EntityPositionOverlay>();
}
}
}
}
/// <summary>
/// Toggles the visual overlay of the rotation for each entity on screen.
/// </summary>
public bool DebugRotations
{
get => _debugRotations;
set
/// <summary>
/// Toggles the visual overlay of the local rotation.
/// </summary>
public bool DebugRotations
{
if (value == DebugRotations)
get => _debugRotations;
set
{
return;
}
if (value == DebugRotations)
{
return;
}
_debugRotations = value;
_debugRotations = value;
if (value && !_overlayManager.HasOverlay<EntityRotationOverlay>())
{
_overlayManager.AddOverlay(new EntityRotationOverlay(_lookup, _transform));
}
else
{
_overlayManager.RemoveOverlay<EntityRotationOverlay>();
if (value && !_overlayManager.HasOverlay<EntityRotationOverlay>())
{
_overlayManager.AddOverlay(new EntityRotationOverlay(_lookup, EntityManager));
}
else
{
_overlayManager.RemoveOverlay<EntityRotationOverlay>();
}
}
}
}
/// <summary>
/// Toggles the visual overlay of the local velocity for each entity on screen.
/// </summary>
public bool DebugVelocities
{
get => _debugVelocities;
set
private sealed class EntityPositionOverlay : Overlay
{
if (value == DebugVelocities)
private readonly EntityLookupSystem _lookup;
private readonly IEntityManager _entityManager;
private readonly SharedTransformSystem _transform;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public EntityPositionOverlay(EntityLookupSystem lookup, IEntityManager entityManager, SharedTransformSystem transform)
{
return;
_lookup = lookup;
_entityManager = entityManager;
_transform = transform;
}
_debugVelocities = value;
protected internal override void Draw(in OverlayDrawArgs args)
{
const float stubLength = 0.25f;
if (value && !_overlayManager.HasOverlay<EntityVelocityOverlay>())
{
_overlayManager.AddOverlay(new EntityVelocityOverlay(EntityManager, _lookup, _transform));
}
else
{
_overlayManager.RemoveOverlay<EntityVelocityOverlay>();
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
foreach (var entity in _lookup.GetEntitiesIntersecting(args.MapId, args.WorldBounds))
{
var (center, worldRotation) = _transform.GetWorldPositionRotation(entity);
var xLine = worldRotation.RotateVec(Vector2.UnitX);
var yLine = worldRotation.RotateVec(Vector2.UnitY);
worldHandle.DrawLine(center, center + xLine * stubLength, Color.Red);
worldHandle.DrawLine(center, center + yLine * stubLength, Color.Green);
}
}
}
}
/// <summary>
/// Toggles the visual overlay of the angular velocity for each entity on screen.
/// </summary>
public bool DebugAngularVelocities
{
get => _debugAngularVelocities;
set
private sealed class EntityRotationOverlay : Overlay
{
if (value == DebugAngularVelocities)
private readonly EntityLookupSystem _lookup;
private readonly IEntityManager _entityManager;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public EntityRotationOverlay(EntityLookupSystem lookup, IEntityManager entityManager)
{
return;
_lookup = lookup;
_entityManager = entityManager;
}
_debugAngularVelocities = value;
if (value && !_overlayManager.HasOverlay<EntityAngularVelocityOverlay>())
protected internal override void Draw(in OverlayDrawArgs args)
{
_overlayManager.AddOverlay(new EntityAngularVelocityOverlay(EntityManager, _lookup, _transform));
}
else
{
_overlayManager.RemoveOverlay<EntityAngularVelocityOverlay>();
}
}
}
private sealed class EntityPositionOverlay(EntityLookupSystem _lookup, SharedTransformSystem _transform) : Overlay
{
public override OverlaySpace Space => OverlaySpace.WorldSpace;
const float stubLength = 0.25f;
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
var xformQuery = _entityManager.GetEntityQuery<TransformComponent>();
protected internal override void Draw(in OverlayDrawArgs args)
{
const float stubLength = 0.25f;
foreach (var entity in _lookup.GetEntitiesIntersecting(args.MapId, args.WorldBounds))
{
var (center, worldRotation) = xformQuery.GetComponent(entity).GetWorldPositionRotation();
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
var drawLine = worldRotation.RotateVec(-Vector2.UnitY);
foreach (var uid in _lookup.GetEntitiesIntersecting(args.MapId, args.WorldBounds))
{
var (center, worldRotation) = _transform.GetWorldPositionRotation(uid);
var xLine = worldRotation.RotateVec(Vector2.UnitX);
var yLine = worldRotation.RotateVec(Vector2.UnitY);
worldHandle.DrawLine(center, center + xLine * stubLength, Color.Red);
worldHandle.DrawLine(center, center + yLine * stubLength, Color.Green);
}
}
}
private sealed class EntityRotationOverlay(EntityLookupSystem _lookup, SharedTransformSystem _transform) : Overlay
{
public override OverlaySpace Space => OverlaySpace.WorldSpace;
protected internal override void Draw(in OverlayDrawArgs args)
{
const float stubLength = 0.25f;
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
foreach (var uid in _lookup.GetEntitiesIntersecting(args.MapId, args.WorldBounds))
{
var (center, worldRotation) = _transform.GetWorldPositionRotation(uid);
var drawLine = worldRotation.RotateVec(-Vector2.UnitY);
worldHandle.DrawLine(center, center + drawLine * stubLength, Color.Red);
}
}
}
private sealed class EntityVelocityOverlay(IEntityManager _entityManager, EntityLookupSystem _lookup, SharedTransformSystem _transform) : Overlay
{
public override OverlaySpace Space => OverlaySpace.WorldSpace;
protected internal override void Draw(in OverlayDrawArgs args)
{
const float multiplier = 0.2f;
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
var physicsQuery = _entityManager.GetEntityQuery<PhysicsComponent>();
foreach (var uid in _lookup.GetEntitiesIntersecting(args.MapId, args.WorldBounds))
{
if(!physicsQuery.TryGetComponent(uid, out var physicsComp))
continue;
var center = _transform.GetWorldPosition(uid);
var localVelocity = physicsComp.LinearVelocity;
if (localVelocity != Vector2.Zero)
worldHandle.DrawLine(center, center + localVelocity * multiplier, Color.Yellow);
}
}
}
private sealed class EntityAngularVelocityOverlay(IEntityManager _entityManager, EntityLookupSystem _lookup, SharedTransformSystem _transform) : Overlay
{
public override OverlaySpace Space => OverlaySpace.WorldSpace;
protected internal override void Draw(in OverlayDrawArgs args)
{
const float multiplier = (float)(0.2 / (2 * System.Math.PI));
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
var physicsQuery = _entityManager.GetEntityQuery<PhysicsComponent>();
foreach (var uid in _lookup.GetEntitiesIntersecting(args.MapId, args.WorldBounds))
{
if(!physicsQuery.TryGetComponent(uid, out var physicsComp))
continue;
var center = _transform.GetWorldPosition(uid);
var angularVelocity = physicsComp.AngularVelocity;
if (angularVelocity != 0.0f)
worldHandle.DrawCircle(center, angularVelocity * multiplier, angularVelocity > 0 ? Color.Magenta : Color.Blue, false);
worldHandle.DrawLine(center, center + drawLine * stubLength, Color.Red);
}
}
}
}
}

View File

@@ -117,7 +117,7 @@ namespace Robust.Client.GameObjects
base.DirtyField(uid, comp, fieldName, metadata);
}
public override void DirtyFields<T>(EntityUid uid, T comp, MetaDataComponent? meta, params string[] fields)
public override void DirtyFields<T>(EntityUid uid, T comp, MetaDataComponent? meta, params ReadOnlySpan<string> fields)
{
// TODO Prediction
// does the client actually need to dirty the field?

View File

@@ -1,66 +0,0 @@
using System.Numerics;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Physics.Components;
namespace Robust.Client.GameObjects;
public sealed class ShowPlayerVelocityDebugSystem : EntitySystem
{
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly IUserInterfaceManager _uiManager = default!;
internal bool Enabled
{
get => _label.Parent != null;
set
{
if (value)
{
_uiManager.WindowRoot.AddChild(_label);
}
else
{
_label.Orphan();
}
}
}
private Label _label = default!;
public override void Initialize()
{
base.Initialize();
_label = new Label();
}
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
if (!Enabled)
{
_label.Visible = false;
return;
}
var player = _playerManager.LocalEntity;
if (player == null || !EntityManager.TryGetComponent(player.Value, out PhysicsComponent? body))
{
_label.Visible = false;
return;
}
var screenPos = _eyeManager.WorldToScreen(_transform.GetWorldPosition(Transform(player.Value)));
LayoutContainer.SetPosition(_label, screenPos + new Vector2(0, 50));
_label.Visible = true;
_label.Text = $"Speed: {body.LinearVelocity.Length():0.00}\nLinear: {body.LinearVelocity.X:0.00}, {body.LinearVelocity.Y:0.00}\nAngular: {body.AngularVelocity}";
}
}

View File

@@ -0,0 +1,54 @@
using System.Numerics;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Physics.Components;
namespace Robust.Client.GameObjects
{
public sealed class VelocityDebugSystem : EntitySystem
{
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly TransformSystem _transform = default!;
internal bool Enabled { get; set; }
private Label _label = default!;
public override void Initialize()
{
base.Initialize();
_label = new Label();
IoCManager.Resolve<IUserInterfaceManager>().StateRoot.AddChild(_label);
}
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
if (!Enabled)
{
_label.Visible = false;
return;
}
var player = _playerManager.LocalEntity;
if (player == null || !EntityManager.TryGetComponent(player.Value, out PhysicsComponent? body))
{
_label.Visible = false;
return;
}
var screenPos = _eyeManager.WorldToScreen(_transform.GetWorldPosition(Transform(player.Value)));
LayoutContainer.SetPosition(_label, screenPos + new Vector2(0, 50));
_label.Visible = true;
_label.Text = $"Speed: {body.LinearVelocity.Length():0.00}\nLinear: {body.LinearVelocity.X:0.00}, {body.LinearVelocity.Y:0.00}\nAngular: {body.AngularVelocity}";
}
}
}

View File

@@ -98,11 +98,9 @@ namespace Robust.Client.Graphics.Clyde
private LightCapacityComparer _lightCap = new();
private ShadowCapacityComparer _shadowCap = new ShadowCapacityComparer();
private float _maxLightRadius;
private unsafe void InitLighting()
{
_cfg.OnValueChanged(CVars.MaxLightRadius, val => { _maxLightRadius = val;}, true);
// Other...
LoadLightingShaders();
@@ -619,9 +617,8 @@ namespace Robust.Client.Graphics.Clyde
// Use worldbounds for this one as we only care if the light intersects our actual bounds
var xforms = _entityManager.GetEntityQuery<TransformComponent>();
var state = (this, count: 0, shadowCastingCount: 0, xforms, worldAABB);
var lightAabb = worldAABB.Enlarged(_maxLightRadius);
foreach (var (uid, comp) in _lightTreeSystem.GetIntersectingTrees(map, lightAabb))
foreach (var (uid, comp) in _lightTreeSystem.GetIntersectingTrees(map, worldAABB))
{
var bounds = _transformSystem.GetInvWorldMatrix(uid, xforms).TransformBox(worldBounds);
comp.Tree.QueryAabb(ref state, LightQuery, bounds);

View File

@@ -71,11 +71,21 @@ namespace Robust.Client.UserInterface.Controls
}
// First, we measure non-stretching children.
var stretching = new List<Control>();
float totalStretchRatio = 0;
foreach (var child in Children)
{
if (!child.Visible)
continue;
var stretch = Vertical ? child.VerticalExpand : child.HorizontalExpand;
if (stretch)
{
totalStretchRatio += child.SizeFlagsStretchRatio;
stretching.Add(child);
continue;
}
child.Measure(availableSize);
if (Vertical)
@@ -92,6 +102,35 @@ namespace Robust.Client.UserInterface.Controls
}
}
if (stretching.Count == 0)
return desiredSize;
// Then we measure stretching children
foreach (var child in stretching)
{
var size = availableSize;
if (Vertical)
{
size.Y *= child.SizeFlagsStretchRatio / totalStretchRatio;
child.Measure(size);
desiredSize.Y += child.DesiredSize.Y;
desiredSize.X = Math.Max(desiredSize.X, child.DesiredSize.X);
}
else
{
size.X *= child.SizeFlagsStretchRatio / totalStretchRatio;
child.Measure(size);
desiredSize.X += child.DesiredSize.X;
desiredSize.Y = Math.Max(desiredSize.Y, child.DesiredSize.Y);
}
// TODO Maybe make BoxContainer.MeasureOverride more rigorous.
// This should check if size < desired size. If it is, treat child as non-stretching (see the code in
// ArrangeOverride). This requires remeasuring all stretching controls + the control that just became
// non-stretching. But the re-measured controls might then become smaller (e.g. rich text wrapping),
// leading to a recursion problem.
}
return desiredSize;
}

View File

@@ -19,112 +19,117 @@ public class Generator : IIncrementalGenerator
public void Initialize(IncrementalGeneratorInitializationContext initContext)
{
IncrementalValuesProvider<(string name, string code)?> dataDefinitions = initContext.SyntaxProvider
.CreateSyntaxProvider(
static (node, _) => node is TypeDeclarationSyntax,
static (context, _) =>
{
var type = (TypeDeclarationSyntax)context.Node;
var symbol = (ITypeSymbol)context.SemanticModel.GetDeclaredSymbol(type)!;
if (!IsDataDefinition(symbol))
return null;
return GenerateForDataDefinition(type, symbol);
}
)
.Where(static type => type != null);
IncrementalValuesProvider<TypeDeclarationSyntax> dataDefinitions = initContext.SyntaxProvider.CreateSyntaxProvider(
static (node, _) => node is TypeDeclarationSyntax,
static (context, _) =>
{
var type = (TypeDeclarationSyntax) context.Node;
var symbol = (ITypeSymbol) context.SemanticModel.GetDeclaredSymbol(type)!;
return IsDataDefinition(symbol) ? type : null;
}
).Where(static type => type != null)!;
var comparer = new DataDefinitionComparer();
initContext.RegisterSourceOutput(
dataDefinitions,
initContext.CompilationProvider.Combine(dataDefinitions.WithComparer(comparer).Collect()),
static (sourceContext, source) =>
{
// TODO: deduplicate based on name?
var (name, code) = source!.Value;
var (compilation, declarations) = source;
var builder = new StringBuilder();
var containingTypes = new Stack<INamedTypeSymbol>();
var declarationsGenerated = new HashSet<string>();
var deltaType = compilation.GetTypeByMetadataName(ComponentDeltaInterfaceName)!;
sourceContext.AddSource(name, code);
foreach (var declaration in declarations)
{
builder.Clear();
containingTypes.Clear();
var type = compilation.GetSemanticModel(declaration.SyntaxTree).GetDeclaredSymbol(declaration)!;
var symbolName = type
.ToDisplayString()
.Replace('<', '{')
.Replace('>', '}');
if (!declarationsGenerated.Add(symbolName))
continue;
var nonPartial = !IsPartial(declaration);
var namespaceString = type.ContainingNamespace.IsGlobalNamespace
? string.Empty
: $"namespace {type.ContainingNamespace.ToDisplayString()};";
var containingType = type.ContainingType;
while (containingType != null)
{
containingTypes.Push(containingType);
containingType = containingType.ContainingType;
}
var containingTypesStart = new StringBuilder();
var containingTypesEnd = new StringBuilder();
foreach (var parent in containingTypes)
{
var syntax = (ClassDeclarationSyntax) parent.DeclaringSyntaxReferences[0].GetSyntax();
if (!IsPartial(syntax))
{
nonPartial = true;
continue;
}
containingTypesStart.AppendLine($"{GetPartialTypeDefinitionLine(parent)}\n{{");
containingTypesEnd.AppendLine("}");
}
var definition = GetDataDefinition(type);
if (nonPartial || definition.InvalidFields)
continue;
builder.AppendLine($$"""
#nullable enable
using System;
using Robust.Shared.Analyzers;
using Robust.Shared.IoC;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Manager.Exceptions;
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
#pragma warning disable CS0618 // Type or member is obsolete
#pragma warning disable CS0612 // Type or member is obsolete
#pragma warning disable CS0108 // Member hides inherited member; missing new keyword
#pragma warning disable RA0002 // Robust access analyzer
{{namespaceString}}
{{containingTypesStart}}
{{GetPartialTypeDefinitionLine(type)}} : ISerializationGenerated<{{definition.GenericTypeName}}>
{
{{GetConstructor(definition)}}
{{GetCopyMethods(definition, deltaType)}}
{{GetInstantiators(definition, deltaType)}}
}
{{containingTypesEnd}}
""");
var sourceText = CSharpSyntaxTree
.ParseText(builder.ToString())
.GetRoot()
.NormalizeWhitespace()
.ToFullString();
sourceContext.AddSource($"{symbolName}.g.cs", sourceText);
}
}
);
}
private static (string, string)? GenerateForDataDefinition(
TypeDeclarationSyntax declaration,
ITypeSymbol type)
{
var builder = new StringBuilder();
var containingTypes = new Stack<INamedTypeSymbol>();
containingTypes.Clear();
var symbolName = type
.ToDisplayString()
.Replace('<', '{')
.Replace('>', '}');
var nonPartial = !IsPartial(declaration);
var namespaceString = type.ContainingNamespace.IsGlobalNamespace
? string.Empty
: $"namespace {type.ContainingNamespace.ToDisplayString()};";
var containingType = type.ContainingType;
while (containingType != null)
{
containingTypes.Push(containingType);
containingType = containingType.ContainingType;
}
var containingTypesStart = new StringBuilder();
var containingTypesEnd = new StringBuilder();
foreach (var parent in containingTypes)
{
var syntax = (ClassDeclarationSyntax)parent.DeclaringSyntaxReferences[0].GetSyntax();
if (!IsPartial(syntax))
{
nonPartial = true;
continue;
}
containingTypesStart.AppendLine($"{GetPartialTypeDefinitionLine(parent)}\n{{");
containingTypesEnd.AppendLine("}");
}
var definition = GetDataDefinition(type);
if (nonPartial || definition.InvalidFields)
return null;
builder.AppendLine($$"""
#nullable enable
using System;
using Robust.Shared.Analyzers;
using Robust.Shared.IoC;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Manager.Exceptions;
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
#pragma warning disable CS0618 // Type or member is obsolete
#pragma warning disable CS0612 // Type or member is obsolete
#pragma warning disable CS0108 // Member hides inherited member; missing new keyword
#pragma warning disable RA0002 // Robust access analyzer
{{namespaceString}}
{{containingTypesStart}}
{{GetPartialTypeDefinitionLine(type)}} : ISerializationGenerated<{{definition.GenericTypeName}}>
{
{{GetConstructor(definition)}}
{{GetCopyMethods(definition)}}
{{GetInstantiators(definition)}}
}
{{containingTypesEnd}}
""");
return ($"{symbolName}.g.cs", builder.ToString());
}
private static DataDefinition GetDataDefinition(ITypeSymbol definition)
{
var fields = new List<DataField>();
@@ -191,7 +196,7 @@ public class Generator : IIncrementalGenerator
return builder.ToString();
}
private static string GetCopyMethods(DataDefinition definition)
private static string GetCopyMethods(DataDefinition definition, ITypeSymbol deltaType)
{
var builder = new StringBuilder();
@@ -262,36 +267,36 @@ public class Generator : IIncrementalGenerator
{{baseCopy}}
""");
foreach (var interfaceName in InternalGetImplicitDataDefinitionInterfaces(definition.Type, true))
foreach (var @interface in InternalGetImplicitDataDefinitionInterfaces(definition.Type, true, deltaType))
{
var interfaceModifiers = baseType != null &&
baseType.AllInterfaces.Any(i => i.ToDisplayString() == interfaceName)
var interfaceModifiers = baseType != null && baseType.AllInterfaces.Contains(@interface, SymbolEqualityComparer.Default)
? "override "
: modifiers;
var interfaceName = @interface.ToDisplayString();
builder.AppendLine($$"""
/// <seealso cref="ISerializationManager.CopyTo"/>
[Obsolete("Use ISerializationManager.CopyTo instead")]
public {{interfaceModifiers}} void InternalCopy(ref {{interfaceName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
{
var def = ({{definition.GenericTypeName}}) target;
Copy(ref def, serialization, hookCtx, context);
target = def;
}
/// <seealso cref="ISerializationManager.CopyTo"/>
[Obsolete("Use ISerializationManager.CopyTo instead")]
public {{interfaceModifiers}} void InternalCopy(ref {{interfaceName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
{
var def = ({{definition.GenericTypeName}}) target;
Copy(ref def, serialization, hookCtx, context);
target = def;
}
/// <seealso cref="ISerializationManager.CopyTo"/>
[Obsolete("Use ISerializationManager.CopyTo instead")]
public {{interfaceModifiers}} void Copy(ref {{interfaceName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
{
InternalCopy(ref target, serialization, hookCtx, context);
}
""");
/// <seealso cref="ISerializationManager.CopyTo"/>
[Obsolete("Use ISerializationManager.CopyTo instead")]
public {{interfaceModifiers}} void Copy(ref {{interfaceName}} target, ISerializationManager serialization, SerializationHookContext hookCtx, ISerializationContext? context = null)
{
InternalCopy(ref target, serialization, hookCtx, context);
}
""");
}
return builder.ToString();
}
private static string GetInstantiators(DataDefinition definition)
private static string GetInstantiators(DataDefinition definition, ITypeSymbol deltaType)
{
var builder = new StringBuilder();
var modifiers = string.Empty;
@@ -325,28 +330,27 @@ public class Generator : IIncrementalGenerator
""");
}
foreach (var interfaceName in InternalGetImplicitDataDefinitionInterfaces(definition.Type, false))
foreach (var @interface in InternalGetImplicitDataDefinitionInterfaces(definition.Type, false, deltaType))
{
var interfaceName = @interface.ToDisplayString();
builder.AppendLine($$"""
{{interfaceName}} {{interfaceName}}.Instantiate()
{
return Instantiate();
}
{{interfaceName}} {{interfaceName}}.Instantiate()
{
return Instantiate();
}
{{interfaceName}} ISerializationGenerated<{{interfaceName}}>.Instantiate()
{
return Instantiate();
}
""");
{{interfaceName}} ISerializationGenerated<{{interfaceName}}>.Instantiate()
{
return Instantiate();
}
""");
}
return builder.ToString();
}
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
private static IEnumerable<string> InternalGetImplicitDataDefinitionInterfaces(
ITypeSymbol type,
bool all)
private static IEnumerable<ITypeSymbol> InternalGetImplicitDataDefinitionInterfaces(ITypeSymbol type, bool all, ITypeSymbol deltaType)
{
var symbols = GetImplicitDataDefinitionInterfaces(type, all);
@@ -364,10 +368,10 @@ public class Generator : IIncrementalGenerator
return symbols;
}
if (symbols.Any(x => x == ComponentDeltaInterfaceName))
if (symbols.Any(x => x.ToDisplayString() == deltaType.ToDisplayString()))
return symbols;
return symbols.Append(ComponentDeltaInterfaceName);
return symbols.Append(deltaType);
}
// TODO serveronly? do we care? who knows!!

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -94,13 +93,13 @@ internal static class Types
return false;
}
internal static IEnumerable<string> GetImplicitDataDefinitionInterfaces(ITypeSymbol type, bool all)
internal static IEnumerable<ITypeSymbol> GetImplicitDataDefinitionInterfaces(ITypeSymbol type, bool all)
{
var interfaces = all ? type.AllInterfaces : type.Interfaces;
foreach (var @interface in interfaces)
{
if (IsImplicitDataDefinitionInterface(@interface))
yield return @interface.ToDisplayString();
yield return @interface;
}
}

View File

@@ -1,22 +1,20 @@
using Robust.Shared.Serialization.Manager.Attributes;
namespace Robust.Shared.GameObjects;
/// <summary>
/// Controls PVS visibility of entities. THIS COMPONENT CONTROLS WHETHER ENTITIES ARE NETWORKED TO PLAYERS
/// AND SHOULD NOT BE USED AS THE SOLE WAY TO HIDE AN ENTITY FROM A PLAYER.
/// </summary>
[RegisterComponent]
[Access(typeof(SharedVisibilitySystem))]
public sealed partial class VisibilityComponent : Component
namespace Robust.Shared.GameObjects
{
/// <summary>
/// The visibility layer for the entity.
/// Players whose visibility masks don't match this won't get state updates for it.
/// Controls PVS visibility of entities. THIS COMPONENT CONTROLS WHETHER ENTITIES ARE NETWORKED TO PLAYERS
/// AND SHOULD NOT BE USED AS THE SOLE WAY TO HIDE AN ENTITY FROM A PLAYER.
/// </summary>
/// <remarks>
/// Not serialized as visibility is normally immediate (i.e. prior to MapInit) and content should be handling it as such.
/// </remarks>
[DataField(readOnly: true)]
public ushort Layer = 1;
[RegisterComponent]
[Access(typeof(SharedVisibilitySystem))]
public sealed partial class VisibilityComponent : Component
{
/// <summary>
/// The visibility layer for the entity.
/// Players whose visibility masks don't match this won't get state updates for it.
/// </summary>
[DataField("layer")]
public ushort Layer = 1;
}
}

View File

@@ -59,7 +59,7 @@ public abstract partial class EntityManager
Dirty(uid, comp, metadata);
}
public virtual void DirtyFields<T>(EntityUid uid, T comp, MetaDataComponent? meta, params string[] fields)
public virtual void DirtyFields<T>(EntityUid uid, T comp, MetaDataComponent? meta, params ReadOnlySpan<string> fields)
where T : IComponentDelta
{
var compReg = ComponentFactory.GetRegistration(CompIdx.Index<T>());

View File

@@ -172,22 +172,12 @@ public partial class EntitySystem
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected void DirtyFields<T>(EntityUid uid, T comp, MetaDataComponent? meta, params string[] fields)
protected void DirtyFields<T>(EntityUid uid, T comp, MetaDataComponent? meta, params ReadOnlySpan<string> fields)
where T : IComponentDelta
{
EntityManager.DirtyFields(uid, comp, meta, fields);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected void DirtyFields<T>(Entity<T?> ent, MetaDataComponent? meta, params string[] fields)
where T : IComponentDelta
{
if (!Resolve(ent, ref ent.Comp))
return;
EntityManager.DirtyFields(ent, ent.Comp, meta, fields);
}
/// <summary>
/// Marks a component as dirty. This also implicitly dirties the entity this component belongs to.
/// </summary>

View File

@@ -9,7 +9,6 @@ using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Physics.Collision.Shapes;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Physics.Shapes;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Utility;
@@ -407,7 +406,7 @@ public sealed partial class EntityLookupSystem
var broadphaseInv = _transform.GetInvWorldMatrix(lookupUid);
var localBounds = broadphaseInv.TransformBounds(worldBounds);
var polygon = new SlimPolygon(localBounds);
var polygon = _physics.GetPooled(localBounds);
var result = AnyEntitiesIntersecting(lookupUid,
polygon,
localBounds.CalcBoundingBox(),
@@ -415,6 +414,7 @@ public sealed partial class EntityLookupSystem
flags,
ignored);
_physics.ReturnPooled(polygon);
return result;
}
@@ -458,8 +458,9 @@ public sealed partial class EntityLookupSystem
{
if (mapId == MapId.Nullspace) return false;
var polygon = new SlimPolygon(worldAABB);
var polygon = _physics.GetPooled(worldAABB);
var result = AnyEntitiesIntersecting(mapId, polygon, Physics.Transform.Empty, flags);
_physics.ReturnPooled(polygon);
return result;
}
@@ -474,8 +475,9 @@ public sealed partial class EntityLookupSystem
{
if (mapId == MapId.Nullspace) return;
var polygon = new SlimPolygon(worldAABB);
var polygon = _physics.GetPooled(worldAABB);
AddEntitiesIntersecting(mapId, intersecting, polygon, Physics.Transform.Empty, flags);
_physics.ReturnPooled(polygon);
}
#endregion
@@ -485,16 +487,18 @@ public sealed partial class EntityLookupSystem
public bool AnyEntitiesIntersecting(MapId mapId, Box2Rotated worldBounds, LookupFlags flags = DefaultFlags)
{
// Don't need to check contained entities as they have the same bounds as the parent.
var polygon = new SlimPolygon(worldBounds);
var polygon = _physics.GetPooled(worldBounds);
var result = AnyEntitiesIntersecting(mapId, polygon, Physics.Transform.Empty, flags);
_physics.ReturnPooled(polygon);
return result;
}
public HashSet<EntityUid> GetEntitiesIntersecting(MapId mapId, Box2Rotated worldBounds, LookupFlags flags = DefaultFlags)
{
var intersecting = new HashSet<EntityUid>();
var polygon = new SlimPolygon(worldBounds);
var polygon = _physics.GetPooled(worldBounds);
AddEntitiesIntersecting(mapId, intersecting, polygon, Physics.Transform.Empty, flags);
_physics.ReturnPooled(polygon);
return intersecting;
}
@@ -757,10 +761,11 @@ public sealed partial class EntityLookupSystem
return;
var localAABB = _transform.GetInvWorldMatrix(gridId).TransformBox(worldAABB);
var polygon = new SlimPolygon(localAABB);
var polygon = _physics.GetPooled(localAABB);
AddEntitiesIntersecting(gridId, intersecting, polygon, localAABB, Physics.Transform.Empty, flags, lookup);
AddContained(intersecting, flags);
_physics.ReturnPooled(polygon);
}
public void GetEntitiesIntersecting(EntityUid gridId, Box2Rotated worldBounds, HashSet<EntityUid> intersecting, LookupFlags flags = DefaultFlags)
@@ -769,10 +774,11 @@ public sealed partial class EntityLookupSystem
return;
var localBounds = _transform.GetInvWorldMatrix(gridId).TransformBounds(worldBounds);
var polygon = new SlimPolygon(localBounds);
var polygon = _physics.GetPooled(localBounds);
AddEntitiesIntersecting(gridId, intersecting, polygon, localBounds.CalcBoundingBox(), Physics.Transform.Empty, flags, lookup);
AddContained(intersecting, flags);
_physics.ReturnPooled(polygon);
}
#endregion

View File

@@ -121,8 +121,9 @@ public sealed partial class EntityLookupSystem
if (!_broadQuery.Resolve(lookupUid, ref lookup))
return;
var polygon = new SlimPolygon(localAABB);
var polygon = _physics.GetPooled(localAABB);
AddEntitiesIntersecting(lookupUid, intersecting, polygon, localAABB, Physics.Transform.Empty, flags, query, lookup);
_physics.ReturnPooled(polygon);
}
private void AddEntitiesIntersecting<T, TShape>(
@@ -251,10 +252,11 @@ public sealed partial class EntityLookupSystem
if (!_broadQuery.Resolve(lookupUid, ref lookup))
return false;
var polygon = new SlimPolygon(localAABB);
var polygon = _physics.GetPooled(localAABB);
var (lookupPos, lookupRot) = _transform.GetWorldPositionRotation(lookupUid);
var transform = new Transform(lookupPos, lookupRot);
var result = AnyComponentsIntersecting(lookupUid, polygon, localAABB, transform, flags, query, ignored, lookup);
_physics.ReturnPooled(polygon);
return result;
}
@@ -425,9 +427,10 @@ public sealed partial class EntityLookupSystem
public bool AnyComponentsIntersecting(Type type, MapId mapId, Box2 worldAABB, EntityUid? ignored = null, LookupFlags flags = DefaultFlags)
{
var polygon = new SlimPolygon(worldAABB);
var polygon = _physics.GetPooled(worldAABB);
var transform = Physics.Transform.Empty;
var result = AnyComponentsIntersecting(type, mapId, polygon, transform, ignored, flags);
_physics.ReturnPooled(polygon);
return result;
}
@@ -493,30 +496,33 @@ public sealed partial class EntityLookupSystem
if (mapId == MapId.Nullspace)
return;
var polygon = new SlimPolygon(worldAABB);
var polygon = _physics.GetPooled(worldAABB);
var transform = Physics.Transform.Empty;
GetEntitiesIntersecting(type, mapId, polygon, transform, intersecting, flags);
_physics.ReturnPooled(polygon);
}
public void GetEntitiesIntersecting<T>(MapId mapId, Box2Rotated worldBounds, HashSet<Entity<T>> entities, LookupFlags flags = DefaultFlags) where T : IComponent
{
if (mapId == MapId.Nullspace) return;
var polygon = new SlimPolygon(worldBounds);
var polygon = _physics.GetPooled(worldBounds);
var shapeTransform = Physics.Transform.Empty;
GetEntitiesIntersecting(mapId, polygon, shapeTransform, entities, flags);
_physics.ReturnPooled(polygon);
}
public void GetEntitiesIntersecting<T>(MapId mapId, Box2 worldAABB, HashSet<Entity<T>> entities, LookupFlags flags = DefaultFlags) where T : IComponent
{
if (mapId == MapId.Nullspace) return;
var polygon = new SlimPolygon(worldAABB);
var polygon = _physics.GetPooled(worldAABB);
var shapeTransform = Physics.Transform.Empty;
GetEntitiesIntersecting(mapId, polygon, shapeTransform, entities, flags);
_physics.ReturnPooled(polygon);
}
#endregion

View File

@@ -26,7 +26,7 @@ public sealed partial class EntityLookupSystem
if (!_broadQuery.Resolve(lookupUid, ref lookup))
return;
var lookupPoly = new SlimPolygon(localAABB);
var lookupPoly = new Polygon(localAABB);
AddEntitiesIntersecting(lookupUid, intersecting, lookupPoly, localAABB, Physics.Transform.Empty, flags, lookup);
}
@@ -40,7 +40,7 @@ public sealed partial class EntityLookupSystem
if (!_broadQuery.Resolve(lookupUid, ref lookup))
return;
var shape = new SlimPolygon(localBounds);
var shape = new Polygon(localBounds);
var localAABB = localBounds.CalcBoundingBox();
AddEntitiesIntersecting(lookupUid, intersecting, shape, localAABB, Physics.Transform.Empty, flags);
@@ -55,7 +55,7 @@ public sealed partial class EntityLookupSystem
if (!_broadQuery.Resolve(lookupUid, ref lookup))
return false;
var shape = new SlimPolygon(localAABB);
var shape = new Polygon(localAABB);
return AnyEntitiesIntersecting(lookupUid, shape, localAABB, Physics.Transform.Empty, flags, ignored, lookup);
}

View File

@@ -159,10 +159,6 @@ public abstract class SharedEyeSystem : EntitySystem
eye.Comp.PvsScale = Math.Clamp(scale, 0.1f, 100f);
}
/// <summary>
/// Overwrites visibility mask of an entity's eye.
/// If you wish for other systems to potentially change it consider raising <see cref="RefreshVisibilityMask"/>.
/// </summary>
public void SetVisibilityMask(EntityUid uid, int value, EyeComponent? eyeComponent = null)
{
if (!Resolve(uid, ref eyeComponent))
@@ -174,32 +170,4 @@ public abstract class SharedEyeSystem : EntitySystem
eyeComponent.VisibilityMask = value;
DirtyField(uid, eyeComponent, nameof(EyeComponent.VisibilityMask));
}
/// <summary>
/// Updates the visibility mask for an entity by raising a <see cref="GetVisMaskEvent"/>
/// </summary>
public void RefreshVisibilityMask(Entity<EyeComponent?> entity)
{
if (!Resolve(entity.Owner, ref entity.Comp, false))
return;
var ev = new GetVisMaskEvent()
{
Entity = entity.Owner,
};
RaiseLocalEvent(entity.Owner, ref ev, true);
SetVisibilityMask(entity.Owner, ev.VisibilityMask, entity.Comp);
}
}
/// <summary>
/// Event raised to update the vismask of an entity's eye.
/// </summary>
[ByRefEvent]
public record struct GetVisMaskEvent()
{
public EntityUid Entity;
public int VisibilityMask = EyeComponent.DefaultVisibilityMask;
}

View File

@@ -8,7 +8,6 @@ using Robust.Shared.Map.Enumerators;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision.Shapes;
using Robust.Shared.Physics.Shapes;
namespace Robust.Shared.Map;
@@ -225,42 +224,48 @@ internal partial class MapManager
public void FindGridsIntersecting(EntityUid mapEnt, Box2 worldAABB, GridCallback callback, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap)
{
var polygon = new SlimPolygon(worldAABB);
var polygon = _physics.GetPooled(worldAABB);
FindGridsIntersecting(mapEnt, polygon, worldAABB, Transform.Empty, callback, approx, includeMap);
_physics.ReturnPooled(polygon);
}
public void FindGridsIntersecting<TState>(EntityUid mapEnt, Box2 worldAABB, ref TState state, GridCallback<TState> callback, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap)
{
var polygon = new SlimPolygon(worldAABB);
var polygon = _physics.GetPooled(worldAABB);
FindGridsIntersecting(mapEnt, polygon, worldAABB, Transform.Empty, ref state, callback, approx, includeMap);
_physics.ReturnPooled(polygon);
}
public void FindGridsIntersecting(EntityUid mapEnt, Box2 worldAABB, ref List<Entity<MapGridComponent>> grids,
bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap)
{
var polygon = new SlimPolygon(worldAABB);
var polygon = _physics.GetPooled(worldAABB);
FindGridsIntersecting(mapEnt, polygon, worldAABB, Transform.Empty, ref grids, approx, includeMap);
_physics.ReturnPooled(polygon);
}
public void FindGridsIntersecting(EntityUid mapEnt, Box2Rotated worldBounds, GridCallback callback, bool approx = IMapManager.Approximate,
bool includeMap = IMapManager.IncludeMap)
{
var polygon = new SlimPolygon(worldBounds);
var polygon = _physics.GetPooled(worldBounds);
FindGridsIntersecting(mapEnt, polygon, worldBounds.CalcBoundingBox(), Transform.Empty, callback, approx, includeMap);
_physics.ReturnPooled(polygon);
}
public void FindGridsIntersecting<TState>(EntityUid mapEnt, Box2Rotated worldBounds, ref TState state, GridCallback<TState> callback,
bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap)
{
var polygon = new SlimPolygon(worldBounds);
var polygon = _physics.GetPooled(worldBounds);
FindGridsIntersecting(mapEnt, polygon, worldBounds.CalcBoundingBox(), Transform.Empty, ref state, callback, approx, includeMap);
_physics.ReturnPooled(polygon);
}
public void FindGridsIntersecting(EntityUid mapEnt, Box2Rotated worldBounds, ref List<Entity<MapGridComponent>> grids,
bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap)
{
var polygon = new SlimPolygon(worldBounds);
var polygon = _physics.GetPooled(worldBounds);
FindGridsIntersecting(mapEnt, polygon, worldBounds.CalcBoundingBox(), Transform.Empty, ref grids, approx, includeMap);
_physics.ReturnPooled(polygon);
}
#endregion

View File

@@ -12,9 +12,7 @@ internal sealed partial class CollisionManager
/// <param name="xfA">The transform for the first shape.</param>
/// <param name="xfB">The transform for the seconds shape.</param>
/// <returns></returns>
public bool TestOverlap<T, U>(T shapeA, int indexA, U shapeB, int indexB, in Transform xfA, in Transform xfB)
where T : IPhysShape
where U : IPhysShape
public bool TestOverlap<T, U>(T shapeA, int indexA, U shapeB, int indexB, in Transform xfA, in Transform xfB) where T : IPhysShape where U : IPhysShape
{
var input = new DistanceInput();

View File

@@ -21,9 +21,12 @@
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics.Collision.Shapes;
using Robust.Shared.Physics.Shapes;
@@ -43,7 +46,7 @@ internal ref struct DistanceProxy
// GJK using Voronoi regions (Christer Ericson) and Barycentric coordinates.
internal DistanceProxy(ReadOnlySpan<Vector2> vertices, float radius)
internal DistanceProxy(Vector2[] vertices, float radius)
{
Vertices = vertices;
Radius = radius;
@@ -68,18 +71,9 @@ internal ref struct DistanceProxy
case ShapeType.Polygon:
if (shape is Polygon poly)
{
Span<Vector2> verts = new Vector2[poly.VertexCount];
poly._vertices.AsSpan[..poly.VertexCount].CopyTo(verts);
Vertices = verts;
Vertices = poly.Vertices.AsSpan()[..poly.VertexCount];
Radius = poly.Radius;
}
else if (shape is SlimPolygon fast)
{
Span<Vector2> verts = new Vector2[fast.VertexCount];
fast._vertices.AsSpan[..fast.VertexCount].CopyTo(verts);
Vertices = verts;
Radius = fast.Radius;
}
else
{
var polyShape = Unsafe.As<PolygonShape>(shape);
@@ -157,7 +151,7 @@ internal ref struct DistanceProxy
return Vertices[bestIndex];
}
internal static DistanceProxy MakeProxy(ReadOnlySpan<Vector2> vertices, int count, float radius )
internal static DistanceProxy MakeProxy(Vector2[] vertices, int count, float radius )
{
count = Math.Min(count, PhysicsConstants.MaxPolygonVertices);
var proxy = new DistanceProxy(vertices[..count], radius);

View File

@@ -173,25 +173,10 @@ namespace Robust.Shared.Physics.Collision.Shapes
{
}
internal PolygonShape(SlimPolygon poly)
{
Vertices = new Vector2[poly.VertexCount];
Normals = new Vector2[poly.VertexCount];
poly._vertices.AsSpan[..VertexCount].CopyTo(Vertices);
poly._normals.AsSpan[..VertexCount].CopyTo(Normals);
Centroid = poly.Centroid;
}
internal PolygonShape(Polygon poly)
{
Vertices = new Vector2[poly.VertexCount];
Normals = new Vector2[poly.VertexCount];
poly._vertices.AsSpan[..VertexCount].CopyTo(Vertices);
poly._normals.AsSpan[..VertexCount].CopyTo(Normals);
Vertices = poly.Vertices;
Normals = poly.Normals;
Centroid = poly.Centroid;
}

View File

@@ -150,15 +150,6 @@ namespace Robust.Shared.Physics.Dynamics.Contacts
Friction = MathF.Sqrt((FixtureA?.Friction ?? 0.0f) * (FixtureB?.Friction ?? 0.0f));
}
public void GetWorldManifold(Transform transformA, Transform transformB, out Vector2 normal)
{
var shapeA = FixtureA?.Shape!;
var shapeB = FixtureB?.Shape!;
Span<Vector2> points = stackalloc Vector2[PhysicsConstants.MaxPolygonVertices];
SharedPhysicsSystem.InitializeManifold(ref Manifold, transformA, transformB, shapeA.Radius, shapeB.Radius, out normal, points);
}
/// <summary>
/// Gets the world manifold.
/// </summary>

View File

@@ -11,28 +11,19 @@ namespace Robust.Shared.Physics.Shapes;
// Internal so people don't use it when it will have breaking changes very soon.
internal record struct Polygon : IPhysShape
{
[DataField]
public byte VertexCount { get; internal set; }
/// <summary>
/// Vertices associated with this polygon. Will be sliced to <see cref="VertexCount"/>
/// </summary>
/// <remarks>
/// Consider using _vertices if doing engine work.
/// </remarks>
public Vector2[] Vertices => _vertices.AsSpan[..VertexCount].ToArray();
public Vector2[] Normals => _normals.AsSpan[..VertexCount].ToArray();
public static Polygon Empty = new(Box2.Empty);
[DataField]
internal FixedArray8<Vector2> _vertices;
public Vector2[] Vertices;
internal FixedArray8<Vector2> _normals;
public byte VertexCount;
public Vector2[] Normals;
public Vector2 Centroid;
public int ChildCount => 1;
public float Radius { get; set; } = PhysicsConstants.PolygonRadius;
public float Radius { get; set; }
public ShapeType ShapeType => ShapeType.Polygon;
// Hopefully this one is short-lived for a few months
@@ -44,58 +35,65 @@ internal record struct Polygon : IPhysShape
public Polygon(PolygonShape polyShape)
{
Unsafe.SkipInit(out this);
Vertices = new Vector2[polyShape.VertexCount];
Normals = new Vector2[polyShape.Normals.Length];
Radius = polyShape.Radius;
Centroid = polyShape.Centroid;
VertexCount = (byte) polyShape.VertexCount;
polyShape.Vertices.AsSpan()[..VertexCount].CopyTo(_vertices.AsSpan);
polyShape.Normals.AsSpan()[..VertexCount].CopyTo(_normals.AsSpan);
}
public Polygon(Box2 box)
{
Unsafe.SkipInit(out this);
Radius = 0f;
VertexCount = 4;
_vertices._00 = box.BottomLeft;
_vertices._01 = box.BottomRight;
_vertices._02 = box.TopRight;
_vertices._03 = box.TopLeft;
_normals._00 = new Vector2(0.0f, -1.0f);
_normals._01 = new Vector2(1.0f, 0.0f);
_normals._02 = new Vector2(0.0f, 1.0f);
_normals._03 = new Vector2(-1.0f, 0.0f);
}
public Polygon(Box2Rotated bounds)
{
Unsafe.SkipInit(out this);
Radius = 0f;
VertexCount = 4;
_vertices._00 = bounds.BottomLeft;
_vertices._01 = bounds.BottomRight;
_vertices._02 = bounds.TopRight;
_vertices._03 = bounds.TopLeft;
CalculateNormals(_vertices.AsSpan, _normals.AsSpan, 4);
Centroid = bounds.Center;
Array.Copy(polyShape.Vertices, Vertices, Vertices.Length);
Array.Copy(polyShape.Normals, Normals, Vertices.Length);
VertexCount = (byte) Vertices.Length;
}
/// <summary>
/// Manually constructed polygon for internal use to take advantage of pooling.
/// </summary>
internal Polygon(ReadOnlySpan<Vector2> vertices, ReadOnlySpan<Vector2> normals, Vector2 centroid, byte count)
internal Polygon(Vector2[] vertices, Vector2[] normals, Vector2 centroid, byte count)
{
Unsafe.SkipInit(out this);
vertices[..VertexCount].CopyTo(_vertices.AsSpan);
normals[..VertexCount].CopyTo(_normals.AsSpan);
Vertices = vertices;
Normals = normals;
Centroid = centroid;
VertexCount = count;
}
public Polygon(Box2 aabb)
{
Unsafe.SkipInit(out this);
Vertices = new Vector2[4];
Normals = new Vector2[4];
Radius = 0f;
Vertices[0] = aabb.BottomLeft;
Vertices[1] = aabb.BottomRight;
Vertices[2] = aabb.TopRight;
Vertices[3] = aabb.TopLeft;
Normals[0] = new Vector2(0.0f, -1.0f);
Normals[1] = new Vector2(1.0f, 0.0f);
Normals[2] = new Vector2(0.0f, 1.0f);
Normals[3] = new Vector2(-1.0f, 0.0f);
VertexCount = 4;
Centroid = aabb.Center;
}
public Polygon(Box2Rotated bounds)
{
Unsafe.SkipInit(out this);
Vertices = new Vector2[4];
Normals = new Vector2[4];
Radius = 0f;
Vertices[0] = bounds.BottomLeft;
Vertices[1] = bounds.BottomRight;
Vertices[2] = bounds.TopRight;
Vertices[3] = bounds.TopLeft;
CalculateNormals(Normals, 4);
VertexCount = 4;
Centroid = bounds.Center;
}
public Polygon(Vector2[] vertices)
@@ -106,15 +104,16 @@ internal record struct Polygon : IPhysShape
if (hull.Count < 3)
{
VertexCount = 0;
Vertices = [];
Normals = [];
return;
}
VertexCount = (byte) vertices.Length;
var vertSpan = _vertices.AsSpan;
vertices.AsSpan().CopyTo(vertSpan);
Vertices = vertices;
Normals = new Vector2[vertices.Length];
Set(hull);
Centroid = ComputeCentroid(vertSpan);
Centroid = ComputeCentroid(Vertices);
}
public static explicit operator Polygon(PolygonShape polyShape)
@@ -126,32 +125,32 @@ internal record struct Polygon : IPhysShape
{
DebugTools.Assert(hull.Count >= 3);
var vertexCount = hull.Count;
var verts = _vertices.AsSpan;
var norms = _normals.AsSpan;
Array.Resize(ref Vertices, vertexCount);
Array.Resize(ref Normals, vertexCount);
for (var i = 0; i < vertexCount; i++)
{
verts[i] = hull.Points[i];
Vertices[i] = hull.Points[i];
}
// Compute normals. Ensure the edges have non-zero length.
CalculateNormals(verts, norms, vertexCount);
CalculateNormals(Normals, vertexCount);
}
public static void CalculateNormals(ReadOnlySpan<Vector2> vertices, Span<Vector2> normals, int count)
internal void CalculateNormals(Span<Vector2> normals, int count)
{
for (var i = 0; i < count; i++)
{
var next = i + 1 < count ? i + 1 : 0;
var edge = vertices[next] - vertices[i];
var edge = Vertices[next] - Vertices[i];
DebugTools.Assert(edge.LengthSquared() > float.Epsilon * float.Epsilon);
var temp = Vector2Helpers.Cross(edge, 1f);
normals[i] = temp.Normalized();
Normals[i] = temp.Normalized();
}
}
public static Vector2 ComputeCentroid(ReadOnlySpan<Vector2> vs)
private static Vector2 ComputeCentroid(Vector2[] vs)
{
var count = vs.Length;
DebugTools.Assert(count >= 3);
@@ -192,15 +191,13 @@ internal record struct Polygon : IPhysShape
public Box2 ComputeAABB(Transform transform, int childIndex)
{
DebugTools.Assert(VertexCount > 0);
DebugTools.Assert(childIndex == 0);
var verts = _vertices.AsSpan;
var lower = Transform.Mul(transform, verts[0]);
var lower = Transform.Mul(transform, Vertices[0]);
var upper = lower;
for (var i = 1; i < VertexCount; ++i)
{
var v = Transform.Mul(transform, verts[i]);
var v = Transform.Mul(transform, Vertices[i]);
lower = Vector2.Min(lower, v);
upper = Vector2.Max(upper, v);
}
@@ -211,41 +208,12 @@ internal record struct Polygon : IPhysShape
public bool Equals(IPhysShape? other)
{
if (other is SlimPolygon slim)
{
return Equals(slim);
}
return other is Polygon poly && Equals(poly);
}
public bool Equals(Polygon other)
{
if (VertexCount != other.VertexCount) return false;
var ourVerts = _vertices.AsSpan;
var otherVerts = other._vertices.AsSpan;
if (other is not PolygonShape poly) return false;
if (VertexCount != poly.VertexCount) return false;
for (var i = 0; i < VertexCount; i++)
{
var vert = ourVerts[i];
if (!vert.Equals(otherVerts[i])) return false;
}
return true;
}
public bool Equals(SlimPolygon other)
{
if (VertexCount != other.VertexCount) return false;
var ourVerts = _vertices.AsSpan;
var otherVerts = other._vertices.AsSpan;
for (var i = 0; i < VertexCount; i++)
{
var vert = ourVerts[i];
if (!vert.Equals(otherVerts[i])) return false;
var vert = Vertices[i];
if (!vert.Equals(poly.Vertices[i])) return false;
}
return true;
@@ -253,6 +221,6 @@ internal record struct Polygon : IPhysShape
public override int GetHashCode()
{
return HashCode.Combine(VertexCount, _vertices.AsSpan.ToArray(), Radius);
return HashCode.Combine(VertexCount, Vertices.AsSpan(0, VertexCount).ToArray(), Radius);
}
}

View File

@@ -1,103 +0,0 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using Robust.Shared.Maths;
using Robust.Shared.Physics.Collision.Shapes;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility;
namespace Robust.Shared.Physics.Shapes;
/// <summary>
/// Polygon backed by FixedArray4 to be smaller.
/// Useful for internal ops where the inputs are boxes to avoid the additional padding.
/// </summary>
internal record struct SlimPolygon : IPhysShape
{
public Vector2[] Vertices => _vertices.AsSpan[..VertexCount].ToArray();
public Vector2[] Normals => _normals.AsSpan[..VertexCount].ToArray();
[DataField]
public FixedArray4<Vector2> _vertices;
public FixedArray4<Vector2> _normals;
public Vector2 Centroid;
public byte VertexCount => 4;
public int ChildCount => 1;
public float Radius { get; set; } = PhysicsConstants.PolygonRadius;
public ShapeType ShapeType => ShapeType.Polygon;
public SlimPolygon(Box2 box)
{
Unsafe.SkipInit(out this);
Radius = 0f;
_vertices._00 = box.BottomLeft;
_vertices._01 = box.BottomRight;
_vertices._02 = box.TopRight;
_vertices._03 = box.TopLeft;
_normals._00 = new Vector2(0.0f, -1.0f);
_normals._01 = new Vector2(1.0f, 0.0f);
_normals._02 = new Vector2(0.0f, 1.0f);
_normals._03 = new Vector2(-1.0f, 0.0f);
}
public SlimPolygon(Box2Rotated bounds)
{
Unsafe.SkipInit(out this);
Radius = 0f;
_vertices._00 = bounds.BottomLeft;
_vertices._01 = bounds.BottomRight;
_vertices._02 = bounds.TopRight;
_vertices._03 = bounds.TopLeft;
Polygon.CalculateNormals(_vertices.AsSpan, _normals.AsSpan, 4);
Centroid = bounds.Center;
}
public Box2 ComputeAABB(Transform transform, int childIndex)
{
DebugTools.Assert(VertexCount > 0);
DebugTools.Assert(childIndex == 0);
var verts = _vertices.AsSpan;
var lower = Transform.Mul(transform, verts[0]);
var upper = lower;
for (var i = 1; i < VertexCount; ++i)
{
var v = Transform.Mul(transform, verts[i]);
lower = Vector2.Min(lower, v);
upper = Vector2.Max(upper, v);
}
var r = new Vector2(Radius, Radius);
return new Box2(lower - r, upper + r);
}
public bool Equals(SlimPolygon other)
{
return Radius.Equals(other.Radius) && _vertices.AsSpan[..VertexCount].SequenceEqual(other._vertices.AsSpan[..VertexCount]);
}
public readonly override int GetHashCode()
{
return HashCode.Combine(_vertices, _normals, Centroid, Radius);
}
public bool Equals(IPhysShape? other)
{
if (other is Polygon poly)
{
return poly.Equals(this);
}
return other is SlimPolygon slim && Equals(slim);
}
}

View File

@@ -40,29 +40,13 @@ namespace Robust.Shared.Physics.Systems
return true;
}
case SlimPolygon slim:
{
var pLocal = Physics.Transform.MulT(xform.Quaternion2D, worldPoint - xform.Position);
var norms = slim._normals.AsSpan;
var verts = slim._vertices.AsSpan;
for (var i = 0; i < slim.VertexCount; i++)
{
var dot = Vector2.Dot(norms[i], pLocal - verts[i]);
if (dot > 0f) return false;
}
return true;
}
case Polygon poly:
{
var pLocal = Physics.Transform.MulT(xform.Quaternion2D, worldPoint - xform.Position);
var norms = poly._normals.AsSpan;
var verts = poly._vertices.AsSpan;
for (var i = 0; i < poly.VertexCount; i++)
{
var dot = Vector2.Dot(norms[i], pLocal - verts[i]);
var dot = Vector2.Dot(poly.Normals[i], pLocal - poly.Vertices[i]);
if (dot > 0f) return false;
}
@@ -102,10 +86,6 @@ namespace Robust.Shared.Physics.Systems
var polygon = (PolygonShape) aabb;
GetMassData(polygon, ref data, density);
break;
case Polygon fastPoly:
return GetMassData(new PolygonShape(fastPoly), density);
case SlimPolygon slim:
return GetMassData(new PolygonShape(slim), density);
case PolygonShape poly:
// Polygon mass, centroid, and inertia.
// Let rho be the polygon density in mass per unit area.
@@ -215,12 +195,6 @@ namespace Robust.Shared.Physics.Systems
var polygon = (PolygonShape) aabb;
GetMassData(polygon, ref data, density);
break;
case Polygon fastPoly:
GetMassData(new PolygonShape(fastPoly), ref data, density);
break;
case SlimPolygon slim:
GetMassData(new PolygonShape(slim), ref data, density);
break;
case PolygonShape poly:
// Polygon mass, centroid, and inertia.
// Let rho be the polygon density in mass per unit area.

View File

@@ -191,12 +191,6 @@ public sealed partial class RayCastSystem
private CastOutput RayCastPolygon(RayCastInput input, Polygon shape)
{
var verts = shape._vertices.AsSpan;
var output = new CastOutput()
{
Fraction = 0f,
};
if (shape.Radius == 0.0f)
{
// Put the ray into the polygon's frame of reference.
@@ -207,15 +201,18 @@ public sealed partial class RayCastSystem
var index = -1;
var norms = shape._normals.AsSpan;
var output = new CastOutput()
{
Fraction = 0f,
};
for ( var i = 0; i < shape.VertexCount; ++i )
{
// p = p1 + a * d
// dot(normal, p - v) = 0
// dot(normal, p1 - v) + a * dot(normal, d) = 0
float numerator = Vector2.Dot(norms[i], Vector2.Subtract( verts[i], p1 ) );
float denominator = Vector2.Dot(norms[i], d );
float numerator = Vector2.Dot(shape.Normals[i], Vector2.Subtract( shape.Vertices[i], p1 ) );
float denominator = Vector2.Dot(shape.Normals[i], d );
if ( denominator == 0.0f )
{
@@ -260,7 +257,7 @@ public sealed partial class RayCastSystem
if (index >= 0)
{
output.Fraction = lower;
output.Normal = norms[index];
output.Normal = shape.Normals[index];
output.Point = Vector2.Add(p1, lower * d);
output.Hit = true;
}
@@ -268,24 +265,17 @@ public sealed partial class RayCastSystem
return output;
}
Span<Vector2> proxyBVerts = new Vector2[]
{
input.Origin,
};
// TODO_ERIN this is not working for ray vs box (zero radii)
// TODO_ERIN this is not working for ray vs box (zero radii)
var castInput = new ShapeCastPairInput
{
ProxyA = DistanceProxy.MakeProxy(verts, shape.VertexCount, shape.Radius),
ProxyB = DistanceProxy.MakeProxy(proxyBVerts, 1, 0.0f),
ProxyA = DistanceProxy.MakeProxy(shape.Vertices, shape.VertexCount, shape.Radius),
ProxyB = DistanceProxy.MakeProxy([input.Origin], 1, 0.0f),
TransformA = Physics.Transform.Empty,
TransformB = Physics.Transform.Empty,
TranslationB = input.Translation,
MaxFraction = input.MaxFraction
};
ShapeCast(ref output, castInput);
return output;
return ShapeCast(castInput);
}
// Ray vs line segment
@@ -446,9 +436,12 @@ public sealed partial class RayCastSystem
// "Smooth Mesh Contacts with GJK" in Game Physics Pearls. 2010
// todo this is failing when used to raycast a box
// todo this converges slowly with a radius
private void ShapeCast(ref CastOutput output, in ShapeCastPairInput input)
private CastOutput ShapeCast(ShapeCastPairInput input)
{
output.Fraction = input.MaxFraction;
var output = new CastOutput()
{
Fraction = input.MaxFraction,
};
var proxyA = input.ProxyA;
var count = input.ProxyB.Vertices.Length;
@@ -520,15 +513,15 @@ public sealed partial class RayCastSystem
if ( vr <= 0.0f )
{
// miss
return;
}
return output;
}
lambda = ( vp - sigma ) / vr;
if ( lambda > maxFraction )
{
// too far
return;
}
return output;
}
// reset the simplex
simplex.Count = 0;
@@ -569,7 +562,7 @@ public sealed partial class RayCastSystem
{
// Overlap
// Yes this means you need to manually query for overlaps.
return;
return output;
}
// Get search direction.
@@ -583,7 +576,7 @@ public sealed partial class RayCastSystem
if ( iter == 0 || lambda == 0.0f )
{
// Initial overlap
return;
return output;
}
// Prepare output.
@@ -598,6 +591,7 @@ public sealed partial class RayCastSystem
output.Fraction = lambda;
output.Iterations = iter;
output.Hit = true;
return output;
}
private int FindSupport(DistanceProxy proxy, Vector2 direction)
@@ -619,14 +613,9 @@ public sealed partial class RayCastSystem
private CastOutput ShapeCastCircle(ShapeCastInput input, PhysShapeCircle shape)
{
Span<Vector2> proxyAVerts = new[]
{
shape.Position,
};
var pairInput = new ShapeCastPairInput
{
ProxyA = DistanceProxy.MakeProxy(proxyAVerts, 1, shape.Radius ),
ProxyA = DistanceProxy.MakeProxy([shape.Position], 1, shape.Radius ),
ProxyB = DistanceProxy.MakeProxy(input.Points, input.Count, input.Radius ),
TransformA = Physics.Transform.Empty,
TransformB = Physics.Transform.Empty,
@@ -634,8 +623,7 @@ public sealed partial class RayCastSystem
MaxFraction = input.MaxFraction
};
var output = new CastOutput();
ShapeCast(ref output, pairInput);
var output = ShapeCast(pairInput);
return output;
}
@@ -643,7 +631,7 @@ public sealed partial class RayCastSystem
{
var pairInput = new ShapeCastPairInput
{
ProxyA = DistanceProxy.MakeProxy(shape._vertices.AsSpan, shape.VertexCount, shape.Radius),
ProxyA = DistanceProxy.MakeProxy(shape.Vertices, shape.VertexCount, shape.Radius),
ProxyB = DistanceProxy.MakeProxy(input.Points, input.Count, input.Radius),
TransformA = Physics.Transform.Empty,
TransformB = Physics.Transform.Empty,
@@ -651,30 +639,21 @@ public sealed partial class RayCastSystem
MaxFraction = input.MaxFraction
};
var output = new CastOutput();
ShapeCast(ref output, pairInput);
var output = ShapeCast(pairInput);
return output;
}
private CastOutput ShapeCastSegment(ShapeCastInput input, EdgeShape shape)
{
Span<Vector2> proxyAVerts = new[]
{
shape.Vertex0,
};
var pairInput = new ShapeCastPairInput();
pairInput.ProxyA = DistanceProxy.MakeProxy([shape.Vertex0], 2, 0.0f);
pairInput.ProxyB = DistanceProxy.MakeProxy(input.Points, input.Count, input.Radius);
pairInput.TransformA = Physics.Transform.Empty;
pairInput.TransformB = Physics.Transform.Empty;
pairInput.TranslationB = input.Translation;
pairInput.MaxFraction = input.MaxFraction;
var pairInput = new ShapeCastPairInput
{
ProxyA = DistanceProxy.MakeProxy(proxyAVerts, 2, 0.0f),
ProxyB = DistanceProxy.MakeProxy(input.Points, input.Count, input.Radius),
TransformA = Physics.Transform.Empty,
TransformB = Physics.Transform.Empty,
TranslationB = input.Translation,
MaxFraction = input.MaxFraction
};
var output = new CastOutput();
ShapeCast(ref output, pairInput);
var output = ShapeCast(pairInput);
return output;
}

View File

@@ -275,9 +275,6 @@ public sealed partial class RayCastSystem : EntitySystem
case PhysShapeCircle circle:
CastCircle(entity, ref result, circle, originTransform, translation, filter, callback);
break;
case SlimPolygon slim:
CastPolygon(entity, ref result, new PolygonShape(slim), originTransform, translation, filter, callback);
break;
case Polygon poly:
CastPolygon(entity, ref result, new PolygonShape(poly), originTransform, translation, filter, callback);
break;

View File

@@ -0,0 +1,55 @@
using System.Buffers;
using System.Numerics;
using Microsoft.Extensions.ObjectPool;
using Robust.Shared.Maths;
using Robust.Shared.Physics.Shapes;
namespace Robust.Shared.Physics.Systems;
public abstract partial class SharedPhysicsSystem
{
/// <summary>
/// Gets a polygon with pooled arrays backing it.
/// </summary>
internal Polygon GetPooled(Box2 box)
{
var vertices = ArrayPool<Vector2>.Shared.Rent(4);
var normals = ArrayPool<Vector2>.Shared.Rent(4);
var centroid = box.Center;
vertices[0] = box.BottomLeft;
vertices[1] = box.BottomRight;
vertices[2] = box.TopRight;
vertices[3] = box.TopLeft;
normals[0] = new Vector2(0.0f, -1.0f);
normals[1] = new Vector2(1.0f, 0.0f);
normals[2] = new Vector2(0.0f, 1.0f);
normals[3] = new Vector2(-1.0f, 0.0f);
return new Polygon(vertices, normals, centroid, 4);
}
internal Polygon GetPooled(Box2Rotated box)
{
var vertices = ArrayPool<Vector2>.Shared.Rent(4);
var normals = ArrayPool<Vector2>.Shared.Rent(4);
var centroid = box.Center;
vertices[0] = box.BottomLeft;
vertices[1] = box.BottomRight;
vertices[2] = box.TopRight;
vertices[3] = box.TopLeft;
var polygon = new Polygon(vertices, normals, centroid, 4);
polygon.CalculateNormals(normals, 4);
return polygon;
}
internal void ReturnPooled(Polygon polygon)
{
ArrayPool<Vector2>.Shared.Return(polygon.Vertices);
ArrayPool<Vector2>.Shared.Return(polygon.Normals);
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using JetBrains.Annotations;
@@ -87,7 +86,7 @@ namespace Robust.Shared.Utility
public Span<T> AsSpan => MemoryMarshal.CreateSpan(ref _00, 2);
}
internal struct FixedArray4<T> : IEquatable<FixedArray4<T>>
internal struct FixedArray4<T>
{
public T _00;
public T _01;
@@ -95,27 +94,9 @@ namespace Robust.Shared.Utility
public T _03;
public Span<T> AsSpan => MemoryMarshal.CreateSpan(ref _00, 4);
public bool Equals(FixedArray4<T> other)
{
return _00?.Equals(other._00) == true &&
_01?.Equals(other._01) == true &&
_02?.Equals(other._02) == true &&
_03?.Equals(other._03) == true;
}
public override bool Equals(object? obj)
{
return obj is FixedArray4<T> other && Equals(other);
}
public override int GetHashCode()
{
return HashCode.Combine(_00, _01, _02, _03);
}
}
internal struct FixedArray8<T> : IEquatable<FixedArray8<T>>
internal struct FixedArray8<T>
{
public T _00;
public T _01;
@@ -127,28 +108,6 @@ namespace Robust.Shared.Utility
public T _07;
public Span<T> AsSpan => MemoryMarshal.CreateSpan(ref _00, 8);
public bool Equals(FixedArray8<T> other)
{
return _00?.Equals(other._00) == true &&
_01?.Equals(other._01) == true &&
_02?.Equals(other._02) == true &&
_03?.Equals(other._03) == true &&
_04?.Equals(other._04) == true &&
_05?.Equals(other._05) == true &&
_06?.Equals(other._06) == true &&
_07?.Equals(other._07) == true;
}
public override bool Equals(object? obj)
{
return obj is FixedArray8<T> other && Equals(other);
}
public override int GetHashCode()
{
return HashCode.Combine(_00, _01, _02, _03, _04, _05, _06, _07);
}
}
internal struct FixedArray16<T>

View File

@@ -123,71 +123,5 @@ namespace Robust.UnitTesting.Client.UserInterface.Controls
Assert.That(control2.Position, Is.EqualTo(new Vector2(0, 65)));
Assert.That(control2.Size, Is.EqualTo(new Vector2(30, 15)));
}
[Test]
public void TestTwoExpandRatio()
{
var boxContainer = new BoxContainer
{
Orientation = LayoutOrientation.Horizontal,
SetSize = new Vector2(100, 10),
Children =
{
new Control
{
MinWidth = 10,
HorizontalExpand = true,
SizeFlagsStretchRatio = 20,
},
new Control
{
MinWidth = 10,
HorizontalExpand = true,
SizeFlagsStretchRatio = 80
}
}
};
boxContainer.Arrange(UIBox2.FromDimensions(Vector2.Zero, boxContainer.SetSize));
Assert.Multiple(() =>
{
Assert.That(boxContainer.GetChild(0).Width, Is.EqualTo(20));
Assert.That(boxContainer.GetChild(1).Width, Is.EqualTo(80));
});
}
[Test]
public void TestTwoExpandOneSmall()
{
var boxContainer = new BoxContainer
{
Orientation = LayoutOrientation.Horizontal,
SetSize = new Vector2(100, 10),
Children =
{
new Control
{
MinWidth = 30,
HorizontalExpand = true,
SizeFlagsStretchRatio = 20,
},
new Control
{
MinWidth = 30,
HorizontalExpand = true,
SizeFlagsStretchRatio = 80
}
}
};
boxContainer.Arrange(UIBox2.FromDimensions(Vector2.Zero, boxContainer.SetSize));
Assert.Multiple(() =>
{
Assert.That(boxContainer.GetChild(0).Width, Is.EqualTo(30));
Assert.That(boxContainer.GetChild(1).Width, Is.EqualTo(70));
});
}
}
}

View File

@@ -46,13 +46,13 @@ public sealed class RayCast_Test
// Polygon
// - Initial overlap, no shapecast
new(new SlimPolygon(Box2.UnitCentered), new Transform(Vector2.UnitY / 2f, Angle.Zero), Vector2.UnitY, null),
new(new Polygon(Box2.UnitCentered), new Transform(Vector2.UnitY / 2f, Angle.Zero), Vector2.UnitY, null),
// - Cast
new(new SlimPolygon(Box2.UnitCentered), new Transform(Vector2.Zero, Angle.Zero), Vector2.UnitY, new Vector2(0.5f, 1f - PhysicsConstants.PolygonRadius)),
new(new Polygon(Box2.UnitCentered), new Transform(Vector2.Zero, Angle.Zero), Vector2.UnitY, new Vector2(0.5f, 1f - PhysicsConstants.PolygonRadius)),
// - Miss
new(new SlimPolygon(Box2.UnitCentered), new Transform(Vector2.Zero, Angle.Zero), -Vector2.UnitY, null),
new(new Polygon(Box2.UnitCentered), new Transform(Vector2.Zero, Angle.Zero), -Vector2.UnitY, null),
};
[Test, TestCaseSource(nameof(_rayCases))]

View File

@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using NUnit.Framework;
using Robust.Shared.Maths;
using Robust.Shared.Physics.Collision.Shapes;
namespace Robust.UnitTesting.Shared.Physics

View File

@@ -9,23 +9,10 @@ namespace Robust.UnitTesting.Shared.Physics;
[TestFixture]
public sealed class Polygon_Test
{
/// <summary>
/// Check that Slim and normal Polygon are equals
/// </summary>
[Test]
public void TestSlim()
{
var slim = new SlimPolygon(Box2.UnitCentered.Translated(Vector2.One));
var poly = new Polygon(Box2.UnitCentered.Translated(Vector2.One));
Assert.That(slim.Equals(poly));
}
[Test]
public void TestAABB()
{
var shape = new SlimPolygon(Box2.UnitCentered.Translated(Vector2.One));
var shape = new Polygon(Box2.UnitCentered.Translated(Vector2.One));
Assert.That(shape.ComputeAABB(Transform.Empty, 0), Is.EqualTo(Box2.UnitCentered.Translated(Vector2.One)));
}
@@ -33,8 +20,8 @@ public sealed class Polygon_Test
[Test]
public void TestBox2()
{
var shape = new SlimPolygon(Box2.UnitCentered.Translated(Vector2.One));
Assert.That(shape._vertices.AsSpan.ToArray(), Is.EqualTo(new Vector2[]
var shape = new Polygon(Box2.UnitCentered.Translated(Vector2.One));
Assert.That(shape.Vertices, Is.EqualTo(new Vector2[]
{
new Vector2(0.5f, 0.5f),
new Vector2(1.5f, 0.5f),
@@ -46,9 +33,9 @@ public sealed class Polygon_Test
[Test]
public void TestBox2Rotated()
{
var shape = new SlimPolygon(new Box2Rotated(Box2.UnitCentered, Angle.FromDegrees(90)));
var shape = new Polygon(new Box2Rotated(Box2.UnitCentered, Angle.FromDegrees(90)));
Assert.That(shape._vertices.AsSpan.ToArray(), Is.EqualTo(new Vector2[]
Assert.That(shape.Vertices, Is.EqualTo(new Vector2[]
{
new Vector2(0.5f, -0.5f),
new Vector2(0.5f, 0.5f),